środa, 27 czerwca 2007

PHP jako platforma

PHP zyskało ogromną popularność jako platforma stron www. Co do tego nie ma żadnych wątpliwości. Utwierdzają w tym przekonaniu przykłady takie jak WordPress. 1.2 miliona pobrań pokazuje skalę zastosowań tego blogowego silnika napisanego właśnie w PHP.

Przy tej ogromnej popularności, PHP jest bardzo dalekie od uniwersalności, czy też - co trafniej określa problem - jednorodności. Ogrom zainstalowanych wersji, różnorodność konfiguracji php.ini czy też dostępność modułów nasuwają programistom pytanie: w jaką konfigurację celować? Pytanie dotyczy wszystkich projektów, mających na celu instalację przez szeroką rzeszę webmasterów amatorów lub też profesjonalistów instalujących systemy na kontach dostarczonych przez klientów - takich jak wspomniany wcześniej WordPress.

Serwis Nexen prowadzi od jakiegoś czasu statystyki dostępności PHP. Do kompletu dostępne są również statystyki konfiguracji. Wszystkie te dane zbierane są od serwerów z publicznie dostępną stroną phpinfo().

Statystyki pokazują pełny przekrój serwerów, od tych napakowanych jak szwedzki scyzoryk, aż do tych na których postawienie bloga jest wyzwaniem. Oczywiście zastanawia prawdziwość owych danych. Wiadomo, że strony stawia się głównie na komercyjnych serwerach, które takich statystyk z reguły nie pokazują. Czy mamy się więc martwić setkami źle skonfigurowanych serwerów domowych? Pewnie nie, ale pokazuje to, że mimo wszystko problem istnieje.

Wg. bloga firmy Zend, 81% ich klientów używa PHP5, co zupełnie nie zgadza się z 11% Nexena. Wyjaśnieniem jest zapewne wspomniana wcześniej duża grupa dobrze skonfigurowanych serwerów komercyjnych. Jaka jest rzeczywista dostępność php5? Myślę, że nie znajdzie się śmiałek zdolny to określić.

Jakiej wersji PHP używać? Odwieczna wojna PHP4 vs. PHP5 trwa. Jeżeli nie ciągniesz projektów będących spuścizną PHP4, oczywistym wydaje się wybór PHP5. Wybieranie przestarzałej już wersji języka zdecydowanie nie pozwoli Ci na rozwinięcie skrzydeł projektu. PHP5 jest łatwiejsze w zarządzaniu i projektowaniu. Miej na uwadze: projektowanie i programowanie jest drogie. Hosting jest tani. Jeżeli provider nie pozwala Ci na używanie PHP5, zmień go! Konfiguracja PHP5 to nie fizyka jądrowa - wiem z doświadczenia.

Skoro wiemy jaką wersję PHP wybrać, przychodzi problem celowania w opcje i moduły. Od jakiegoś czasu trwa dyskusja na listach PHP aby stworzyć rekomendowany plik php.ini dla platform projektowych oraz dla środowisk produkcyjnych. Pomysł świetny, tak samo jak możliwość wybierania profili. Doczekamy ? Czas pokaże.

czwartek, 21 czerwca 2007

Ajax? Tak, byle dostępnie.

Jak to bywa z nowymi technologiami, kompatybilność wstecz to senne marzenie. Nie inaczej jest z nowym podejściem do architektury www - AJAXem. Niezwykłe efekty na miarę Flash'a, udogodnienia w interfejsie, mniejsze zużycie łącza ... wszystko to za cenę wykrzaczenia się strony na wszystkim poniżej IE6 i Firefox 1.5 (tak wiem, XMLHTTPRequest był dostępny już wcześniej, ale ... nie oszukujmy się ;). Nie wspominam tutaj o coraz popularniejszych przeglądarkach w komórkach (i ten argument zaczyna się dla mnie liczyć - sam używam) czy innych PDA.

Sprawę można podchodzić z różnych stron. Kilka z nich jest słusznych, kilka trochę mniej.

Złe podejścia do AJAX'a

