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


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 dwa 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.


Autoryzacja gdy PHP jest zainstalowane jako CGI w Apache

06 maja 2010

Czas najwyższy na jakiś wpis z konkretnymi poradami bo ostatnio jest tutaj z tym kiepsko... :)

Jak wiemy (albo i nie :P ) autoryzacja w PHP działa tylko wtedy gdy PHP jest zainstalowane jako moduł Apache (o innych serwerach dzisiaj pisać nie będę). Tak przynajmniej oficjalnie głosi dokumentacja. Jest jednak światełko w tunelu dla tych, którzy mają hosting z PHP postawionym jako CGI.

Poniżej opisane jest jedno z rozwiązań. Nie ma pewności, że będzie u Ciebie działało, zależy to od możliwości/ustawień Twojego konta hostingowego. U mnie działa ;) W komentarzach do wyżej podlinkowanej dokumentacji znajdziesz inne rozwiązania więc próbuj do skutku.

Pierwszy wymagany krok to ustawienie w pliku .htaccess odpowiedniej regułki. Wygląda ona tak:

CODE:
  1. RewriteEngine on
  2. RewriteRule \.php$ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]

W ten sposób ustawiamy w PHP specjalną wartość w tablicy $_SERVER z kluczem HTTP_AUTHORIZATION i danymi autoryzacyjnymi. Teraz wystarczy w PHP odczytać to co zaserwuje nam Apache takim kodem:

PHP:
  1. if(isset($_SERVER['HTTP_AUTHORIZATION']) AND $_SERVER['HTTP_AUTHORIZATION'] != '') {
  2.   list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
  3. }

Przemyśl dokładnie gdzie to wstawić w swój kod. Dzięki takiej sztuczce PHP dostanie dwie wartości, Twój login i hasło, tak jakbyś korzystał z modułu Apache.

Gdyby powyższe nie działało to można spróbować jeszcze takiego kodu w .htaccess (zamiennie do powyższego):

CODE:
  1. SetEnvIf Authorization "^(Basic .+)$" HTTP_AUTHORIZATION=$1

U siebie akurat stosuje ten drugi kod bo to było pierwsze rozwiązanie na które natrafiłem. Jednak na większości serwerów bardziej dostępny jest mod_rewrite niż mod_setenvif dlatego wybrałem taką kolejność podawanych rozwiązań.


Podstawy typów danych w PHP

04 marca 2010

PHP ma bardzo liberalne podejście do typów danych jakich możemy używać. Dzięki dynamicznemu rzutowaniu typów nie musimy się bawić w żadne deklaracje typu tuż przy inicjacji zmiennej. Zmieni się on automatycznie gdy przypiszemy do tej zmiennej jakąś wartość. W jednej chwili zmienna może być typu string aby po chwili ta sama zmienna zwracała jako typ integer.

Oczywiście są zwolennicy i przeciwnicy takiego rozwiązania. Na pewno dla niektórych brak konieczności deklarowania i zapamiętywania typu każdej zmiennej to duży plus. Z drugiej strony robi nam się lekki bałagan (programiści potrafią zrobić niezły bajzel gdy nie są dyscyplinowani przez język w którym piszą), łatwiej o pomyłkę i powstaje wiele pytań kiedy tak naprawdę zmienna jest pusta, kiedy zwróci FALSE itd.

Dwóch pierwszych problemów nie rozwiążemy, co do trzeciego to przyda nam się ściągawka. Taka jak na stronie www.blueshoes.org/en/developer/php_cheat_sheet/. Znajdują się tam ładne i wygodne w użytkowaniu tabelki, które obrazują różnice między zmiennymi z przypisanymi różnymi wartościami, jak na te wartości reagują wbudowane funkcje PHP oraz który warunek if będzie prawdziwy przy użyciu == a który przy użyciu ===.

Polecam się z tym zapoznać, może to Was uchronić przed wieloma błędami logicznymi w Waszych skryptach. A jak wiemy, takie są często bardzo trudne do wykrycia...

Przy okazji wyrażę swoje zdanie na temat tego, że PHP automatycznie zmienia typ danych.

Przyznam, że mam co do tego mieszane uczucia. W swoich skryptach staram się unikać sytuacji gdy zmienna bez jakiegoś szczególnego powodu zmienia swój typ. Co więcej, możliwość takiej automatycznej zmiany sprawia, że czasami mam tylko więcej pracy przy zabezpieczaniu swoich stron.

