KIELMAS.COM Kielmas Jarosław

Blog głównie o programowaniu

Browsing Posts in PHP, Narzędzia, Frameworki

CMS modx

1 comment

Musiałem zapoznać się i przetestować  CMS modx. Zaciagnąłem więc najnowszą wersję 1.0.3 rozpakowałem, skopiowałem do katalogu z projektami, dodałem VirtualHost z dyrektywą   AllowOverride All, ponieważ w katalogu głównym w .htaccess znajduje się RewriteEngine On i kilka warunków przekierowania (min. na index.php w katalogu głównym), wykluczające pliki. Od razu zastanowiłem się dlaczego ustawienie php_flag register_globals ustawione jest na Off. Po restarcie apache i wpisaniu ustalonego adresu zdefiniowanego w ServerName można ujrzeć proces  instalacyjny tego CMS. Wystarczy tylko stworzyć odpowiedniego użytkownika w bazie danych, poczym zautoryzować go przez formularz instalacyjny i można od razu przeprowadzić test połączenia z bazą – udało się. Następnie należy stworzyć pusty plik konfiguracyjny /modx/manager/includes/config.inc.php (do którego później zapisywana jest konfiguracja systemu – ustawienia dla połączenia z DB, ziarno dla sesji, zdefiniowanie scieżek). Należy także ustawić odpowiedniego właściciela oraz prawa zapisu dla niektórych katalogów w /modx/assets/ (np. cache, images). W między czasie można było zdefiniować lokale systemowe, kodowanie dla DB, dane  webmastera  oraz wybrać sobie domyślny template dla naszej webaplikacji. Następnie wystarczy jedynie zaakceptować regulamin i można instalować. Chwilę poźniej widzimy już stronę główną. Cała instalacja to zaledwie kilka kliknięć i jakieś 2 minuty.

Po zalogowaniu sie do CMS (/manager) widać dashboard i główne menu, gdzie między innymi można sobie zarządzać użytkownikami, modułami (chyba chodzi o pluginy?), w narzędziach można dokonać backupu całej aplikacji/export/import. Ciekawie prezentuje się także system logowania zdarzeń, gdzie można podejrzeć logowane z góry akcje wywołane przez użytkownika.  Można dodawać sobie nowe role systemowe (domyślnie są to administrator, editor, publisher) oraz zdefiniować im ACL – bardzo fajnie. CMS przypomina troszkę WordPressa.

Troszkę inaczej wygląda sprawa kodu. Od początku. Bootstrap niby wporządku, ale na sztywno wpisany jest tam kod html ! (to chyba troszkę za nisko).  Dalej odpalana  jest sesja, tworzony jest obiekt:

$modx = new DocumentParser;

i odpalany parsser. Parser min. dokonuje połączenia z DB (domyślnie używa klasy DBAPI, w której także zdefiniowane są podstawowe metody tj update, insert, oferuje metodę query, w której dodatkowo jest prosty profiler,  itd. troszke to niebezpieczne, zapytanie nie są filtrowane, brak quote escape, ciekawy czy ciezko jest wstrzyknać SQL ), ale to tak na pierwszy rzut oka.

Metoda executeParser(), to tak jakby dispatcher, chociaż  troszkę przesadziłem teraz.

W katalogu /manager/actions są wywoływane akcje, ale cięzko tu mówić o MVC, ponieważ nie ma  kontrolerów. Same pliki actions to mieszanka php, js oraz html. Dlaczego te pliki maja w takim razie rozszerzenie php? czemu nie np. phtml to i edytor ładnie by kolorował składnie – na pierwszy rzut oka nie wiem. Nie wiem jeszcze z jakiego systemu szablonów korzysta, ale później do tego na pewno jeszcze wrócę.

Całość przypomina mieszankę obiektowego php4 z proceduralnym php. Wydaje się być ciężko rozwijalnym projektem. Do tej pory nie zawuażyłem w kodzie żadnego wzorca projektowego, no poza użytym praktycznie wszędzie global, co chyba niegdyś było rejestrem ?

Tyle jeśli chodzi wstępne 10 minut spędzonych nad MODX. Zobaczymy jak się będzie pracowało korzystając z niego.

Na pierwszy rzut oka przyciąga swoim usability, natomiast kod ODSTRASZA I ZNIECHĘCA. Ale prawdopodobnie się mylę.  Ciekawe jaki będzie kolejny wpis na ten temat :)

Zend_Loader_Autoloader_Resource umożliwia  łatwą ingerencję w domyślną strukturę plików i katalogów ZF, a co za tym stoi -  manipulowanie ich nazwami.

