‘ Tworzenie stron www ’ Kategoria archiwum

Zend_Navigation i własny routing

22 lutego 2011

Zend_Navigation to całkiem przyjemny moduł Zend Framework, który ułatwia nam tworzenie takich elementów strony jak menu czy tzw. breadcrumbs. Zend_Navigation dzieli się tak naprawdę na dwie części: Zend_Navigation_Page_Mvc i Zend_Navigation_Page_Uri. Krótko je opiszę:

  • Zend_Navigation_Page_Mvc pozwala niejako zintegrować Zend_Navigation z innymi elementami strony. Podczas konfiguracji Zend_Navigation_Page_Mvc musimy podać przynajmniej jeden z wymienionych elementów: action, controller, module lub route. Trzy pierwsze są chyba jasne, czwarty to nazwa routera z jakiego chcielibyśmy skorzystać. Na podstawie tych informacji Zend_Navigation_Page_Mvc wybierze odpowiedni router i później w oparciu o niego zbuduje linki np. w naszym menu. Zend_Navigation_Page_Mvc jest zalecane ponieważ ładnie się integruje z MVC, zmiany w routingu automatycznie wpłyną na działanie naszych elementów nawigacji.
  • Zend_Navigation_Page_Uri pozwala na budowanie linków bez oglądania się na pozostałe elementy strony. Sami wpisujemy jak link ma wyglądać i ZF nijak w to nie integruje. Jest to wygodne gdy chcemy wstawić pusty link (np. na potrzeby JavaScript) lub zewnętrzny odnośnik. Nie polecam natomiast używać tego sposobu do linkowania wewnętrznego. Od tego jest Zend_Navigation_Page_Mvc.

O ile w Zend_Navigation_Page_Uri kwestia linków jest jasna (sami sobie ustalamy jak mają wyglądać) o tyle w Zend_Navigation_Page_Mvc linki są budowane za pomocą Zend_Controller_Action_Helper_Url. Klasa ta tworzy link na podstawie konfiguracji jaką przekazaliśmy do Zend_Navigation. Szuka odpowiedniego routera, czyta zawarte w nim wskazówki i generuje link.

Z domyślnym routerem nie ma problemów. O jego działaniu można poczytać tutaj. W skrócie, linki budowane są w następujący sposób: controller/action. Można też dołączyć na początku nazwę modułu: module/controller/action. Jest także sposób na przekazywanie dodatkowych parametrów: controller/action/var1/value1/var2/value2.

Jeżeli adresy oparte są o taki domyślny router to Zend_Controller_Action_Helper_Url nie ma problemów z generowaniem linków. Do Zend_Navigation przekazujemy standardową konfigurację (pisałem o niej wcześniej: action, controller, module lub route) i aplikacja ma wszystkie potrzebne informacje wymagane do budowy linka.

Co jednak gdy mamy niestandardowy routing? Oto przykład z mojego kodu:

PHP:
  1. $router->addRoute(
  2.     'functions',
  3.     new Zend_Controller_Router_Route(
  4.         ':function',
  5.         array(
  6.             'controller' => 'function',
  7.             'action'     => 'function',
  8.             'function'   => ''
  9.         ),
  10.         array(
  11.             'function' => '\w*'
  12.         )
  13.     )
  14. );

Router nazywa się functions, przekierowuje na kontroler FunctionController i akcję functionAction. Link składa się tylko z jednego elementu (adresy mają taką postać: www.example.com/function), który został nazwany :function, domyślnie ma pustą wartość i może składać się z małych i dużych liter, cyfr i znaku podkreślenia _.

Czy Zend_Controller_Action_Helper_Url poradzi sobie ze zbudowaniem linku? Nie. Ma on co prawda informacje jaki router ma zostać użyty, jaki kontroler i akcja ale skąd ma wiedzieć co ma podstawić pod :function?

Tu dochodzimy do sedna sprawy. W konfiguracji Zend_Navigation trzeba uwzględnić fakt, że nie wszystkie elementy linka są znane i należy te braki uzupełnić. Służy do tego dodatkowe ustawienie. Zmienna $params w postaci tablicy w której podajemy wszystkie brakujące fragmenty linka.

Najlepiej zobrazować to na przykładzie. Oto wycinek (zmodyfikowany na potrzeby tego wpisu) mojej konfiguracji, dla pojedynczego elementu menu:

PHP:
  1.     'label'  => 'Nazwa linku',
  2.     'route'  => 'functions',
  3.     'params' => array('function' => 'moj_adres')
  4. );

Label to tzw. anchor linka. Route to nazwa routera. W params przekazujemy brakujące ogniwo czyli podpowiadamy, że w routerze :function ma zostać zastąpione przez moj_adres. Ostatecznie Zend_Navigation wygeneruje menu w którym będziemy mieli link w takiej postaci: www.example.com/moj_adres.


Zend Framework: własne ustawienia w application.ini

21 lutego 2011

Niedawno (bardzo niedawno) zacząłem bawić się Zend Framework. Aż wstyd, że dopiero teraz się za niego zabieram ale dlaczego tak właśnie się stało to już dłuższa historia, innym razem :) Jako bardzo początkujący w ZF mam wiele typowych, banalnych problemów. Chciałbym co jakiś czas napisać parę słów o tym oprogramowaniu ale nie oczekujcie jakiejś wielce tajemnej wiedzy. Dopiero się uczę.

Główna konfiguracja ZF znajduje się w configs/application.ini. Nic nie stoi na przeszkodzie aby stworzyć własny plik z innymi danymi konfiguracyjnymi, dedykowanymi naszej aplikacji. Ale jeżeli tych opcji nie jest zbyt dużo to szkoda męczyć serwer dodatkowym czytaniem i parsowaniem pliku. Warto rozważyć dodanie ustawień do głównego pliku konfiguracyjnego (wspomniany application.ini).

Początkowo myślałem, że istnieje jakiś z góry narzucony format własnych wpisów w application.ini a wartości tam zapisane są automatycznie dostępne w całej aplikacji. Nic z tych rzeczy.

W związku z tym możesz użyć dowolnego nazewnictwa podczas zapisywania swojej konfiguracji. Łatwiej jest pracować na przykładach więc załóżmy, że nasza strona ma mieć możliwość odczytania Twojej skrzynki pocztowej a wszystkie dane potrzebne do "wejścia" na nią będą w application.ini. Może to wyglądać tak:
mail.user = "mariusz"
mail.pass = "123"
mail.pop3 = "pop3.example.com"
mail.smtp = "smtp.example.com"
mail.port = "110"

Gdyby były wątpliwości, jest to fragment naszej konfiguracji z application.ini.

Konfiguracja zapisana ale jak się do niej dostać w kontrolerach, modelach etc.? Jak to zwykle bywa w ZF, sposobów jest wiele. Dla uzupełnienia dodam, że poniższe metody dadzą dostęp do całej konfiguracji z pliku application.ini, nie tylko do naszych ustawień.