Megalomania
Na obecnym poziomie AJAX służy do oprogramowywania drobnych elementów na stronie. Najgorszym podejściem jakie aktualnie możesz wybrać jest ładowanie całych stron XML'em. Dlaczego? Pierwszym argumentem jest to co napisałem powyżej - ignorowanie wszystkich ludzi bez JavaScriptu (a jest ich wg rankingów około 10%), drugim jest kompletne zbagatelizowanie problemu SEO. Jako iż większość ruchu na stronach pochodzi z wyszukiwarek, takie podejście może przysporzyć Ci kilku niemiłych dyskusji z Twoim specem od marketingu.

Cloaking
Jeżeli Google nie potrafi czytać Twojego JavaScriptu, czemu nie podstawić mu wersji specjalnie spreparowanej? Sprawę załatwiła dawno temu rzesza spamerów podmieniająca treści stron przygotowane dla Google. Nie chcesz wylecieć z wyników? Nie cloakuj.

Warstwy DHTML'a
Spotkałem się w kilku artykułach, że aby stworzyć Twoją stronę bardziej przyjazną przeglądarką, należy używać mechanizmów warstw w DHTML'u. Wszystko byłoby fajnie, jednak jaki jest sens rozwiązywania braku JavaScriptu .... kolejnym skryptem JS? Całość nie powoduje ani lepszego dostosowania SEO, ani większej dostępności.

Porzucenie AJAX'a
Najprostszą opcją w takim układzie wydaje się porzucenie AJAX'a i wrócenie do technik po stronie serwera. Czy warto jednak rezygnować z tych świetnych efektów i usprawnień po stronie klienta? Nie, jeżeli wiesz co robisz.

Jak zacząć dobrego AJAX'a

JavaScript
Zacznij projektowanie strony z wyłączonym językiem JavaScript. Upewnij się, że każdy jej element działa poprawnie i możliwe jest wygodne, normalne surfowanie po Twojej stronie. Dotyczy to wszystkich elementów onClick i innych akcji.

Kod strony
Jeżeli projektujesz stronę bez użycia JavaScriptu od początki, jedynymi aktywnymi elementami jakie uzyskasz na stronie są <a> oraz <input type="submit">, tracąc bajery jakie możesz uzyskać za pomocą klikalnych div'ów i eventów. Jednak praktycznie wszystkie te efekty możesz uzyskać wykazując trochę więcej kreatywności w tworzeniu stylów kaskadowych oraz kodu.

W tym miejscu, mając dobrze przygotowaną stronę, możemy zabrać się do implementacji AJAX'a. Problemem jest fakt, iż wszystkie aktywne elementy posiadają swoje linki i gdzieś prowadzą. Gdzie tutaj miejsce na swobodę? Z pomocą przychodzi oczywiście JavaScript.
<a href="plik.php" onclick="return false">Link</a>
Teraz ten link, w przypadku włączonego JavaScriptu, kompletnie nie zadziała. Jest to martwy link nie powodujący żadnej akcji. W przypadku elementów <input> zamiast onClick należy użyc onSubmit. Zautomatyzujmy to więc.
function disableActions () {
var links = document.getElementsByTagName("a");

for (i=0; i<links.length; i++){
var link = links[i];
link.onclick = function() {return false;}
}
}
Po wykonaniu tej akcji wszystkie linki na Twojej stronie będą nieaktywne.

Odzyskiwanie funkcjonalności
W tym momencie przychodzi czas na odzyskanie funkcjonalności z użyciem AJAX'a. Dla łatwiejszej identyfikacji, nadajmy aktywnym elementom odpowiednie klasy.
<a href="plik.php" class="funcUsun">Usun</a>
Teraz musimy stworzyć funkcję, która obsłuży funkcjonalność jaką link ten musi zapewnić.
var allFuncs = new Object();

allFuncs["Usun"] = function() {

// kod ajax w tym miejscu
return false;
}
Posiadamy już funkcję obsługującą oraz same linki. Jednak nie są one w żaden sposób połączone. Nic prostszego...
function disableActions () {
var links = document.getElementsByTagName("a");

for (i=0; i<links.length; i++){
var link= links[i];
link.onclick = allFuncs[getAction(link.className)];
}
}