Przykładowo w Twoim projekcie przewidziałeś dwa (lub więcej) modułów, np. /application/modules/default/ oraz /application/modules/panel. Chciałbyś teraz uporządkować w ich ramach np. modele, viewhelper’y, formularze, aby zachować porządek nazewnictwa tych klas i logikę ich wywołania.  Wygłądałoby to tak:

/application/modules/default/models
 
/application/modules/default/forms
 
/application/modules/default/views/helpers

oraz

/application/modules/panel/models
 
/application/modules/panel/forms
 
/application/modules/panel/views/helpers

Kolejne nazwy klas, w takim układzie  nazywałyby się np.:  Default_Model_Foo, Default_Form_Foo, Default_View_Helper_Foo,  Panel_Model_Foo,  Panel_Form_Foo, itd …

Oczywiście, przed każdą inicjalizacją obiektu, którejś z tych  klas NIE postępujemy w ten sposób:

require_once '/application/modules/panel/forms/Foo.php';
$form = new Panel_Form_Foo();

ponieważ PHP5 (jak już kiedyś wspominałem) w wielu przypadkach sprawniej radzi sobie z
załadowaniem pliku przez __autoload niż używajać funkcji require_once.
Zatem dążymy do stworzenia instancji nowego obiektu, a jego załadowaniem niech zajmie się Zend Framework.

Możemy to zrobić na przykład dodając nowy zasób, który wskazywał by frameworkowi przestrzeń nazw, oraz lokalizację, w których te klasy się znajdują. Takie rozwiązanie, po pierwsze pozwoli nam wykorzystać świetny mechanizm Application Resource, a także zachować porządek w naszej aplikacji. Przykładowa klasa wyglądała by tak:

class Zextend_Application_Resource_Types extends Zend_Application_Resource_ResourceAbstract
{
 
    public function init()
    {
        $resourceAutoloader = new Zend_Loader_Autoloader_Resource( array(
                        'basePath'      => APPLICATION_PATH,
                        'namespace'     => 'Default',
                        'resourceTypes' => array(
                                            'form' => array(
                                                            'path'       => 'modules/default/forms',
                                                            'namespace'  => 'Form'),
                                            'viewhelper' =>  array(
                                                             'path'       => 'modules/default/views/helpers',
                                                             'namespace' =>  'View_Helper')
                            )
                        ));
        $resourceAutoloader = new Zend_Loader_Autoloader_Resource( array(
                        'basePath'      => APPLICATION_PATH,
                        'namespace'     => 'Panel',
                        'resourceTypes' => array(
                                            'form'       =>  array(
                                                                   'path'       => 'modules/panel/forms',
                                                                   'namespace'  => 'Form'),
                                            'model' =>  array(
                                                             'path'      =>  'modules/panel/models',
                                                             'namespace' =>  'Model',
 
                                            ),
                                            'viewhelper' =>  array(
                                                                   'path'       => 'modules/panel/views/helpers',
                                                                   'namespace' =>  'View_Helper')
                                                    )
        ));
    }
 
}

Pamiętajmy o  odpowiednim wpisie do applikcation.ini:

resources.types      = „array()”

Dzięki takiemu rozwiązaniu wprowadziliśmy do struktury aplikacji naszą logikę, którą bardzo łatwo możemy uruchomić lub przestać z niej korzystać.

W środę, 28 kwietnia 2010 na wydziale MFI UG odbył się wykład zatytułowany „Critical testing process”, który wygłosił pan Wojciech Jaszcz – Dyrektor Departamentu Zapewnienia Jakości, Wdrożeń i Analiz w firmie Acxiom

Mimo konkretnych korków na obwodnicy, spowodowanych naprawą nawierzchni (przynajmniej 3 zwężenia drogi na odcinku 10 km – seek!) udało mi się dojechać na czas.  Razem z matipl tworzyliśmy niewielką grupę słuchaczy.

Temat bardzo ciekawy i na pewno nie został wyczerpany w ciągu półtorej godziny  – został raczej wprowadzony. Pan Wojtek  na wstępie przedstawił 12 fundamentalnych zasad testowania aplikacji. W ich skład wchodzi m.in. Plan, Prepare, Perform, Perfect, Develop team … (niestety tylko tyle udało mi się zapamiętać). Każde z tych zasad zostało omówione na konkretnym przykładzie, co dodatkowo ułatowiło zrozumienie tematu.

Podejście pana Wojtka do tematu było czysto praktyczne, a tym samym w małym stopniu  teoretyczne i oparte o problemy, jakie napotkał pracując w zawodzie. Abstrahując od tematu – takie wykłady na studiach z pewnością zaciekawiły by niejednego studenta, który wyniósł by przynajmniej częsciowe wyobrażenie nt. problemów  i sposobów ich rozwiązywania w przyszłej karierze.