W kontrolerze do konfiguracji można się dostać w ten oto sposób (kod do umieszczenia np. w jakiejś akcji):

PHP:
  1. $bootstrap = $this->getInvokeArg('bootstrap');
  2. $options = $bootstrap->getOptions();

Metoda ładna i przyjemna. W $config mamy zwykłą tablicę PHP, do konfiguracji możemy dostać się tak:

PHP:
  1. echo $options['mail']['user'];

Niestety sposób ograniczony tylko do kontrolera. Poza kontrolerem możesz zrobić tak:

PHP:
  1. $bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
  2. $options = $bootstrap->getOptions();

Ponownie konfigurację dostajemy w postaci tablicy.

Aby ustawienia były obiektem, można użyć Zend_Config. Nic prostszego:

PHP:
  1. $config = new Zend_Config($options);

Oczywiście $options to wcześniej wyciągnięte przez nas ustawienia, w postaci tablicy.

Teraz już mamy ładny, obiektowy dostęp do naszych danych skrzynki pocztowej. Sposób użycia:

PHP:
  1. echo $config->mail->user;

Nadal jednak może Ci się nie podobać, że w różnych miejscach aplikacji musisz używać różnych sposobów na wydobycie tych ustawień. Z pomocą przychodzi Zend_Registry. Gdzieś w pliku Bootstrap.php umieść tą metodę:

PHP:
  1. protected function _initConfig()
  2. {
  3.     $config = new Zend_Config($this->getOptions(), TRUE);
  4.     Zend_Registry::set('config', $config);
  5.     return $config;
  6. }

W pierwszej linijce przekazujemy do Zend_Config całą konfigurację. W Bootstrap jest do niej bezpośredni dostęp dlatego wystarcza $this->getOptions. Drugi parametr ustawiony na TRUE zezwala na późniejsze ewentualne modyfikacje konfiguracji. Ustaw zależnie od potrzeb. Po takich operacjach mamy w zmiennej $config obiekt ze wszystkimi ustawieniami.
Druga linijka zapisuje obiekt do rejestru. Od tej chwili w całej aplikacji jest bardzo łatwy dostęp do tych danych. Wystarczy w jakimkolwiek miejscu użyć takiego kodu:

PHP:
  1. echo Zend_Registry::get('config')->mail->user;

Prawda, że wygodne? Rejestr w Zend Framework to alternatywa dla różnego rodzaju metod do globalnego dostępu do danych. Tak naprawdę to do wszystkich ustawień z application.ini można się dostać z poziomu tablicy $GLOBALS:

PHP:
  1. $mail = $GLOBALS['application']->getOption('mail');
  2. echo $mail['user'];[

Nie polecam jednak takiej drogi na skróty. Pomijam już brzydotę tego rozwiązania :) Ale w przyszłości możemy się nieźle przejechać gdy w ZF coś zmienią. Lepiej używać do takich operacji dedykowanych sposobów.


Phing

12 lutego 2011

Istnieje dowcip, że komputer to narzędzie dzięki któremu rozwiązujemy problemy, które bez niego by nie istniały. Coś w tym jest. Mimo to komputery w dużej części służą do robienia różnych rzeczy szybciej, dokładniej, jednym słowem lepiej. Co dziwne, sami programiści czasami o tym zapominają i robią mnóstwo rzeczy "na piechotę" zamiast wyręczyć się maszyną.

Im bardziej skomplikowany jest projekt nad którym pracujemy tym bardziej złożony jest proces od tworzenia po wdrożenie kodu. Wgrywanie plików na kolejne serwery (produkcyjny, testowy etc.), pilnowanie spójności bazy danych, zarządzanie repozytorium, generowanie dokumentacji, wersjonowanie itd. To wszystko zabiera naprawdę mnóstwo czasu.

Czy można to jakoś zautomatyzować?
Oczywiście, że można i są do tego narzędzia, chociażby make. Pojawiają się jednak przeszkody.
Po pierwsze, są problemy z przenośnością między różnymi systemami. Najczęściej dane rozwiązanie działa tylko na jednej rodzinie systemów.
Po drugie, narzędzia te nie są łatwe (dla windowsowca make to naprawdę orka). Oczywiście ich nauka to samo dobro, przyda nam się na przyszłość ale czasami są ważniejsze rzeczy do poznania a i czas nie jest tu bez znaczenia.
Po trzecie wreszcie, przydałoby się nam narzędzie pomyślane i zaprojektowane stricte pod PHP. Najlepiej napisane w samym PHP bo po co nam kolejne technologie...

Panaceum na nasze bolączki okazuje się Phing.

Nie jest on nowym projektem. Nie mogłem doszukać się konkretnej daty jego startu ale wszystkie znaki na niebie i ziemi wskazują, że zaczęło się już w 2002 roku. Mimo upływu tylu lat, w Polsce (trudno mi oceniać jak jest w innych krajach) projekt nie jest chyba zbyt popularny. Takie jest przynajmniej moje wrażenie bo google nie zwraca zbyt wielu sensownych wyników na zapytanie "Phing" dla naszego języka, nie widywałem również dyskusji na forach/grupach dyskusyjnych o tym narzędziu a już zupełnie nikt o nim nie wspomina w CV lub ogłoszeniach o pracę. Błąd.

Sam zainteresowałem się Phingiem jakiś czas temu, co oznacza, że było to kiedyś ale nie pamiętam dokładnie kiedy :) Niestety, tylko na niego zerknąłem, stwierdziłem, że mi to nie jest potrzebne i zapomniałem. Faktycznie, wtedy nie było mi potrzebne ale z perspektywy czasu wiem, że rok, półtora roku temu Phing mógłby znacznie ułatwić mi życie.

Phing'a bardzo ładnie opisał zyxist w artykule Phing - system budowania dla PHP (proponuję zacząć naukę Phinga właśnie od tego artykułu) więc nie będę powielał tych informacji. Dla zachęty pozwolę sobie jedynie zacytować listę możliwości:

  • kopiowanie plików, katalogów,
  • tworzenie archiwów GZIP, BZIP2, ZIP,
  • tworzenie archiwów PHAR,
  • tworzenie pakietów PEAR i PEAR2,
  • zarządzanie numerami wersji,
  • wysyłanie plików na serwer FTP,
  • wysyłanie żądań HTTP, autoryzacja HTTP,
  • wysyłanie e-maili,
  • generowanie raportów pokrycia kodu testami (PHPUnit),
  • wykonywanie testów jednostkowych (PHPUnit),
  • generowanie dokumentacji API (PHPDocumentor),
  • obsługa systemów kontroli wersji: CVS, SVN, Git,
  • instalacja baz danych,
  • obsługa SCP i SSH,
  • wsparcie dla różnych narzędzi takich, jak PHP_CodeSniffer.