function getAction(name) {
allNames = name.split("");
for(x = 0; x >< allNames.length; x++) {
if(left(allNames[x], 4) == "func") {
return right(allNames[x], allNames[x].length - 4);
}
}
return "";
}
Teraz wszystkie pola o zadanych klasach mają swoje kody obsługujące je w AJAX'ie. Uzyskaliśmy w ten sposób funkcjonalność jaką mieliśmy w przypadku prostej strony.

Oczywiście problemem jest oprogramowanie podwójnego interfejsu (AJAX'owego oraz zwykłego) po stronie serwera. Trzeba się więc zastanowić czy warto. Ja uważam, że tak.

wtorek, 19 czerwca 2007

Jak przyspieszyć ładowanie się stron?


Ostatnimi czasy natknąłem się na ciekawy artykuł napisany przez jednego z inżynierów Googla - Aarona Hopkins'a. Opisuje on wiele czynników wpływających na szybkość ładowania się stron.

Jednym ze źródeł problemu powolnego ładowania się jest ilość elementów, o które musi zapytać przeglądarka. Dlaczego?


  • Przeglądarki takie jak Firefox czy IE wykonują kolejne zapytanie http dopiero po zakończeniu poprzedniego (tzw. opcja pipelining jest domyślnie wyłączona), jedyną przeglądarką posiadającą tą opcję włączoną domyślnie jest Opera. Dodatkowo serwery posiadają w większości wyłączoną opcję keepalive, która pozwala na omijanie zbędnych handshake'ów podczas kolejnych zapytań.
  • Zarówno IE jak i Firefox mają limit dwóch połączeń do jednego hosta (przy nagłówku HTTP/1.1) oraz osiem połączeń całkowicie.
  • Większość łącz internetowych posiada asymetryczny transfer. W czym problem? Otóż wysłanie kilku-nastu/dziesięciu zapytań o obrazki, jscript czy css'y może przyblokować upload, który z reguły jest w stosunku 1:5 - 1:20 z downloadem.
Jak sobie z tym radzić? Jest kilka metod wykorzystujących zasady działania przeglądarek, co pozwala na ominięcie tych niedogodności.
  • Włącz opcję HTTP keepalive dla elementów zewnętrznych. Dzięki temu zyskasz trochę czasu i kilobajtów na zbędnych handshake'ach. Jeżeli boisz się o zbyt dużą ilość otwartych połączeń ustal limit keepalive na 5-10 sekund.
  • Ładuj mniej zewnętrznych obiektów. Jeden większy obiekt załaduje się szybciej niż dwa mniejsze (jednakże mniej "płynnie" - np. w przypadku dużych obrazów pociętych na kawałki). Jeżeli Twój design zawiera mnóstwo małych gif'ów, może spróbuj stworzyć layout za pomocą styli kaskadowych CSS.
  • Jeżeli posiadasz dużo obrazków, pomyśl nad umieszczeniem ich na różnych hostach. Dla przykładu możesz umieścić obrazki na static0.example.com, static1.example.com... itp. Jednakże, umieszczanie elementów na więcej niż 4 hostach nie ma sensu, z powodu globalnego limitu 8 połączeń przeglądarek (Firefox i IE).
  • Ustawiaj nagłówek Expire na jak najdłuższy czas. Generalnie dobrym pomysłem jest ustawianie nagłówka na czas maksymalny, co pozwoli na cache'owanie strony i jej elementów przez przeglądarki. Nie przyspieszy to pierwszego ładowania, ale następne już znacznie. W przypadku częstych zmian grafiki, możesz spróbować umieszczać je w różnych katalogach w zależności od czasu edycji. Np. po pierwszej edycji obrazek logo jest w example.com/build/1234/logo.jpg. W przypadku chęci zmiany umieszczamy go w example.com/build/1235/logo.jpg.
  • Zminimalizuj pliki nagłówkowe. W przypadku obrazków nie ma sensu składować ich na jednym hoście razem ze stronami. Przez to każdy obrazek otrzymywany jest razem z nagłówkami zawierającymi dane cookie i inne. Stwórz osobny host dla elementów stricte statycznych.
Aby zdiagnozować swoją stronę, możesz użyć kilku narzędzi. W artykule podany jest np. przepis na kod JavaScript zapisujący w logach serwera czas, jaki zabrały poszczególne zapytania. Jeżeli nie masz dostępu do logów możesz użyć innych narzędzi.
  • Dla sprawdzenia strony przy użyciu zewnętrznego, szybkiego serwera możesz posłużyć sie narzędziem Pingdom.
  • Jeżeli chcesz sprawdzić jak u Ciebie ładuje się Twoja strona skorzystaj z pluginu do Firefox'a stworzonego przez Google - Load Time Analyzer

Firefox jako IDE Web Developera


Firefox jest rewelacyjnym narzędziem jeżeli chodzi o pracę nad kodem stronek. Oferuje wystarczającą niezawodność i funkcjonalność, a po uzupełnieniu go o szereg wtyczek staje się naprawdę potężnym i przydatnym narzędziem.

DOM Inspector
Narzędzie to dostępne jest jeżeli podczas instalacji wybierzemy instalację niestandardową i zaznaczymy do instalacji DOM Inspectora. To proste narzędzie pozwala dokładne zapoznanie się ze strukturą dowolnej strony. Całość jest nieco toporna w obsłudze i wymaga wielokrotnego klikania myszką w rozwijające się drzewo. Pomocne jest jednak zaznaczanie czerwoną ramką elementów blokowych i obrazków. Dodatkowo można obserwować kaskadowe arkusze stylów (CSS) oraz skrypty JavaScript.

Rozszerzenie Web Developer
Web Developer to jedna z najbardziej rozbudowanych wtyczek dla web developerów pod Firefox'a. Kojarzy się ona z nafaszerowanym bajerami fińskim scyzorykiem i oferuje naprawdę ogrom przydatnych opcji.

Z tym dodatkiem możesz dowolnie kroić, mierzyć i nadzorować stronę za pomocą wbudowanych linijek i elementów pomiarowych. Jest to bardzo przydatne do ustawiania elementów strony w idealnej linii. Wartym wspomnienia narzędziem jest również lupa.

Developer do dyspozycji otrzymuje również szereg narzędzi do edycji oraz wyłączania obrazków, css'ów czy JavyScript. Ogromną ilość narzędzi informacyjnych wspomagają zewnętrzne validatory CSS, (X)HTML, JavaScriptów itp. Dla developerów PHP dostępne są opcje edycji plików cookie i formularzy w czasie rzeczywistym.

Zainstaluj rozszerzenie Web Developer.

Rozszerzenie Tamper Data
Tamper Data jest rozszerzeniem pozwalającym na dokładną obserwację całej komunikacji serwera http z przeglądarką. Pozwala ono na prześledzenie wszystkich zapytań http wysyłanych przez obie strony. Jest to niezwykle użyteczne w przypadku np. niewyjaśnionych problemów z kodowaniem lub dziwnymi przekierowaniami serwera. Narzędzie pozwala na testowanie aplikacji za pomocą ręcznie wprowadzanych nagłówków.

Zainstaluj rozszerzenie Tamper Data.

Rozszerzenie FireBug
FireBug jest chyba najbardziej znanym rozszerzeniem dla twórców stron www. I słusznie! Jest to najbardziej kompleksowe narzędzie do debugowania html'a, css'a, JavyScriptu, elementów DOM i wielu innych. Ogrom funkcji prowadzi czasami do oczopląsu, jednak w miarę przejrzysta budowa i chwila obcowania z programem pozwala na sprawną pracę. FireBug powiela wiele elementów ze wspomnianych wcześniej narzędzi, jednak jest chyba najbardziej dopracowanym rozszerzeniem i moim zdaniem powinno służyć jako główne narzędzie w debugowaniu. Pozostałe elementy służą jako często pomocne dodatki.

Zainstaluj rozszerzenie FireBug.

Rozszerzenie ColorZilla
ColorZilla jest malutkim pluginem służącym tylko i wyłącznie do pobierania koloru z dowolnego elementu strony. Jest to niezwykle przydatne, co w moim przypadku zaoszczędza mi kupę czasu na kopiowaniu screenshotów do edytora graficznego.

Zainstaluj rozszerzenie ColorZilla.

środa, 13 czerwca 2007

reCAPTCHA - weryfikacja człowieczeństwa przynosi korzyści

Niedawno natknąłem się na interesujący projekt - reCAPTCHA. Zasada projektów wykorzystujących obliczenia rozproszone, takich jak np. seti@home, jest podział problemu na małe kawałeczki i rozwiązywanie ich w terenie. Co czyni interesującym akurat ten projekt jest to, że to ludzie rozwiązują problem, nie maszyny. ReCAPTCHA ma dwa cele. Pierwszym jest przedstawienie słów niezrozumianych przez komputer tak, aby użytkownik rozpoznał tekst na obrazku. Drugim celem jest oczywiście weryfikacja bycia człowiekiem, tzw. mechanizm CAPTCHA. Niesamowitym pomysłem jest zaprzęgnięcie wielu ludzi do pracy nad projektem, zamieniając bezużyteczne przepisywanie cyferek i literek w coś pożytecznego. Genialne w swej prostocie!

Ale skoro komputer nie wie jak pisać dane słowo to na jakiej zasadzie zachodzi sprawdzanie? ... rozwiązaniem jest oczywiście dodanie drugiego, znanego już przez komputer słowa. Projekt jest niezwykle interesujący, a sam pomysł zachęca do jego wykorzystywania. Szkoda tylko, że nie ma wersji z polskimi słowami. Może wkrótce?

środa, 6 czerwca 2007

Zagnieżdżane kategorie

Zagnieżdżone kategorie w sklepie/koszyku czy innej stronie mogą sprawić lekki problem początkującemu programiście PHP. Poniżej postaram się przedstawić szybką metodę na stworzenie warstwy danych i warstwy logicznej (zgodnie z modelem MVC, który polecam) prostego systemu kategorii, który można łatwo wprowadzić w swój projekt.

Założenia
Do stworzenia kategorii użyjemy struktury drzewa, która idealnie się tutaj nadaje. Podstawowym problemem jest jak zaznaczyć zależności dzieci i pobierać to z jednopoziomowej bazy danych. Istnieje wiele podejść, jednak osobiście preferuje strukturę drzewa. Algorytm polega na oznaczaniu kolejnym numerkiem liścia/korzenia począwszy od lewej w dół. Myślę ze obrazek dobrze ilustruje założenia.


Warstwa danych
Podejrzewam, że najpopularniejszą bazą danych jest wszędobylski MySQL - darmowy, szybki i ogólnodostępny. Czego chcieć więcej. Przyjmuję założenie, że posiadasz już skonfigurowaną bazę danych i potrafisz wykonywać polecenia SQL. A więc do dzieła!

Pierwszą rzeczą, którą musimy zrobić jest oczywiście utworzenie tabeli zawierającej rekordy z poszczególnymi kategoriami. Czego potrzebujemy? W sumie niewiele. Wystarczy nam nazwa kategorii, opis i oczywiście pola na oznaczenie dwóch stron rekordu - lewej i prawej.
CREATE TABLE `cart_categories` (
`cac_id` int(11) NOT NULL auto_increment,
`cac_left` int(11) NOT NULL,
`cac_right` int(11) NOT NULL,
`cac_name` varchar(200) collate latin1_general_ci NOT NULL,
`cac_desc` longtext collate latin1_general_ci NOT NULL,
PRIMARY KEY (`cac_id`),
KEY `sides` (`cac_left`,`cac_right`)
) ENGINE=MyISAM;

Tabela jest niezwykle prosta, mamy wszystkie pola o których mówiliśmy wcześniej i oczywiście pole auto_increment oznaczające id danego rekordu. Nic prostszego nie mogliśmy wymyślić.

Warstwa logiki
No cóż, jednak to wszystko trzeba gdzieś przetwarzać. Posiadając prosty model danych musimy nadrobić wszystko w modelu logiki aplikacji. Ale bez paniki! Wszystko jest dla ludzi :)

