KIELMAS.COM Kielmas Jarosław

Blog głównie o programowaniu

Browsing Posts in WWW

Dla ludzi, którzy mają problem z wygenerowaniem sobie najnowszej dokumentacji Zend Framework w polskim języku udostępniam  quickstart, pochodzący z   wersji 2.0.0dev1.

Oprócz tego dla pozostałych, pracujących na *nix (dla Windows wymagany jest CYGWIN), króciutki opis jak wygenerować sobie pełną  dokumentację ( oczywiście nie wszystko jest przetłumaczone).

Wcześniej zainstaluj sobie następujące biblioteki:

  • autoconf
  • make
  • xsltproc
  • xmllint

Następnie:

$ git clone git://git.zendframework.com/zf.git
$ cd ZendFramework-2.0.0dev1/documentation/manual/pl
$ autoconf (zostanie wygenerowany ./configure)
$ ./configure
$ make

Czekamy chwilkę, w między czasie powstaje sobie _temp_manual.xml, który potem zostanie pokrojony na odpowiednie dokumenty html w katalogu html/

Poniżej dokumentacja w postaci html do ściągnięcia

Logowanie błędów jest kluczowym elementem minitoringu aplikacji. Przekonał się o tym chociażby każdy programista, który pracuje w firmie oferującej dodatkowo dla swoich produktów terminowy support.

Dzięki temu, że błędy są logowane jesteśmy w stanie szybko i czasami transparentnie wyeliminować bugi w aplikacji, tak że klient nawet się nie dowie o ich istnieniu:)

Jeśli Zend Framework jest podstawą twojego projektu oferuję Tobie bardzo wygodny i przejrzysty sposób na logowanie błędów aplikacji.

Potrzebna będzie jedna klasa, ponieważ aż się prosi aby system logowania błędów był ładowany jako resource, przez Zend_Application. Zarządzanie zasobów zaprezentowane w Zend Framework jest moim zdaniem rewelacyjne, ponieważ bardzo łatwo możemy wydzielić zasoby, dzięki czemu mamy wprowadzoną dodatkową  logikę i ład w naszej aplikacji,  ponadto  dowolny zasób  można łatwo i sprawnie włączyć/wyłączyć.

Przykładowa klasa mogłaby wygłądać następująco:

class Zextend_Application_Resource_Log extends Zend_Application_Resource_ResourceAbstract
{
 
    public function init()
    {
        /**
         * Pobranie głównej konfiguracji.
         */
        $config = $this->getBootstrap()->getContainer()->config;
 
        /**
         * Przygotowanie formatu zapisanej informacji.
         *
         * @example Postać zmiennej $config->format:
         * "%timestamp% %priorityName%: %message%"
         */
        $format = new Zend_Log_Formatter_Simple($config->format . PHP_EOL);
 
        /**
         * Sprawdzanie, ktory sposób logowania błędów został włączony.
         *
         * @example Postać zmiennej $config->log->stream:
         * "../temporary/log/error.log" 
         */
        $logger = new Zend_Log();
        if($config->log->file) {
           $logger->addWriter(
                   $this->_getStreamWriter($format, $config->log->stream));
        }
 
        if($config->log->stdoutput) {
           $logger->addWriter(
                   $this->_getStreamWriter($format, 'php://output'));
        }
 
        if($config->log->firebug) {
           $logger->addWriter(
                   $this->_getFirebugWriter($format));
        }
        if($config->log->mail) {
            $logger->addWriter($this->_getMailWriter($config));
        }
 
        /**
         * Używamy tego logger wszędzie w aplikacji.
         */
        Zend_Registry::set( 'log', $logger );
        return $logger;
    }
    /**
     * Zwraca obiekt zapisania informacja do Firebug.
     * 
     * @param Zend_Log_Formatter_Simple $format
     * @return Zend_Log_Writer_Firebug
     */
    private function _getFirebugWriter(Zend_Log_Formatter_Simple $format)
    {
        $writer = new Zend_Log_Writer_Firebug();
        $writer->setFormatter($format);
 
        return $writer;
    }
    /**
     * Zwraca obiekt zapisania informacji w postaci emaila.
     *
     * @param Zend_Config_Ini $config
     * @return Zend_Log_Writer_Mail
     */
    private function _getMailWriter(Zend_Config_Ini $config)
    {
        $mail = new Zend_Mail();
        $mail->setDefaultTransport(new Zend_Mail_Transport_Smtp());
 
        $mail->setFrom($config->email->from, $config->email->fromAlias);
        $mail->addTo($config->app->admin->email);
        $mail->addTo($config->app->webmaster->email);
        $mail->setSubject($config->app->name . ' - nastapil nieoczekiwany blad!');
 
        $writer = new Zend_Log_Writer_Mail($mail);
        $writer->addFilter(Zend_Log::WARN);
 
        return $writer;
    }
    /**
     * Zwraca obiekt zapisania informacji do PHP stream.
     * 
     * @param Zend_Log_Formatter_Simple $format
     * @param string $stream
     * @return Zend_Log_Writer_Stream
     */
    private function _getStreamWriter(Zend_Log_Formatter_Simple $format, $stream = '')
    {
        $writer = new Zend_Log_Writer_Stream($stream);
        $writer->setFormatter($format);
 
        return $writer;
    }
}