Wykład bardzo mi się podobał. Dzięki niemu zdałem sobie sprawę, jak ważne są testy aplikacji i jak bardzo kosztowne jest zaniedbanie testowania przed wydaniem release (mowa tu np.  o około 60 miliardach dolarów rocznie, jakie wydawane są przez amerykańskie firmy, których soft okazał się trefny).  Zdałem sobie także sprawę, że większość projektów, w których uczestniczyłem nie została przetestowana tak, jak powinna być.

Podobało mi się także stwierdzenie wypowiedziane przez pana Wojtka – „o testy jednostkowe dba programista i musi je wykonać. Koniec, Kropka!! „. Także, nie bez powodu, w przyborniku matipl znalazła się pozycja PHPUnit.

Wykład bardzo ciekawy, udany, dużo się dowiedziałem i już czekam na kolejne tego typu spotkania.

Bardzo często systemem, na którym pracują programiści webaplikacji (mam na myśli głównie pisane w  PHP) jest Windows.  Osobiście pracuję uzywając Linuxa (aktualnie OpenSuse v 11.2) i raczej staram się wymusić na pracodawcy tę praktykę, argumentując to tym, że mogę pracować na środowisku zbliżonym do tego, na jakim stoi produkcja, mogę chociażby uniknąć błędu związanego z nazwami plików (wielkie/małe litery plików – dla Windows to obojetne, a dla Linuxa to już znacząca różnica), czy też sprawdzić wysyłkę emaili (wystarczy mi ze zainstaluje jedynie postfixa, bądź sendmaila, ale raczej jestem za tym pierwszym).

Jeśli chodzi o test wysyłki emali naszej aplikacji to pod Windows jest troszke problematyczne. Osobiście nie znam żadnego darmowego serwera poczty dla Windows, pewnie mógłbym wygoooglać jakiś, ale niestety nie mam czasu.  Dla ludzi korzystajacych z Zend Framework i pracujących  na Windows (i nie tylko także pod Linux, jak kto woli) rozwiązanie jest proste.

Wystarczy stworzyć podklasę dla klasy Zend_Mail_Transport_Sendmail i w niej przeciążyc metodę _sendMail. Przykładowa klasa mogłaby wyglądać tak:

cclass Zextend_Mail_Transport_File extends Zend_Mail_Transport_Sendmail
{
   /**
    * Sciezka zapisu plików z email.
    * 
    * @var string
    */
   protected $_savePath;
 
   public function getSavePath()
   {
       return $this->_savePath;
   }
 
   public function setSavePath($path)
   {
       $this->_savePath = $path;
   }
   /**
    * Zapisuje cały email do plku.
    */
   function _sendMail()
   {
       $fileName = time() . '.eml';
       $data = 'Subject: ' . $this->_mail->getSubject() . "\n"
             . 'To: ' . $this->recipients . "\n"
             . $this->header . "\n"
             . $this->body;
       file_put_contents($this->getSavePath() . '/' . $fileName, $data);
   }
}

Aby utrzymać porządek w kodzie i nie zaburzyc prawidłowego działania naszej aplikacji można napisać nowy resource, np:

class Zextend_Application_Resource_MailTransport extends Zend_Application_Resource_ResourceAbstract
{
 
    public function init()
    {
       if ('development' === APPLICATION_ENV) {
            $transport = new Zextend_Mail_Transport_File();
            $transport->setSavePath(ROOT_PATH . '/temporary/mail');
        } else {
            $transport = new Zend_Mail_Transport_Sendmail();
        }
 
            Zend_Mail::setDefaultTransport($transport);
            Zend_Mail::setDefaultFrom('jarek@kielmas.com');
   }
}

Taki zasób bardzo łatwo wcielamy w życie przez dodanie linijki, obok juz istniejących (lub nie:) innych zasobów  w application.ini:

resources.types = „array()”
resources.view = „array()”
resources.mailtransport = „array()”
resources.navigation = „array()”
resources.acl = „array()”
resources.log = „array()”

Taka podmianka sposobu transportu emaili uodporni nas przed:

  1. nieposiadaniem serwera pocztowego, zainstalowanego na lokalnej maszynie,
  2. bałaganem w kodzie, wywołanym np. użyciem exit(), die() itd,
  3. dotykaniem wersji trunk w repozytorium (wystarczy tylko dodać wpis w pliku konfiguracyjnym, którego lokalna instancja i tak nie jest wersjonowana)