Jeżeli chcesz pobrać drzewo za pomocą wartości left i right, musisz najpierw zidentyfikować wartości jakie ma element będący korzeniem. Przykładowo dla elementu 'Auto' powinieneś pobrać elementy o wartości left pomiędzy 2 i 11 (jako iż takie wartości posiadają jego pola left i right).
SELECT * FROM cart_categories WHERE cac_left BETWEEN 2 AND 11 ORDERY BY cac_left;
W wyniku tego otrzymasz wszystkie rekordy będące dziećmi rekordu samochód. Jedynym problemem jest brak poprawnej struktury drzewa, którą chcielibyśmy wyświetlić lub przetworzyć. Otrzymamy to za pomocą prostego algorytmu w PHP.
function fillDepths($sqlResults) {
$stack = array();

// przeprowadzamy iterację dla każdego elementu
foreach($sqlResults as $k => $v) {
// sprawdzamy stos
if (count($stack)>0) {
// sprawdzamy czy powinniśmy ściągnąć coś ze stosu
while ($stack[count($stack)-1]<$v['cac_right']) { array_pop($stack); } } // zapisanie glebokosci $sqlResults[$k]['depth'] = count($stack); // dodajemy do stosu $stack[] = $v['cac_right']; } }
Funkcja ta za pomocą stosu wyznacza głębokości (wcięcia) poszczególnych elementów drzewa, co pozwala na swobodne wyświetlanie drzewa w warstwie prezentacji. Funkcja jest pozbawiona części mniej istotnych funkcjonalności dla uproszczenia zapisu.

Bread Crumbs
Częstym problemem jest wyświetlenie ścieżki gdzie aktualnie się znajdujemy (pomocne przy tworzeniu tzw. bread crumbs). W przypadku takiej struktury jest to niezwykle proste. Wszystko załatwia się jednym zapytaniem. Możemy zauważyć, że wszyscy rodzice elementu o wartościach left 4 i right 5 mają wartość left mniejszą, natomiast wartość right większą. Wykorzystajmy to.

SELECT cac_name FROM cart_categories WHERE cac_left <> 5 ORDER BY cac_left ASC;W ten sposób otrzymujemy posortowaną listę rodziców zaczynającą się od korzenia.

Liczenie potomków
Równie prosto określić ile dzieci posiada dany element. Określa się to wzorem.
potomkowie = (cac_right - cac_left - 1) / 2
Dodawanie kategorii
Aby dodać kategorię, musimy przesunąć wszystkie elementy znajdujące się po prawej stronie od miejsca gdzie chcemy dodać element. Jest to niezwykle proste, wystarczy zwiększyć wartość left i right o 2 danym elementom. Jedyne co musimy wiedzieć to miejsce w którym chcemy dodać element.

Już niedługo opublikuję w tym miejscu w pełni działającą klasę implementującą powyższy kod.

Telefoniczne natręctwo


Dlaczego pani z Tele2 musi zawsze dzwonić w trakcie obiadu?! Generalnie jestem opanowaną osobą ale po raz kolejny przerwano mi spokojne spożywanie (a jem 3x dziennie i delektuje się tymi chwilami). Po drobnym poszukiwaniu w necie znalazłem podobnych mi niepokojonych przeżuwaczy i sposób na panią.

Pewna osoba pracująca w jednej z większych tego typu firm w kraju napisała prostą metodę na telefonicznych gwałcicieli spokoju. Wystarczy powiedzieć jedno magiczne zdanie: "Jeżeli zadzwoni pan/pani do mnie jeszcze raz to skontaktuje się z Wami mój prawnik". Rutynowo otrzymasz przeprosiny i zostaniesz wykreślony z całego systemu kontaktów z klientem. Wiadomo, operację trzeba powtórzyć u wszystkich chętnych do rozmowy, ale chyba warto dla spokojnych posiłków.

Teletubisiowe porno

Tinky Winky gejem? To najmniejszy problem... Wszystkie te kolorowe paskudztwa są zboczone! ... a kolory to pewnie oznaki różnych dewiacji. Ot co!

I są dowody! Film poniżej.

A kto twierdzi ze Sowińska jest homofobem ten jest oczywiście pedałem!



Jeżeli ktoś nie jest zorientowany to dostarczam wyjaśnień (za wikipedią oczywiście):

Ten niebieski kolega z trójkątną antenką to Tinky Winky - chłopiec, przedstawiciel Europejczyków. Natomiast jasnozielony nazywa się Dipsy, jest również chłopcem i symbolizuje Murzynów i Mulatów. Czy poza podtekstem erotycznym mamy tutaj jakieś inne?