Z drugiej strony konieczność deklaracji typu także nastręczałaby problemów. Zawsze musielibyśmy pamiętać o typie i asekuracyjnie sprawdzać czy jest prawidłowy przy przypisywaniu wartości do zmiennej bo inaczej wygenerujemy błąd.

Jeżeli miałbym decydować o najbliższej przyszłości języka PHP to chciałbym aby wprowadzono możliwość deklarowania typu. Nie konieczność a możliwość. Tak abym mógł przy inicjacji zmiennej zadeklarować jej typ i zmienić go tylko za pomocą dostępnych funkcji rzutowania. Zmienne bez deklaracji typu zachowywałyby się tak jak dotychczas. Czekam aż ktoś się przyczepi do tego pomysłu :P


HipHop for PHP

04 lutego 2010

Nie mam w zwyczaju pisać na blogu o nowościach. Jest wystarczająco dużo stron www, które dostarczają programistom itp. codzienną dawkę informacji i silenie się przez niektórych blogerów aby pisać o wszystkich niuansach (co przeważnie sprowadza się do napisania własnymi słowami tego co przed chwilą przeczytało się na dużym portalu) jest żałosne. Tym razem jednak złamię swoją zasadę bo wydarzyło się coś naprawdę interesującego :)

Język PHP, mimo iż niektórzy starają się usilnie tego nie zauważać, ma sporo zalet. Najwidoczniej nie tylko ja tak uważam skoro największy portal na świecie, Facebook, jest wykonany właśnie w tej technologii. Jednocześnie nie da się ukryć niektórych wad PHP, które przy ruchu 400mld odsłon miesięcznie są z pewnością szczególnie uciążliwe. Ludzie z Facebook postanowili coś z tym zrobić i tak powstała idea HipHop for PHP.

Jak wszyscy dobrze wiemy, instrukcje PHP są interpretowane od nowa za każdym razem gdy wywołamy jakiś skrypt PHP. To niestety musi powodować zwiększone obciążenie i wolniejsze działanie od języków, które kompilujemy. HipHop for PHP ma na celu "przeczytać" kod źródłowy PHP a następnie zamienić go na wysoko wydajny kod C++, który potem możemy sobie z łatwością skompilować. Taki stwór ma nam znacząco zwiększyć wydajność naszych aplikacji.

Przyznam, że mam mieszane uczucia co do tej wiadomości. Niby jest to jakiś rozwój PHP ale nie cieszy mnie fakt, że trzeba uciekać się do takich sztuczek aby znacząco zwiększyć wydajność kodu. W ten sposób szybko dojdziemy do wniosku, że w takim razie wypadałoby pisać strony w C++ i jedynie prostota PHP nas przed tym powstrzymuje.

Należy również wspomnieć, że nie zostały upublicznione żadne informacje o metodach przeprowadzonych testów. Jedyna informacja jaką wyczytałem to około 50% mniejsze obciążenie procesorów. Ciekaw jestem jak na tej samej maszynie wypadłby Zend Optimizer i tym podobne aplikacje.

Szkoda, że ludzie z Facebook nie dogadali się z Zend i nie powstało jakieś oficjalne rozwiązanie bo może się okazać, że HipHop for PHP będzie użyteczny tylko dla garstki osób. Mam nadzieję, że twórcy PHP wyciągną pewne wnioski z tego wydarzenia. PHP 5 wniosło wiele dobrego ale od tamtego czasu rozwój tak jakby zwolnił a PHP 6 nie zapowiada się na żadną rewolucję a ponadto nie słychać nic o premierze chociaż prace zaczęły się podobno już w roku 2005.

Miło byłoby aby wreszcie pozbierać ten język do kupy. Olać kompatybilność wsteczną, zrobić porządek z nazewnictwem a być może nawet całkowicie przejść na obiektowość. Nie cieszy również mnogość niektórych rozwiązań co wprowadza tylko zamęt. Osobiście nie jestem również do końca zadowolony z postaci czy rozwoju takich narzędzi jak PEAR czy Zend Framework (chociaż ZF podobno staje się coraz lepsze ale jakoś ostatnio nie śledzę tego projektu). Oby ktoś nad tym wszystkim nareszcie zapanował bo PHP to naprawdę przyjemny język.

Mam nadzieję, że mój sceptycyzm okaże się nieuzasadniony :) Wkrótce HipHop for PHP zostanie upublicznione, wraz z kodem źródłowym. Właściwie to już powinno być dostępne ale jakoś się nie doszukałem :)