Lista ta jest niepełna, a wraz z rozwojem projektu liczba dostępnych typów zadań jest coraz większa.

Dodam od siebie jedynie tyle, że gdyby jednak czegoś Wam na tej liście brakowało to Phing'a można w łatwy sposób rozbudowywać pisząc specjalne rozszerzenia. Odsyłam do dokumentacji. Właśnie, dokumentacja całego projektu jest przyjemna, wszystko ładnie opisane, nikt nie powinien mieć problemów ze zrozumieniem.

Gdyby ktoś chciał poznać inne narzędzia tego typu to polecam zacząć od komentarzy do wcześniej podlinkowanego artykułu z zyxist.com. Warto przejrzeć również linki zamieszczone na końcu tekstu Automatyczne publikowanie aplikacji PHP autorstwa Jakuba Zalasa.


php.js

05 grudnia 2010

Na blogu staram się opublikować coś chociaż raz w miesiącu. Pomysłów na tematy jest więcej tylko czasu brak (wiem, przynudzam już z tym brakiem czasu). Zdarza się tak, że chcę o czymś napisać, tworzę szkic, napiszę klika zdań i... nie kończę, nie publikuję.

Tak było z php.js. Chciałem o tym napisać półtora roku temu! Wspomniałem potem o tej bibliotece we wpisie "Pokochać JavaScript" ale to były zaledwie dwa zdania.

Ostatnimi laty coraz częściej miałem do czynienia z JavaScript, zwłaszcza przez ostatni rok. Początkowo nie byłem z tego powodu jakoś szczególnie radosny ale dzisiaj spokojnie mogę napisać, że darzę JS taką samą sympatią jak PHP. Niestety czasami brakuje mi funkcji dobrze znanych z PHP. Szczególnie w pracy z ciągami znaków.

Nasze kochane PHP zdobyło swoją popularność między innymi dlatego, że posiada wiele wbudowanych funkcji, które rozwiązują za nas szereg często występujących problemów. Rozleniwiony takim komfortem programista (na przykład ja) może czuć się zagubiony i zakłopotany podczas pracy z innymi, mniej przyjaznymi pod tym względem językami (na przykład JavaScript).

Na szczęście kilku zdolnych ludzi powołało do życia zbiór funkcji pod wspólną nazwą php.js. Projekt ten ma na celu przeniesienie popularnych funkcji dostępnych w PHP do JavaScript. Efekt jest imponujący.

Ilość funkcji przeniesionych z PHP sięga 438 (stan na 5 grudnia 2010)! Udostępniono gotowe do ściągnięcia paczki, możemy również sami je tworzyć wybierając tylko te funkcje, które nas interesują. Nic także nie stoi na przeszkodzie aby podejrzeć źródło wybranej funkcji i po prostu przekopiować kod do własnego projektu.

Projekt jest open source, każdy może tam wnieść swoją cenną wiedzę. Z tym jednak coraz ciężej, do oprogramowania zostały już funkcje najbardziej problematyczne w implementacji.

Czerp z tego projektu pełnymi garściami ale nie używaj bezmyślnie całego zbioru funkcji! Nie uwierzę, że pracujesz przy pojedynczym projekcie, który wymaga od Ciebie więcej niż 5% funkcji zawartych w php.js. Przemyśl czego potrzebujesz i zbuduj własny plik lub wklej odpowiednie funkcje do swoich skryptów.

Uwaga
Funkcje z projektu php.js można pobrać w trzech postaciach. Zwykłej, obiektowej (namespaced) i zgodnej z założeniami CommonJS. Jeżeli nie wiesz co wybrać to znaczy, że potrzebujesz postaci domyślnej (zwykłej).


Raport zbiorczy z kilku tabel w MySQL

01 listopada 2010

Dzisiaj kolega zapewnił mi niezłą "rozrywkę" :) Przyszedł z ciekawym zadaniem a ja jestem uparty, zawsze siedzę aż zrobię. Problem był następujący:

  • Mamy dwie tabele w bazie
  • Obie tabele wyglądają tak samo (pola: ID, saldo)
  • Pola ID w obu tabelach dotyczą tego samego obiektu ale w obu tabelach ID te mogą się różnić (np. w jednej są ID 1, 5, 7 a w drugiej 1, 3, 9)
  • Oczekiwany efekt to jedno zapytanie SQL, które zwróci trzy kolumny: ID, saldo z tabeli pierwszej, saldo z tabeli drugiej
  • W raporcie muszą być wszystkie numery ID z obu tabel ale nie mogą się dublować
  • Jeżeli konkretne ID istnieje tylko w jednej tabeli to w kolumnie z saldem z tej tabeli ma się wyświetlić odpowiednia wartość a w drugiej kolumnie (z saldem z drugiej tabeli) ma być 0

Najłatwiej objaśnić wszystko na przykładzie. Zrzut bazy:

SQL:
  1. CREATE TABLE IF NOT EXISTS `a` (
  2.   `id` tinyint(2) UNSIGNED NOT NULL,
  3.   `saldo` varchar(100) NOT NULL,
  4.   PRIMARY KEY (`id`)
  5. ) ENGINE=MyISAM;
  6.  
  7. INSERT INTO `a` (`id`, `saldo`) VALUES
  8. (1, '10'),
  9. (2, '15'),
  10. (5, '12'),
  11. (8, '9');
  12.  
  13. CREATE TABLE IF NOT EXISTS `b` (
  14.   `id` tinyint(2) UNSIGNED NOT NULL,
  15.   `saldo` varchar(100) NOT NULL,
  16.   PRIMARY KEY (`id`)
  17. ) ENGINE=MyISAM;
  18.  
  19. INSERT INTO `b` (`id`, `saldo`) VALUES
  20. (2, '11'),
  21. (5, '7'),
  22. (11, '19');

Prezentacja graficzna:

Oczekiwany wynik:

Teraz powinno być już wszystko jasne. Z pozoru proste zadanie ale gdy się chwilę zastanowimy to pojawia się wiele problemów. Polecam samemu pokombinować nim poczytacie dalej :)

Pierwszy wniosek jaki się nasuwa to wszelkie kombinacje INNER/LEFT/RIGHT JOIN oparte tylko na tych dwóch tabelach i relacjach między ID nie mają sensu. Owszem, te ID dotyczą tego samego obiektu ale nasze wymagania są dość specyficzne. Chcemy wyświetlić bez powtarzania wszystkie ID, bez względu na to czy istnieją w jednej czy w dwóch tabelach.

Najpierw skupmy się wyłącznie na tychże ID. Czy wiesz jakim zapytaniem wyświetlić ich listę? Jeżeli wszelkie JOIN'y odpadają to pozostaje nam połączyć wyniki poprzez użycie UNION.

SQL:
  1. SELECT id FROM a UNION SELECT id FROM b

Wynik:

Piękne w swej prostocie. Dodatkowo UNION domyślnie jest w postaci UNION DISTINCT co powoduje wywalenie duplikatów z wyników.