Jak widać oferuje ona możliwość logowania błędów na kilka sposobów: streaming do pliku, badź na ekran, JSON’em prosto do firebug’a (warunkiem odczytu jest zainstalowanie wtyczki FirePHP w Firefox) lub powiadomienie na email. W przykładzie część konfiguracji znajduje się w pliku /application/configs/application.ini, ktory ładowany jest poprzez Zend_Config_Ini do rejestru. Odczytany zostaje między innymi email do webmastera oraz do admina, czyli osób odpowiedzialnych za poprawne działanie aplikacji, ponadto zdefiniowane są takie parametry jak scieżka do pliku  z blędami (error.log) czy też format komunikatu z błędem.

Oczywiście trzeba tę funkcjonalność dołączyć  do projektu jako  nowy zasób. Nic prostszego – w pliku konfiguracyjnym /application/configs/application.ini dodajemy kolejny resource do listy:

resources.types      = "array()"
resources.view       = "array()"
resources.navigation = "array()"
resources.log        = "array()"

Dodatkowo musimy zdefniować ustawienia, które załączają odpowiednei przekazanie informacji o błędzie. Czyli znów w /application/configs/application.ini dopisujemy:

log.file      = true
log.firebug   = true
log.mail      = true
log.stdoutput = true

Rzecz jasna korzystamy z odpowiedniego typu logowania w zależności od potrzeb. Bardzo wygodnie jest w trakcie tworzenia kodu, czy też jego testowania, otrzymywać komunikaty do konsoli FF, wykorzystując firePHP, dodatkowo wygodne jest odczytywanie błędów z pliku error.log – otwieramy konsole i  dajemy polecenie:

# tail -f error.log

i mamy nasz system monitorujacy, w którym dokładnie zapisane jest gdzie wystąpił nieoczekiwany błąd (kupujemy telewizor LCD, który pokazuje te komunikaty przez cały czas :) oczywiście żartuje :) ).  Jeśli aplikacja jest już w wersji produkcyjnej to bardzo fajne jest rozwiązanie z przychodzącym emailem – można wtedy szybko interweniować.

Pamiętajmy jednak, że jeśli aplikacja jest już na produkcji, należy zablokować logowanie błędów do FireBug’a!!

Logowane błędy są tylko dla nas – developerów, adminów, …,  i mają stanowić wskazówkę do szybkiego ich  rozwiązania. Jeśli te informacje trafią do niepowołanych rąk to niestety możemy wskazać najsłabsze ogniwo naszej aplikacji, a tym samym stworzyć okazję do włamania się do niej np. hackerowi.

Kolejną sprawą jest miejsce, w którym nasz logger, trzymany w rejestrze, może dokonać zapisu błędu. Jednym z lepszym miejsc jest kontroler ErrorController.php i w nim metoda errorAction,  na którą domyślnie przekierowany jest błąd. Oczywiście gdzie będzie odbywało się logowanie błędów utożsamiony z tą linijką kodu:

if (Zend_Registry::isRegistered('log')) {
            $logger = Zend_Registry::get('log');
            if (!is_null($this->_error)) {
                $logger->log(
                    $this->_error->exception->getMessage() . "\n\n" .
                    $this->_error->exception->getTraceAsString(),
                    Zend_Log::ERR
                );
            }
        }

jest uzależnione od  potrzeb aplikacji i zaprojektowania jej przez programistę.

Taki sposób logowania błędów  można implementować w każdym projekcie – nie tylko opartym o Zend Framework. Po drobnych modyfikacjach, korzystając tylko z kilku komponentów (w wersji ZF 1.10.6), Log(20 plików), Wildfire (8 plików),  Json (14 plików), Config (8 plików) + Exception.php, można stworzyć taki sam system logowania błędów.

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 :)

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)