PS
Zdecydowanie polecam przeczytać oryginalny news zapowiadający HipHop for PHP na Facebook.

PS2
Jeszcze ciekawszy tekst z którego wyczytamy sporo o technicznych aspektach uruchomienia i działania HipHop for PHP.


Pokochać JavaScript

25 stycznia 2010

Stało się i nie ma już odwrotu. Trzeba nauczyć się JavaScript. Nie, nie mówię o sobie bo ja już go znam (na jakimś tam akceptowalnym poziomie :) ). Mówię do Ciebie.

Nie pamiętam kiedy pierwszy raz zetknąłem się z JavaScript ale wiem na co wtedy była moda. Implementacja tego języka w wielu przeglądarkach kulała a jeżeli nawet coś bardziej złożonego działało to w każdej przeglądarce trzeba było to napisać trochę inaczej. Z tego powodu nie było wielu zaawansowanych koderów JS a w sieci krążyły różne skrypciki, które webmasterzy dzisiaj zgodnie określają mianem wodotrysków. Implementacja zegarka albo kalkulatora, "padający śnieg" czy animowany tekst podążający za kursorem myszki - oto na co było stać większość programistów JS i czym zachwycali się twórcy stron www.

Wreszcie przyszło jednak opamiętanie i wszystkie straszydła zaczęły znikać z sieci. Bo na cholerę komuś zegarek na stronie skoro każdy ma go w prawym dolnym rogu... ?

Nadeszły gorsze czasy dla JS... co wyszło temu językowi tylko na dobre :) Na całe szczęście język nadal się rozwijał a producenci przeglądarek nie zapomnieli o nim i pomału programowanie ze znośnego stawało się przyjemne. Co mądrzejsi zaczęli się zastanawiać jak wykorzystać potencjał, niemały potencjał należy dodać, tego języka. Po cichu zaczęły się pojawiać jakieś bardziej ambitne próby stworzenia czegoś praktycznego w JavaScript.

Może i Google Maps nie przyniosłoby ze sobą otrzeźwienia (mimo zaawansowanego użycia JS) gdyby nie to, że wykorzystano obiekt XMLHttpRequest (wymyślony przez Microsoft) dzięki któremu od tamtej chwili webmasterzy na całym świecie mogą się cieszyć technologią znaną pod nazwą AJAX.

I zaczęło się :) Wchodzisz sobie na stronę, klikasz, coś się dzieje, ewidentnie pobierane są nowe informacje z serwera a strona się nie przeładowała... To była rewolucja, technika ta szybko stała się pragnieniem każdego właściciela www. JavaScript nareszcie został doceniony, to były jego drugie narodziny. Szczęśliwie zbiegło się to w czasie z już całkiem przyzwoitymi implementacjami JS w przeglądarkach.

Dalej opowiadać chyba już nie trzeba, gdyby nie powyżej opisane wydarzenia to Internet z pewnością wyglądałby dzisiaj i działał zdecydowanie gorzej. Właściwie to nawet może nie mielibyśmy dzisiaj Web 2.0? Kto wie, w końcu JS i AJAX miały w tym trendzie bardzo duży udział a nowe możliwości były inspiracją dla pierwszych pionierów.

Dzisiaj programowanie w JS to zupełnie inna bajka niż wtedy gdy pierwszy raz się nim zainteresowałem. Twórcy przeglądarek prześcigają się w implementacji ekstremalnie szybkich silników JavaScript, mamy do wyboru wiele frameworków i bibliotek ułatwiających życie webmasterom a Google jasno daje do zrozumienia swoimi rozwiązaniami (Gmail, Google Maps, Google Docs etc. oraz cała idea Chromium OS), że bez JS już się nie da...

Zachęcam wszystkich do poznawania możliwości JS. Zapewniam Cię, że jeżeli wiążesz swoją przyszłość z tworzeniem stron www to nie masz wyjścia ;)

Na nasze szczęście istnieją takie projekty jak jQuery, dzięki któremu programowanie w JS to poezja. Wybór frameworka należy do Ciebie ale podpowiem Ci, że ja już nie zabieram się za żaden projekt bez jQuery :) Oczywiście czasami coś prostego lepiej zaprogramować w czystym JS niż ładować dużą bibliotekę tylko po to aby obsłużyć jeden przycisk. Pamiętaj o tym.