Po tym sukcesie chciałoby się szybko stworzyć coś takiego:

SQL:
  1. SELECT (SELECT id FROM a UNION SELECT id FROM b) AS id

Niestety, póki podzapytanie zwraca więcej niż jeden wynik to takie zapytanie zwróci błąd numer 1242: "Subquery returns more than 1 row". Mamy więc dane, które są dla nas kluczowe ale nie możemy ich wykorzystać. Gdyby tak była trzecia tabelka z tymi informacjami...

I to jest ten moment gdy powinniśmy sobie przypomnieć, że podzapytania można stosować praktycznie w każdym miejscu zapytania, również w sekcji odpowiedzialnej za wybieranie tabel. Rozwińmy nasze zapytanie:

SQL:
  1. SELECT z.id FROM (SELECT id FROM a UNION SELECT id FROM b) AS z

Wynik jest taki sam jak na ostatnim obrazku ale tym razem stworzyliśmy sobie tymczasową, wirtualną tabelę z której możemy korzystać jak i kiedy nam się podoba. Teraz mamy już naprawdę z górki. Nareszcie można użyć tak dobrze znane nam LEFT JOIN ponieważ mamy dane, które spajają obie tabele.

SQL:
  1. SELECT z.id, a.saldo AS "saldo A", b.saldo AS "saldo B" FROM (SELECT id FROM a UNION SELECT id FROM b) AS z LEFT JOIN a ON z.id = a.id LEFT JOIN b ON z.id = b.id

Wynik:

Udało się! Wyciągnęliśmy wszystkie potrzebne informacje, jednym zapytaniem. Jeszcze tylko kosmetyczne poprawki:

SQL:
  1. SELECT z.id, IFNULL(a.saldo, 0) AS "saldo A", IFNULL(b.saldo, 0) AS "saldo B" FROM (SELECT id FROM a UNION SELECT id FROM b) AS z LEFT JOIN a ON z.id = a.id LEFT JOIN b ON z.id = b.id

I mamy nasz upragniony raport:

Mam nadzieję, że ktoś czegoś się z tego nauczył :) Ja rozwiązując ten problem przypomniałem sobie możliwości nie tylko UNION i wszelkich JOIN'ów ale również szereg innych, ciekawych ale rzadko stosowanych konstrukcji SQL. Nim doszedłem do rozwiązania to nieźle kombinowałem... :)


Flash z obcej domeny

09 sierpnia 2010

Gdy próbujemy osadzić na naszej stronie plik flash pochodzący z innej domeny możemy spotkać się z problemem braku interakcji. Nagle przestają działać np. odnośniki.

Jest na to rozwiązanie. Podczas osadzania pliku SWF należy umieścić dodatkowy parametr: AllowScriptAccess. Może on przyjmować trzy ustawienia:

  • always - komunikacja między plikiem Flash a stroną www na której plik został umieszczony odbywa się zawsze, nawet gdy plik jest umieszczony pod innym adresem niż sama strona.
  • sameDomain - komunikacja następuje tylko gdy strona i plik znajdują się w tej samej domenie.
  • never - niezalecane i wycofywane ustawienie. Komunikacja nie następuje nigdy. Dokumentacja proponuje, że jeżeli masz pliki SWF, którym nie ufasz i nie chcesz aby mogły się komunikować to utwórz dla nich osobną subdomenę i stamtąd je linkuj ustawiając AllowScriptAccess na "sameDomain".

Domyślne ustawienie parametru AllowScriptAccess to sameDomain. Pamiętaj o tym! Przy takim ustawieniu napotkasz problemy wstawiając SWF z innej domeny.

PS
Problem może dotyczyć nawet sytuacji gdy wejdziemy na stronę poprzez adres z www a pliki flash wstawione są jako adresy bez www. Najłatwiejszym rozwiązaniem problemu jest użyć przekierowanie 301. Tylko pamiętajcie, że jeżeli macie przekierowanie to mimo to starajcie się podawać prawidłowe adresy. Po co męczyć serwer dodatkowymi przekierowaniami...

PS 2
Wpis inspirowany moimi wczorajszymi problemami :)


Pogoda na stronie www

23 czerwca 2010

Skryptów PHP do pobierania informacji o pogodzie widziałem kilka. Większość to straszne kobyły, część nie działa, inna część działa błędnie lub mało wydajnie. Znowu przyszło mi pisać coś swojego...

Z pogodą na stronie jest jeden podstawowy problem: trzeba skądś pobrać dane. Są trzy wyjścia:

  • Wstawić na stronę gotową "wklejkę", która jest odpowiedzialna za wyświetlenie pogody (i przy okazji linka do źródła, logo innego serwisu itp.).
  • Parsować jakąś stronę www i "na chama" wyciągać z niej informacje o pogodzie (np. z pogoda.onet.pl).
  • Parsować ogólnodostępne źródło, najczęściej jakiś RSS

Pierwszym sposobem zajmować się nie będziemy wcale. Po to piszemy własny skrypt żeby wyświetlić pogodę dokładnie tak jak chcemy, bez zbędnych linków, obrazków itd. a nie tak jak ktoś nam nakaże.

Drugi sposób ma swoje zalety i wady.
Nasze rodzime serwisy informacyjne mają bogatą bazę polskich miejscowości. Każdy znajdzie tam swoją miejscowość lub inną, bardzo blisko położoną swojej. Dodatkowo najczęściej udostępniają bardzo wiele informacji pogodowych.
Przejdźmy do wad. Po pierwsze, nasze (znaczy polskie) serwisy nie udostępniają informacji o pogodzie w jakimś otwartym standardzie np. RSS. Musimy na własną rękę parsować wybraną witrynę. Gdy na stronie coś się zmieni to w najlepszym przypadku nasza pogoda nagle przestanie działać. W najgorszym, zaczną się u nas wyświetlać jakieś bzdury co w skrajnym przypadku rozwali nam cały serwis...
Druga wada jest powiązana z pierwszą. Skoro musimy parsować stronę to zwyczajnie kradniemy dane. I nie pomoże nawet informacja u nas na stronie skąd pobieramy pogodę. Jeżeli ktoś nie udostępnił RSS, XML czy innego ustrojstwa to znaczy, że nie chce nam czegoś dać, koniec kropka.