Jeżeli na początku Twojej przygody brakuje Ci wielu rozwiązań z PHP to polecam zainteresować się projektem php.js. Nie radzę dodawać tej biblioteki do swoich stron (chociażby z powodu jej rozmiarów) ale łatwiej będzie Ci się czegoś nauczyć gdy zobaczysz jak zaimplementowane są w JS funkcje z dobrze znanego Ci języka.

Jest też coś dla programistów Java. Napisz wszystko w Javie i użyj Google Web Toolkit a kompilator sam zbuduje całą aplikację łącznie ze skryptami JS. Chociaż nie wszyscy są zdania, że to dobre rozwiązanie...

Podsumowując, chociażbyś bardzo chciał to JavaScript nie unikniesz. Ale nie ma ku temu powodów, czasy się zmieniły zdecydowanie na korzyść tego języka. Zacznij go odkrywać a zobaczysz ile nowych możliwości otworzy się przed Tobą i jak łatwo uzyskać niektóre efekty (zwłaszcza z pomocą narzędzi typu jQuery :) ).


Pamiętaj o Ctype Functions

28 października 2009

Kolejna drobna wskazówka dla programistów PHP. W kursach o tym nie przeczytacie :P

Bywa tak, nawet całkiem często, że musimy się upewnić, że w zmiennej mamy np. tylko same cyfry lub małe litery. Pierwsze co wszystkim przychodzi do głowy to wyrażenia regularne. Ale chyba każdy z nas słyszał nie raz przestrogę, że jak można to lepiej unikać wyrażeń bo są wolne. Czy jest jakaś alternatywa?

A jest :P Nazywa się to Character type checking. Funkcji do wyboru jest kilkanaście, przykładowo (za php.net):

PHP:
  1. <?php
  2. $strings = array('1820.20', '10002', 'wsl!12');
  3. foreach ($strings as $testcase) {
  4.     if (ctype_digit($testcase)) {
  5.         echo "The string $testcase consists of all digits.\n";
  6.     } else {
  7.         echo "The string $testcase does not consist of all digits.\n";
  8.     }
  9. }
  10. ?>

Dostaniemy:

CODE:
  1. The string 1820.20 does not consist of all digits.
  2. The string 10002 consists of all digits.
  3. The string wsl!12 does not consist of all digits.

Lista wszystkich dostępnych funkcji znajduje się tutaj, polecam się z nimi zapoznać. Testy wykazują, że funkcje wykonują się znacznie szybciej niż wyrażenia regularne. Funkcje ctype_* są wbudowane w PHP, nie trzeba się martwić o ich dostępność.


Jak NIE należy pisać kodu

27 października 2009

Właśnie pracuję nad jednym małym zleceniem. Problem w tym, że jestem podwykonawcą, odpowiedzialnym tylko za jeden element, całą resztę zrobił już inny programista. Muszę teraz edytować jego kod i dostosowywać do nowych potrzeb.

No to zaczynamy. Otwieram pliki. No super... Zamykam pliki i przełączam edytor na ISO :) Mogliby się ludzie już przestawić na UTF.

Otwieram pliki :) Plik konfiguracyjny z kilkudziesięcioma definicjami stałych, dla każdej tabeli w bazie jedna definicja. Nie podoba mi się to ale niech już tak będzie.

Szukam plików odpowiedzialnych za element którym mam się zająć. Na ftp trochę bajzel, można było to chyba trochę lepiej pogrupować ale nie jest tragicznie.

Jest coś dotyczącego zlecenia, otwieram. Ech, wszystko wpakowane w funkcje. Przykład:

PHP:
  1. function BodyLogowanie_START()
  2. {
  3.   print'
  4.  <TABLE width="100%" height="100%" border="0" cellspacing="0" cellpadding="0" style="margin-bottom:15px;">
  5.    <TR>
  6.    <TD align="center" valign="middle">';
  7. }
  8. function BodyLogowanie_END()
  9. {
  10.    </TD>
  11.  </TR>
  12.  </TABLE>';
  13. }

Funkcje fajna rzecz ale ich używanie obwarowane jest pewnymi zasadami. Każdy lepszy kurs i każda książka o tym mówi. Założę się, że są tutaj funkcje, które używane są tylko w jednym miejscu przez co funkcjami nie powinny być. Olać, idę dalej.

Przeglądam pliki, cały HTML oparty na tabelkach. Oj dostałoby się koledze za to na publicznych forach :) Olać, HTML w tym zleceniu to już zupełnie nie moja bajka.

O, jest jakiś kawałek o logowaniu. Od razu w oczy rzuca się ten kod (fragment zapytania do MySQL):

PHP:
  1. login='".$_POST['email']."'

To już nie jest takie śmieszne, ktoś się włamie a potem powiedzą, że moja wina. Trzeba będzie poprawić...

Identyfikator połączenia z bazą danych trzymany w sesji? Przyznam, że się nie spotkałem :)

Przyzwyczaiłem się już do angielskiego nazewnictwa plików, zmiennych itd. dlatego rażą mnie w oczy polskie słowa. Ale tu już się czepiać nie będę, wielu programistów tak robi, sam kiedyś tak pisałem. Musiałem się potem przestawić jak zacząłem pisać kod w wielonarodowych zespołach.

Pisałem już, że kod PHP jest całkowicie wymieszany z HTML'em? :) Dodajmy do tego, że wszystko jest pozamykane w dziesiątkach funkcji i mamy niezły mętlik. Nie lubię programować gdy nad kodem muszę się zastanawiać jak nad wierszem: "Co autor miał na myśli?".

Jak już jesteśmy przy domyślaniu się to wspomnę, że jest zero komentarzy w kodzie.

Dobra, czas zacząć pracować. Znalazłem to co mnie interesuje. I od razu szok:

PHP:
  1. $zapytanie="SELECT login,email FROM `"._CONF_table_users."`;";
  2. $wykonaj=$Connect->MysqlQueryRet($zapytanie);
  3. if($Connect->GetValue('error_mysql')==False){
  4.   $i=0;
  5.   while($wiersz=@mysql_fetch_array($wykonaj)){
  6.     if($wiersz['email']==$_POST['email']||$wiersz['login']==$_POST['login'])$i++;
  7.   }
  8.   if($i>0)$ret=$RETURN_KONTO[1][0];else $ret=$RETURN_KONTO[0][0];
  9. }

Jak z PHP mam do czynienia już 6 czy 7 rok tak jeszcze takiego cuda nie widziałem. Czy ten programista nigdy nie słyszał o warunkach WHERE w zapytaniach SQL? Wyciągać całą zawartość tabeli tylko po to żeby sprawdzić czy czasem nam się konto nie zdubluje? :/ Paranoja... Nie chce myśleć co się będzie działo z serwerem gdy na stronie zarejestruje się więcej niż kilka tysięcy ludzi.

Po powyższym wiedziałem już, że łatwo z tym zleceniem nie będzie ;) Na tym zakończę tą "recenzję" bo chociaż są jeszcze inne błędy, niektóre naprawdę poważne, to recenzowanie nie jest celem tego wpisu. Teraz kilka pytań bez odpowiedzi.

  • Czy powinienem zwrócić programiście uwagę na jego błędy?
  • Czy powinienem powiadomić właściciela projektu o kwiatkach jakie zgotował im ich programista?
  • Czy powinienem poprawić chociaż te błędy, które jako tako dotyczą mojego zlecenia czy pojechać szablonem, który wyznaczył główny programista i mieć zlecenie z głowy?
  • Czy powinienem o tym pisać na blogu? :D

Odpowiedzi na pytania wcale nie są proste. Albo zacznę interweniować i wszyscy mnie znienawidzą a sam będę miał tylko więcej nerwów i pracy albo nie odezwę się ani słowem i będę miał spokój ale strona będzie przez następne trzy lata łatana, pewnie przez tego samego programistę.

Z powyższego wpisu można wyciągnąć kilka wniosków.

  • Po pierwsze, trzeba naprawdę dużo praktyki żeby zacząć dobrze programować.
  • Po drugie, nie należy oszczędzać na programistach :P Nie wiem ile temu chłopakowi płacą ale kod jest słaby a projekt ciągnie się już pół roku i pewnie jeszcze trochę potrwa nim strona pojawi się w sieci. Lepszy programista zainkasowałby odpowiednio dużo kasy ale zrobiłby to porządnie a oni już by zarabiali.
  • Po trzecie, na rynku chyba nie ma wielu dobrych programistów... Dostaję na to coraz więcej dowodów.

Zachęcam do dyskusji w komentarzach. Co o tym wszystkim myślicie? Co doradzacie?


PHP Filter Functions

30 września 2009