I nareszcie przechodzimy do trzeciego sposobu. Zagraniczne serwisy o pogodzie są bardziej liberalne od polskich i najczęściej każde miasto ma swój kanał RSS, który możemy spokojnie pobierać. Mamy dużo większą pewność, że nie będziemy musieli raz na kilka miesięcy zmieniać swoich skryptów w skutek zmian w strukturze pliku nad którym pracujemy. Nie obejdzie się jednak bez wad. Baza miejscowości jest przeważnie dużo skromniejsza niż byśmy tego oczekiwali. Jednak na własnym przykładzie przetestowałem, że nie jest to aż taki duży problem. Dla Tomaszowa Mazowieckiego na jednej ze stron wyświetlam pogodę z kanału Łodzi (jakieś 45km odległości licząc od środka jednego miasta do środka drugiego) i zawsze jest wyjątkowo aktualna :)
Udostępniono tylko kilka podstawowych informacji o pogodzie ale tu również praktyka pokazuje, że to jest wystarczające. Pamiętaj, że pogoda na Twojej stronie to tylko dodatek a nie główny element. Jeżeli jest inaczej to musisz podpisać umowę z odpowiednimi instytucjami o udostępnienie danych pogodowych a nie myśleć o pobieraniu tego wszystkiego od innych...

Przejdźmy do konkretów. Rok temu stanąłem przed problemem wyświetlania pogody na stronie www. Dzisiaj udostępniam skrypt :) Zdecydowałem się skorzystać z serwisu weather.yahoo.com i po dziś dzień nie miałem okazji pożałować tego wyboru. Poniżej zamieszczam prostą klasę PHP5 z której korzystam:

PHP:
  1. <?php
  2.  
  3. class yahoo_weather {
  4.  
  5.   public $city_code;
  6.   protected $weather;
  7.  
  8.   public function __construct($city_code) {
  9.     $this->city_code = $city_code;
  10.   }
  11.  
  12.   public function get($get_channel = NULL) {
  13.  
  14.     if(empty($this->weather)) {
  15.  
  16.       try {
  17.         $xml = @new SimpleXMLElement('http://weather.yahooapis.com/forecastrss?w='. $this->city_code .'&u=c', NULL, TRUE);
  18.       } catch (Exception $e) {
  19.         return FALSE;
  20.       }
  21.  
  22.       $xml->registerXpathNamespace('yweather', 'http://xml.weather.yahoo.com/ns/rss/1.0');
  23.  
  24.       $wind       = $xml->xpath('//channel/yweather:wind');
  25.       $atmosphere = $xml->xpath('//channel/yweather:atmosphere');
  26.       $astronomy  = $xml->xpath('//channel/yweather:astronomy');
  27.       $condition  = $xml->xpath('//channel/item/yweather:condition');
  28.       $forecast   = $xml->xpath('//channel/item/yweather:forecast');
  29.  
  30.       $this->weather = array(
  31.         'wind'       => array(
  32.           'chill'      => "{$wind[0]['chill']}",
  33.           'direction'  => "{$wind[0]['direction']}",
  34.           'speed'      => "{$wind[0]['speed']}",
  35.           ),
  36.         'atmosphere' => array(
  37.           'humidity'   => "{$atmosphere[0]['humidity']}",
  38.           'visibility' => "{$atmosphere[0]['visibility']}",
  39.           'pressure'   => "{$atmosphere[0]['pressure']}",
  40.           'rising'     => "{$atmosphere[0]['rising']}",
  41.         ),
  42.         'astronomy'  => array(
  43.           'sunrise'    => "{$astronomy[0]['sunrise']}",
  44.           'sunset'     => "{$astronomy[0]['sunset']}",
  45.         ),
  46.         'condition'  => array(
  47.           'text'       => "{$condition[0]['text']}",
  48.           'code'       => "{$condition[0]['code']}",
  49.           'temp'       => "{$condition[0]['temp']}",
  50.           'date'       => "{$condition[0]['date']}",
  51.         ),
  52.         'forecast'   => array(
  53.           'today'      => array(
  54.             'day'        => "{$forecast[0]['day']}",
  55.             'date'       => "{$forecast[0]['date']}",
  56.             'low'        => "{$forecast[0]['low']}",
  57.             'high'       => "{$forecast[0]['high']}",
  58.             'text'       => "{$forecast[0]['text']}",
  59.             'code'       => "{$forecast[0]['code']}",
  60.           ),
  61.           'tomorrow'   => array(
  62.             'day'        => "{$forecast[1]['day']}",
  63.             'date'       => "{$forecast[1]['date']}",
  64.             'low'        => "{$forecast[1]['low']}",
  65.             'high'       => "{$forecast[1]['high']}",
  66.             'text'       => "{$forecast[1]['text']}",
  67.             'code'       => "{$forecast[1]['code']}",
  68.           )
  69.         )
  70.       );
  71.  
  72.       $xml = NULL;
  73.     }
  74.  
  75.     if($get_channel == NULL) {
  76.       return $this->weather;
  77.     }
  78.     else {
  79.       return $this->weather[$get_channel];
  80.     }
  81.   }
  82. }

Użycie skryptu wygląda następująco (pobieramy kanał http://weather.yahooapis.com/forecastrss?w=505120&u=c):

PHP:
  1. $yahoo_weather = new yahoo_weather('505120');
  2. $weather = $yahoo_weather->get();

I od teraz w zmiennej $weather mamy piękną tablicę ze wszystkimi informacjami o pogodzie. Dodatkowo możemy dokonać pewnych przekształceń (według własnych upodobań):

PHP:
  1. $weather['wind']['speed'] = round($weather['wind']['speed'], 1);
  2.  
  3. $weather['astronomy']['sunrise'] = substr($weather['astronomy']['sunrise'], 0, -3);
  4.  
  5. $sunset = explode(':', substr($weather['astronomy']['sunset'], 0, -3));
  6. $sunset[0] += 12;
  7.  
  8. $weather['astronomy']['sunset'] = $sunset[0] .':'. $sunset[1];

Specjalny kod miejscowości, który przekazujemy do klasy musimy znaleźć sami. Wchodzimy na weather.yahoo.com, odszukujemy miasto, które nam najbardziej odpowiada i klikamy w prawym górnym roku na pomarańczową ikonkę RSS. W adresie na jaki zostaniemy przekierowani w parametrze "p" odnajdziemy interesujący nas kod. Właśnie wyczytałem w dokumentacji, że w adresie RSS zamiast parametru "p" (który został uznany za przestarzały) należy używać parametru "w" podając tzw. kod WOEID. Problem w tym, że gdy odnajdzie się stronę z pogodą dla wybranej miejscowości to sami linkują ikonkę RSS pod adres z parametrem "p". Kod, którego potrzebujemy jest w adresie strony z pogodą dla miasta. Dla Łodzi adres to http://weather.yahoo.com/poland/lodz/%EF%BF%BD%EF%BF%BDd%EF%BF%BD-505120/ z czego interesują nas ostatnie cyferki czyli 505120. Oto nasz kod (wcześniej PLXX0014). Poprawiłem klasę, jest już przygotowana do nowego standardu.

Klasa jest prosta i nie będę jej omawiał :) Na jej podstawie można napisać parser pogody z innego serwisu niż weather.yahoo.com lub np. nauczyć się jak pobierać i parsować dokumenty RSS, Atom, XML itp.

Skąd wziąć ikony pogody? Ten problem powtarza się bardzo często, ładne ikony to nieodzowny element prognozy pogody na każdej stronie www. Tu nie pomogą nam nawet zagraniczne serwisy. Nie chcą udostępniać swoich ikon bo to dla nich dodatkowy ruch na łączach i serwerach. A jak chcą to są dodatkowe obostrzenia, konieczność wyświetlania ich logo itp. Z tego powodu moja klasa nie wyciąga adresu do ikony aktualnej pogody mimo, że taki jest podawany (chociaż nie tak otwarcie jak reszta danych). Na szczęście w internecie jest sporo ikon, często darmowych (przynajmniej dla zastosowań niekomercyjnych), którymi możemy na swojej stronie zobrazować pogodę. Tu pojawia się zaleta wyboru weather.yahoo.com. Ich RSS udostępnia kod warunków pogody np. w chwili gdy to piszę jest częściowe zachmurzenie w nocy czemu powinna odpowiadać ikona księżyca za lekkimi chmurkami i takim warunkom przypisano kod 33. Wiele gotowych pakietów ikon uwzględnia te kody przez co sami już nie musimy sprawdzać wszystkich kodów i samemu ich przypisywać do odpowiednich ikon. Wystarczy wyświetlić ikonę 33.png :)

Nie możesz znaleźć ikon dla siebie? Moim faworytem są te ikony. Są już przystosowane do kodów z weather.yahoo.com. Co więcej, ich autorem jest nasz rodak ;) Ikony są darmowe do zastosowań niekomercyjnych a za wykorzystanie w komercyjnym projekcie trzeba zapłacić 200zł (tak było rok temu, teraz nie wiem). Warte tych pieniędzy.

W ekstremalnej sytuacji można ściągnąć ikony weather.yahoo.com na własny serwer. Chyba się nie pogniewają... :)

Na koniec pamiętaj aby wynik powyższej klasy zapisywać jakoś do cache (np. korzystając z dobrodziejstw crona). Niedopuszczalne jest pobieranie informacji o pogodzie za każdym razem gdy musimy je wyświetlić u siebie na stronie. To spowodowałoby bardzo duży wzrost transferu, niepotrzebne obciążenie maszyny i opóźnienie w wyświetlaniu się naszej witryny. Osobiście ściągam pogodę raz na godzinę.

PS
Zachęcam do zapoznania się z dokumentacją RSS, którą można znaleźć pod tym adresem developer.yahoo.com/weather.

PS 2
A może jednak jakiś polski serwis udostępnia w prostej postaci i za darmo informacje o pogodzie? Poproszę o linki w komentarzach :)


Reset styli CSS

11 maja 2010

No tak... Ledwo się Riddle pojawił a już mi podebrał temat o którym planowałem w najbliższych dniach napisać ;) Ale to bardzo dobrze bo pewnie nie mielibyście takiego pożytku z mojego wpisu jak z jego.

Każda przeglądarka internetowa ma zbiór podstawowych reguł CSS, które ustawiają właściwości tagów HTML. Dzięki temu możemy stworzyć dokument HTML bez żadnych reguł CSS a mimo to będzie odpowiednio wyglądał (tag strong będzie pogrubiał, h1 będzie ustawiał odpowiednio duży nagłówek itd.). To nie jest efekt jakichś magicznych reguł umieszczonych wprost w kodzie źródłowym przeglądarki a właśnie rezultat zastosowania CSS.

Wszystko byłoby pięknie gdyby twórcy przeglądarek umieli się dogadać ale jak wszyscy wiemy nie potrafią nawet stosować się do przyjętych standardów więc jak tu wymagać aby stosowali takie same zasady w mniej formalnych aspektach. Z tego powodu mamy totalny bajzel. Wystarczy spojrzeć na te dwie strony (tu i tu - adresy też znaleziony u Riddle :) ) aby przekonać się jak duże różnice są między przeglądarkami a nawet ich poszczególnymi wersjami.

Dążąc do perfekcji (aby nasze strony wyglądały identycznie w każdej przeglądarce) webmasterzy postanowili stworzyć zbiór reguł CSS, które zresetują wszystkie zasady CSS ustawione przez przeglądarki. W ten sposób zaczniemy budować każdą stronę z "czystym kontem". Wystarczy jednak wpisać w Google "CSS reset" aby się przekonać, że pomysłów na rozwiązanie problemu jest wiele. Który jest najlepszy?

Rozwiązaniem może być to co wczoraj zaproponował Riddle. Wygląda to naprawdę zgrabnie, jest dokładnie opisane, przygotowuje nas już do pracy z HTML5. Polecam.

Pamiętaj jednak, że nim zaczniesz stosować te reguły to dobrze byłoby je zrozumieć. Znam przykłady gdzie koder zastosował jakieś reguły mimo, że ich nie potrzebował i potem przy każdym wystąpieniu np. odnośnika (tu zwróć uwagę na to co napisał Riddle o text-decoration) je "odkręcał". Z drugiej strony warto rozszerzać te reguły o swoje własne jeżeli upraszczają pracę z Twoim projektem. Nie chodzi przecież o to aby mieć idealne reguły resetujące ustawienia przeglądarek a o to aby posiadać reguły maksymalnie ułatwiające nam budowanie własnej strony.

EDIT 02.02.2011
Eric Meyer opublikował drugą wersję swoich resetujących styli, link tutaj.


Riddle wrócił

08 maja 2010

Bycie informatykiem to trochę jak bycie lekarzem. Właściwie każda specjalizacja (programowanie, tworzenie www, sprzęt itd.) wymaga od Ciebie ciągłej nauki i samodoskonalenia. Na szczęście są osoby, które chętnie dzielą się swoją wiedzą i/lub znajdują dla nas ciekawe materiały w sieci. Nie ma ich zbyt wielu a pod koniec 2007 roku ubyło jednego. Ku zadowoleniu webdeveloperów (także mojemu) wrócił... :)

Piotr Petrus szerzej znany pod pseudonimem Riddle to, moim skromnym zdaniem, naprawdę utalentowany młody chłopak o bardzo bogatej wiedzy. Na swoim blogu opisywał ciekawe aspekty tworzenia www, podpowiadał wyjątkowo funkcjonalne rozwiązania. Ponad dwa lata temu zniknął by w marcu tego roku wrócić ponownie (o czym dowiedziałem się dopiero dzisiaj).

Jeżeli nie miałeś przyjemności trafić wcześniej na artykuły Piotrka to zapraszam pod ten adres. Mimo upływu czasu na pewno znajdziesz tam coś ciekawego. Tymczasem pod starym adresem jego bloga czyli www.perfectionorvanity.com jest już nowa odsłona witryny gdzie autor porusza wyjątkowo ciekawe i aktualne aspekty tworzenia aplikacji na potrzeby globalnej sieci. Czas najwyższy zacząć poważnie szkolić się w nowych technologiach takich jak HTML5 czy CSS3 a także poznać interesujące rozwiązania w JavaScript. Jestem pewien, że nowa inicjatywa Riddle mnie nie zawiedzie :) Obyś i Ty stał się stałym gościem jego strony. Warto.