Coś ostatnio nie mam czasu na pisanie na blogu, na nic nie mam czasu... A pomysłów na tematy wiele...

Dzisiaj jest jednak okazja ;) Uruchomiłem ogłoszenia na NaszTomaszow.pl nad którymi ostatnio pracowałem a przy okazji natrafiłem na ciekawy element PHP o którym trzeba napisać kilka słów.

Już trochę czasu minęło od premiery PHP 5.3 tymczasem jakimś sposobem nie zauważyłem bardzo ciekawego rozwiązania, które wprowadzono w PHP 5.2.

Często w procesie budowania stron www spotykamy się z problemem poprawnej walidacji adresu e-mail, adresu www czy IP. Użytkownik nam się rejestruje i my musimy być pewni, że ten e-mail to naprawdę e-mail a nie coś w stylu "pocałujcie mnie w dupę" :)

Jak sprawdzić poprawność danego ciągu znaków? Oczywiście kłaniają się, tak przeze mnie ostatnio lubiane, wyrażenia regularne. Przeważnie mamy kilka opcji do wyboru:

      Napisać własne (jak żeś głupi to się męcz :P )
      Poszukać czegoś w google
      Poszukać czegoś w swoich poprzednich projektach

Pierwszego nie będę komentował :P No chyba, że się uczysz i chcesz spróbować własnych sił to możesz się pobawić... Ale lepiej poszukać gotowca w sieci lub mieć gdzieś w dalekich zakątkach swojego dysku jakąś funkcję i używać jej w razie potrzeby.

Której metody byście nie użyli to mam lepsze rozwiązanie :P Z pomocą przychodzi PHP 5.2 i funkcja filter_var (i ogólnie funkcje filtrujące).

Z czym to się je? Prosty przykład:

PHP:
  1. if(!filter_var($email, FILTER_VALIDATE_EMAIL)) {
  2. // błędny e-mail, obsłuż błąd
  3. }

Zawsze jedna funkcja, zawsze działa tak samo, wbudowana w parser i ktoś inny martwi się żeby działała poprawnie czyli nie musisz się zastanawiać czy aby na pewno znalezione/napisane przez Ciebie wyrażenie regularne jest poprawne. Kolejna funkcja, która mi ułatwi życie a przeciwnikom PHP da argument, że język ten ma mnóstwo funkcji w których można się pogubić i które rozleniwiają programistę. Świat jest piękny :)

Przydatne linki:
Lista wszystkich funkcji filter_* - pl2.php.net/manual/en/ref.filter.php
Lista dostępnych filtrów i ich opisy - pl2.php.net/manual/en/filter.filters.php

PS
Z jakiegoś powodu filtr FILTER_VALIDATE_URL przepuścił u mnie taki ciąg znaków "http://111111". Dziwne... Sam dodatkowo musiałem zatroszczyć się o sprawdzenie czy jest chociaż jedna kropka. Po jej dodaniu filtr działa już sprawnie.


Odczytanie sesji mając tylko jej ID

19 sierpnia 2009

Dzisiaj stanąłem przed pewnym problemem, który powstał w takiej sytuacji:
- mam panel administracyjny dostępny po zalogowaniu
- informacja o zalogowanym użytkowniku trzymana jest w sesji
- ID sesji jest przekazywany wyłącznie za pomocą cookie
- potrzebuję komunikować się między PHP a Flash

Problem powstał właśnie w miejscu komunikacji między PHP i Flash. Z jakiegoś powodu plik PHP wywoływany przez Flash nie potrafił odczytać sesji, tworzył nową. A ja musiałem odczytać info z sesji czy użytkownik jest zalogowany i ma odpowiednie uprawnienia. Ten element strony odpowiada za wgrywanie zdjęć na serwer i nie mógłbym zostawić tak dużej dziury w bezpieczeństwie.

Osobiście byłem tym problemem mocno zaskoczony bo przecież ID sesji przekazywane jest w cookie i PHP nie powinno mieć żadnego problemu z odczytaniem tej wartości. W jakiś sposób przeszkadza tu Flash bo inne skrypty oparte na Ajax działają bez problemów. Niestety na budowę Flash nie mam wpływu, korzystam z gotowego rozwiązania. Gdzieś tutaj widziałem źródła Flash ale nie po to stosuję gotowca żeby teraz w nim grzebać i pamiętać o zmianach zawsze gdy będę przeprowadzał aktualizację. Czytaj dalej »