PS
Należy wspomnieć o znanym projekcie Riddle czyli Em Calculator. Szalenie przydatny skrypcik gdy budujemy swoją stronę na em'ach zamiast pikselach (do czego zachęcam). Pierwsza wersja kalkulatora została udostępniona w maju 2006, druga w lutym 2007 ale przez lata projekt niczego nie stracił na funkcjonalności. Gdzieś po sieci od dłuższego czasu krążą zapowiedzi Piotra o wersji trzeciej. Nawet screeny są, filmik. Nic tylko czekać :)

EDIT
Riddle nam się jakiś grymaśny zrobił :) Teraz można go czytać pod tym adresem: perfectionorvanity.tumblr.com. Starej domeny nie przedłużył.


Gotowe rozwiązania w PHP – samo dobro?

06 maja 2010

Za napisanie czegoś w tym temacie zabierałem się dobre kilka lat :) Uznawałem do tej pory, że lepiej nie wdawać się w długie dyskusje tylko dalej robić swoje i po swojemu. Jednak coraz częściej spotykane, wręcz euforyczne komentarze na temat różnych gotowych rozwiązań w PHP skłaniają mnie do dodania łyżki dziegciu do tej beczki miodu...

Bez dwóch zdań, PHP to lider w tematyce tworzenia stron www. Za popularnością idzie również wzrost aplikacji open source, których w świecie PHP jest zatrzęsienie. Gdyby tak na nie wszystkie spojrzeć to można byłoby dojść do wniosku, że tworzenie stron www stało się bajecznie proste i bardzo szybkie, przecież dla każdego elementu strony mamy setkę różnych darmowych rozwiązań. Tylko czerpać garściami...

Szybko się jednak okazuje, że nie ma tak różowo. Bez względu na to czy mówimy o rozbudowanych systemach CMS czy o frameworkach to zawsze powtarzają się te same główne problemy:

  • Wydajność
  • Zbyt dużo elementów, których nie potrzebujemy a które są uruchamiane...
  • ... natomiast nie ma tego co dla nas jest najważniejsze lub jest ale nie spełnia naszych wszystkich oczekiwań
  • Obciążające mechanizmy, które mają za zadanie dostosować aplikację do wszystkich popularnych serwerów, rozwiązań itd. a Tobie nigdy się nie przydadzą
  • Musisz się uczyć nowego oprogramowania, czasami jest tyle zasad, komend i funkcji do zapamiętania, że przybiera to rozmiary małego języka programowania

Oczywiście lista wad zależy od naszych potrzeb i oczekiwań. Przy malutkim projekcie mało kto zwraca uwagę na wydajność. Przy typowym projekcie bez żadnych szczególnych wymagań zadowolimy się gotowymi modułami, które są dostarczone razem z CMS'em. I tak dalej... Z drugiej strony czy potrzebujemy wytaczać tak ogromne działa jak np. Drupal do strony jakiejś firmy gdzie będzie maksymalnie 10 podstron? I jak często projekty, które tworzysz są tak typowe, że zadowalasz się gotowymi modułami do CMS'a i nic już w nich nie musisz zmieniać?

Nie byłbym obiektywny gdybym widział w gotowych rozwiązaniach tylko same wady. Podstawowe zalety to:

  • Dobra (przeważnie) dokumentacja
  • Duża społeczność gdzie wielu służy Ci pomocą gdy masz problem i jednocześnie możesz szukać pracownika/współpracownika, który nie zgubi się w Twoich projektach bo korzysta z tego samego oprogramowania
  • Sporo gotowych rozwiązań, których można używać lub się na nich uczyć
  • Bezpieczeństwo
  • Łatwość programowania

Jak widać, gotowe rozwiązania mają się czym bronić.

Dokumentacja, jaka by nie była to i tak jest lepsza od tej, którą Ty masz na starcie w swoim autorskim rozwiązaniu (a nie masz żadnej póki sobie jej sam nie stworzysz).

Społeczność i spore grono już "wyszkolonych" pracowników to też zaleta. Chociaż z tymi specjalistami to też nie jest tak różowo bo najczęściej brakuje takich, którzy już umieją dobrze sobie radzić z danym narzędziem i trzeba ich i tak douczać. A nawet jak są to się cenią...

Gotowe moduły to fajna rzecz. Niestety i tutaj jest się do czego przyczepić. Często są źle zaprojektowane, niechlujnie napisane, powodują zwiększone obciążenie, mają w sobie furtki dla włamywaczy a na ich podstawie możemy się jedynie nauczyć jak NIE programować. Nie będę jednak tak krytyczny, zdarzają się prawdziwe perełki a duża część trzyma poziom.

Bezpieczeństwo: z jednej strony błędy są szybko wychwytywane przez społeczność i developerów, z drugiej główny kod Twojej strony, tzw. "silnik" jest dostępny dla każdego i każdy teoretycznie może tam wyszukać lukę lub skorzystać z gotowej instrukcji jeżeli na czas nie załatałeś strony oficjalną łatką. Jednocześnie jednak CMS'y i frameworki najczęściej mają mechanizmy, które pilnują programistę aby nie potworzył furtek dla włamywaczy a w Twoim własnym oprogramowaniu sam się musisz pilnować lub pisać samemu mechanizm zabezpieczeń. Jeszcze inaczej podchodząc do tematu, w Twojej aplikacji może być luka ale jest niewielkie prawdopodobieństwo, że ktokolwiek na nią trafi i ją wykorzysta bo nikt nie ma Twoich kodów źródłowych :) Chociaż to nie jest oczywiście najlepsze z możliwych podejście do bezpieczeństwa...

Z łatwością programowania to już jest bardzo różnie... Takie są ogólne zasady powstawania gotowych narzędzi: ułatwiać życie programiście. Nie zawsze jednak cel jest osiągany. Nie da się jednak zaprzeczyć, że wraz z gotowymi rozwiązaniami dostajemy często moduły np. do łatwego testowania i debugowania naszych aplikacji. We własnych projektach jest z tym dużo gorzej.

Czas na kilka przykładów z życia. Pierwszy raz na blogu odniosę się do mojej ponad rocznej pracy z systemem Drupal. Nie byłem z tego wyboru jakoś wielce zadowolony chociaż rozumiałem argumenty dlaczego go wybrano w firmie. Nasz największy projekt od dnia startu do pierwszej wersji beta pochłonął kilka miesięcy pracy dwóch programistów. Dodajmy uczciwie, że cały czas uczyliśmy się tego CMS'a. Efektem była całkiem sympatyczna aplikacja i kilkadziesiąt naszych autorskich modułów. Nie wszystko jednak wyglądało tak jak powinno... To co zaliczyłbym do wad:

  • Miesiące nauki a i tak co chwila napotykaliśmy na problemy (a chyba tacy głupi nie jesteśmy... :) )
  • Niektóre rzeczy napisalibyśmy w godzinę ale głowiliśmy się kilka dni jak to ugryźć w Drupalu przez narzucone przez niego ograniczenia
  • No właśnie, ciągle ograniczenia i ograniczenia. Jasne zasady i standardy są dobre ale nie można przesadzać...
  • Wydajność... a raczej jej brak
  • Co z tego, że jest dużo gotowych rozwiązań skoro i tak wiele z nich musieliśmy poprawiać i dostosowywać do swoich potrzeb

Odniosę się tylko do wydajności bo cała reszta jest raczej jasna (a w szczegóły wchodzić nie ma sensu bo to nie wpis o Drupalu). Nie wiem dokładnie jak obciążony był serwer naszą aplikacją ale z pewnością bardzo i od początku były z tym problemy. Gdy pierwszy raz sprawdziliśmy ile zapytań do bazy danych generuje strona główna to mało nie spadłem z krzesła. Ponad tysiąc dwieście! Specjalnie napisałem słownie żeby nie było wątpliwości, że się nie pomyliłem :) Po optymalizacjach wyszło ponad 700. W Drupalu były włączone standardowe mechanizmy cachowania itp. Nie wiem czy i jak sobie z tym poradzili, wkrótce potem zająłem się innymi sprawami. Te liczby są porażające... Odnosząc się do moich autorskich rozwiązań strona główna nasztomaszow.pl generuje dziennie kilkadziesiąt zapytań do bazy. Dziennie! Kilkadziesiąt! I to bez względu na to jak duży ruch jest na stronie z racji sprytnego systemu cachowania. Poza nawiasem dodam, że standardowy system cachowania w Drupalu jest oparty o... bazę danych. Nie wiem czemu tak, może ja się nie znam i tak jest najwydajniej :)

Po tamtych doświadczeniach wiedziałem już, że mój następny projekt oprę o własne rozwiązania. Tak się składa, że NaszTomaszow.pl (a o nim właśnie mowa) można śmiało porównywać z opisywaną aplikacją w Drupalu. Obie mniej więcej odpowiadają za to samo - za prowadzenie/obsługę portali miejskich. O liczbie zapytań do MySQL strony głównej już pisałem. Niestety nie pamiętam już jakie wyniki generował XDebug dla witryny w Drupalu natomiast praktycznie goła instalacja tego CMS'a generuje się u mnie w średnio 700ms. Jak na tym tle wygląda mój system? Stronę główną NaszTomaszow.pl parser wypluwa w około 45ms. Czystą instalację w 15ms.

Zrobiłem inne testy*. Sprawdziłem jak szybko generują się strony we frameworku Yii (podobno bardzo wydajny), przetestowałem system forum IPB i CakePHP. Wiem, że to nie jest jakaś super reprezentatywna grupa ale są to raczej popularne skrypty a ja zrobiłem testy tylko z ciekawości a nie aby badać cały rynek ;) Oto wszystkie wyniki:

  • Yii (przykład blogu z oficjalnej paczki) - średnio 650ms
  • forum IPB (konfiguracja z NaszTomaszow.pl, forum puste) - średnio 420ms
  • CakePHP (czysta instalacja, tuż po wypakowaniu z oficjalnej paczki) - średnio 790ms
  • Drupal (malutka, prosta stronka, baza praktycznie pusta) - średnio 700ms
  • Mój system (na podstawie aktualnej wersji plików i bazy NaszTomaszow.pl) - 45ms
  • Mój system (czysta instalacja) - 15ms

Czy więc odradzam korzystanie z gotowców? Nie, zdecydowanie nie. Sam przecież czasami stawiam coś na Drupalu a ten blog oparty jest o WordPress'a. Warto poznać popularne rozwiązania open source, chociażby po to żeby móc mówić o ich wadach. Przy pracy z nimi zdobędziesz też wiele doświadczenia i odkryjesz ciekawe rozwiązania wielu problemów. Czasami ich wybór jest jedynym słusznym rozwiązaniem.

Ten artykuł jest bardzo lakoniczny, może nawet ktoś uzna, że amatorski. Nie chciałem porównywać konkretnych systemów, głosić jedynej słusznej teorii i nawracać niewiernych. Chciałem przypomnieć Ci, że nic nie zastąpi zdrowego rozsądku. Pogódź się z tym, że nie ma idealnych narzędzi. Zawsze to Ty musisz się wykazać znajomością problematyki i przewidzieć na jakie problemy możesz się natknąć oraz wykazać jakie rozwiązania będą efektywniejsze i spełnią Twoje założenia. Czasami potrzebna jest ekstremalna wydajność, czasami najważniejszy jest czas oddania projektu, innym razem masz wieloosobowy a może nawet międzynarodowy zespół. Czynników, które powinny wpływać na Twoją decyzję co do wyboru oprogramowania są dziesiątki.

Przypominają mi się w tej chwili stare wojny np. programowanie strukturalne versus obiektowe. Zawsze to co nowe podbijało szybko serca programistów, przychodził jednak czas opamiętania i rozsądek podpowiadał wtedy starą prawdę: stosuj wedle zapotrzebowania.

* Wszystkie testy były banalnie proste. Uruchomiony XDebug, wyniki odczytywane za pomocą WinCacheGrind, kilkanaście prób i wyciągnięta średnia.

EDIT 14.06.2010
Postanowiłem dzisiaj sprawdzić jak wydajnościowo mają się dwa frameworki, którymi jestem szczególnie zainteresowany. Oto uzupełnienie powyższej listy:

  • Symfony (przykładowa paczka Sandbox) - średnio 680ms
  • Zend Framework (oficjalne aplikacje demo) - średnio 365ms

Takiego właśnie wyniku Symfony się spodziewałem. Słyszałem narzekania na jego wydajność. Paczka Sandbox to, o ile dobrze przeczytałem, wstępnie skonfigurowane Symfony. Na tle Drupala, który po instalacji ma już wiele gotowych elementów do zaoferowania nie wygląda to imponująco (chociaż po ponad rocznej pracy z Drupalem i pięciu minutach w dokumentacji Symfony chyba w tym drugim, jako programista, czułbym się lepiej ;) ).

Testując Zend Framework skorzystałem z gotowych aplikacji demo, które znalazłem w paczce. Zdarzały się dużo lepsze czasy niż powyżej zaprezentowana średnia (poniżej 200ms a nawet poniżej 50ms) ale powiedzmy sobie szczerze, ile może trwać wyświetlenie jednego formularza? :) Dlatego wybrałem dwie czy trzy trochę bardziej wymagające wersje demo i na nich oparłem wyniki testu. Ciekaw jestem jak wyglądałyby wyniki np. przy jakimś prostym systemie newsów.