Archiwum

Archiwum dla ‘PHP’ Kategoria

PHP: prawie jak parametry nazwane

Marzec 3rd, 2010 zergu 6 comments

W PHP nie ma czegoś takiego jak parametry nazwane z działania, ale jest taka rzecz z …wyglądu. O co chodzi? Mając funkcję:

function foo ($a = 1, $b = 22)
{
    echo $a.' ~ '.$b;
}

Normalnie ją wywołujemy na takie sposoby:

foo() // → '1 ~ 22'
foo (6) // → '6 ~ 22'
foo (3, 4) // → '3 ~ 4'

Jednak możemy sobie funkcję wywołać dodając etykiety parametrom:

foo ($a = 2, $b = 33) // → '2 ~ 33'

Możemy to zrobić również „niepoprawnie”:

foo ($b = 7, $a = 3) // → '7 ~ 3'

Jak widać PHP nie bierze pod uwagę tych nazw parametrów, jedynie kolejność. Nazwy mogą być dowolne, użyte zostaną i tak te z deklaracji funkcji. Powstaje więc pytanie — po co tego w ogóle używać? Otóż — dla przejrzystości kodu, co powinien dobrze zobrazować poniższy przykład:

render_last_posts (10, 'desc', true, null) // → że jakie te posty mają być?
 
render_last_posts ($limit = 10, $sort = 'desc',
    $only_published = true, $by_user = null) // aaa, takie!

UWAGA. Takie przypisania ustawiają wartości zmiennych w danym zakresie, co czasami może doprowadzić do błędów (nadpisanie wcześniej zdefiniowanej zmiennej) lub być sytuacją pożądaną (przypisanie wartości do późniejszego wykorzystania).

Grafika na licencji:

Kategorie:PHP Tagi:

Podstawy: osadzanie kodu PHP w pliku

Luty 12th, 2010 zergu 1 komentarz

Żeby nie zanudzać tym, co już zostało wiele razy napisane, napiszę szybko, że istnieją 4 sposoby osadzania kodu PHP w pliku, z czego 2 najpopularniejsze wyglądają tak:

<?php $tutaj_kod // sposób standardowy ?>
<? $tutaj_kod // sposób skrócony ?>

O pozostałych dwóch (tag <script language='php'>… i znany z ASP <% … %>) najlepiej od razu zapomnieć, chociażby z tego powodu, że nikt ich nie używa, a pierwszy z nich dodatkowo jest długi, brzydki i nie da się go mieszać z HTML-em.

Więc który sposób jest lepszy?

Jeśli tworzysz oprogramowanie, które ma być możliwie kompatybilne z wszelkimi serwerami i ich konfiguracjami (np. gdy tworzysz framework lub jakąś bibliotekę) właściwie musisz stosować zapis standardowy. Oszczędzi to zapewne wiele frustracji użytkownikom, bo jest to sposób, który zawsze działa. Jeśli jednak opisywany przypadek Cię nie dotyczy to warto stosować zapis skrócony (my go właśnie stosujemy bezproblemowo od dłuższego czasu). Wymaga on co prawda włączenia dyrektywy short_open_tag = On w pliku php.ini, jednak tworzony kod zyskuje sporo na czytelności. Do trybu PHP wskakujemy wtedy umieszczając tylko 2 znaki zamiast 5, natomiast zapis <?php echo 'Cześć' ?> można skrócić się aż o 7 znaków do <?= 'Cześć' ?>. A skoro można coś zapisać krócej, to czemu tego nie zrobić?

Co warto wiedzieć o wychodzeniu z „trybu PHP”

PHP wymaga kończenia  instrukcji średnikiem. Jak jednak widać w poprzednim akapicie, tego średnika nie ma. Jest to dopuszczalne, ponieważ wyjście z „trybu PHP” (?>)  samo wstawia brakujący średnik. Warto to wykorzystać i nie zaciemniać kodu niepotrzebnymi znakami, szczególnie w przypadku gdy „wchodzimy” i „wychodzimy” do PHP w tej samej linii.

Inną sprawą jest fakt, że pliki zawierające w całości kod PHP, nie muszą i właściwie nie powinny mieć zakończenia „trybu PHP” (wtedy jednak nie wolno pominąć średnika w ostatniej instrukcji). Chroni nas to przed specyficzną sytuacją, gdy omyłkowo pozostawione puste linie w plikach ładowanych za pomocą funkcji include lub require spowodują rozpoczęcie wysyłania odpowiedzi (PHP od razu wysyła treść, która nie jest buforowana, ani nie jest kodem PHP), czego typowym następstwem jest komunikat:

 […] headers already sent by […]
Kategorie:PHP Tagi:,

Profilowanie aplikacji w PHP z wykorzystaniem Xdebug

Czerwiec 2nd, 2009 zergu Brak komentarzy

Xdebug to rozszerzenie do PHP umożliwiające m.in debugowanie i profilowanie aplikacji napisanych właśnie w tym języku. O samej jego instalacji nie ma się co rozpisywać (użytkownikom Arch Linuksa tylko podpowiem, że znajduje się on w AUR).

Po zainstalowaniu, jeśli nasz system tego nie zrobi za nas, trzeba rozszerzenie wstępnie skonfigurować. Na przykładzie wspomnianego Archa zostaniemy dokładnie poinstruowani co trzeba zrobić:

 ==> Please add the following lines to your /etc/php/conf.d/xdebug.ini

 zend_extension=/usr/lib/php/xdebug.so
 xdebug.remote_enable=on
 xdebug.remote_host=<ip address>
 xdebug.remote_port=<port>
 xdebug.remote_handler=dbgp

Jednakże dla nas ważna jest tylko pierwsza linia tych ustawień, ponieważ pozostałe służą do komunikacji z osobnym klientem. Dlatego też można te linie zakomentować, bądź usunąć. Interesuje nas natomiast włączenie profilowania, co można uskutecznić poprzez dopisanie następujących linii do pliku konfiguracyjnego:

xdebug.profiler_enable=1
xdebug.profiler_output_dir=/tmp

Katalog docelowy jest domyślnie ustawiony na /tmp, więc ta linia tak naprawdę nic nie wnosi, poza tym, żeby było jasno widać jak można ten katalog zmienić.

Następnie restartujemy serwer włączamy stronę www. Po czym w katalogu docelowym powinny znajdować się już raporty o nazwach w rodzaju: cachegrind.out.<PID>.

Raporty takie można otworzyć za pomocą programów KCacheGrind, WinCacheGrind czy MacCallGrind i wyglądają mniej więcej tak:

kcachegrind

Na koniec ostateczny wygląd xdebug.ini:

zend_extension=/usr/lib/php/xdebug.so
xdebug.profiler_enable=1
xdebug.profiler_output_dir=/tmp

Bardzo ciężki quiz PHP

Kwiecień 2nd, 2009 Mistrz Wu 3 comments

W ramach rozluźniania się zrobiliśmy sobie test wiedzy na temat PHP. Test może wydawać się trudny, ale na doświadczonym pisarzu PHP nie zrobi wrażenia.

Taką tabelkę sobie uknuliśmy. Należy odpowiedzieć na pytanie: jaką wartość zwrócą poszczególne wyrażenia z tabeli? Obok nasze typowania i ukryta odpowiedź prawidłowa (zaznacz tabelę/tekst, aby zobaczyć, co tam się kryje).

Wyrażenie Reinmar Mr. Z mcv Odpowiedź prawidłowa
0 == 'abc' T F T T
'0' == 'abc' F F T F
'0' == '0000' T F T T
'000' == 'abc' F F F F
'000' == '00000' F F F T
false != null F F F F

Tak więc przypominamy o istnieniu operatora ===. Niech się koduje!

Kategorie:PHP Tagi:,

Sortowanie obiektów po dacie utworzenia

Luty 23rd, 2009 zergu 1 komentarz

Czasami istnieje potrzeba posortowania obiektów wg własnych kryteriów — dajmy na to w przypadku, gdy w jednej tablicy mamy obiekty różnych typów. Aby jednak sortowanie mogło mieć sens, potrzebne jest jakieś wspólne pole. W tym przykładzie chcemy sortować po dacie utworzenia, więc z założenia wynika, że obiekty będą miały pole created_at.

Do sortowania może wykorzystać funkcję usort:

bool usort ( array &$array , callback $cmp_function )

Jako argumenty przyjmuje ona tablicę i funkcję do porównywania (tzw. callback).

Funkcja porównująca przyjmuje za argumenty wartości do porównania (tutaj: obiekty) i zwraca -1, 0 lub 1 aby określić czy pierwsza wartość jest odpowiednio mniejsza, równa czy większa. W naszym przypadku znaki są zamienione by posortować „od najnowszego” oraz, dla uproszczenia, pomijamy zwracanie zera, ponieważ jego wystąpienie i tak jest mało prawdopodobne, a ponad to nie istnieje kolejne kryterium porównania, więc w przypadku takiej samej daty, kolejność dwóch obiektów jest dowolna:

public static function compare_by_date ($o1, $o2)
{
    return ($o1->getCreatedAt ('U') <= $o2->getCreatedAt ('U')) ? +1 : -1;
}

Metoda korzysta z dość typowego w aplikacjach Symfony (opartych na Propelu) gettera getCreatedAt, który przyjmuje format daty w takiej samej postaci jak funkcja PHP date. W tym przypadku jest to tzw. Unix epoch oznaczający liczbę sekund od początku roku 1970. A sekundy (czytaj: liczby całkowite) porównuje się już bez problemu.

Mając gotową taką funkcję (tutaj jako metoda klasy std) możemy posortować tablicę obiektów poprzez wykonanie:

usort ($images_and_videos, array ('std', 'compare_by_date'))

Kategorie:PHP Tagi:,

Epic: ALERT w PHP i niespodziewane białe ekrany śmierci

Grudzień 22nd, 2008 Mistrz Wu 1 komentarz

Zacznę od tego, że problemu nie zdołałem rozwiązać, gdyż zwyczajnie brakowało mi czasu i nerwów. Zamiast tego, zrobiłem obejścia, które… no, działają. A dlaczego? Nie wiem. Może jakiś doświadczony czytelnik zostawi odpowiedź w komentarzu…

Tak więc onegdaj, podczas wesołej pracy z PHP, zdarzyło się kilka razy, że przy prostej i klarownej operacji na dobrze określonych wartościach, PHP rzucał białym ekranem śmierci, a w logach pojawiało się niejasne:

ALERT - canary mismatch on efree() - heap overflow detected (attacker '127.0.0.1', file '/home/mcv/htdocs/cośtam/dispatch.php')

Czasami tylko zamiast efree było erealloc.

Chwila kombinowania wykazała, że taki efekt daje następująca linia:

$ids = array_merge ($this->f1(), $this->f2(), $this->f3());

Nazwy metod zostały zmienione dla większej czytelności. Wartości zwracane przez metody były najzwyklejszymi, PHP-owymi tablicami zawierającymi jedynie liczby całkowite. Liczba? Na pewno nie większa niż około 5800 w sumie. 5800 intów to dużo czy jak?

Po straceniu stosownej ilości nerwów, wpadłem na to, żeby trochę PHP-a oszukać. Dodałem zmienne pomocniczne:

$a = $this->f1();
$b = $this->f2();
$c = $this->f3();
$ids = array_merge ($a, $b, $c);

Magia! Wszystko zaczęło działać. Czy PHP znowu coś przede mną ukrywa?

W międzyczasie Pan Google pomógł skojarzyć dziwne wpisy w logach z „utwardzaczem” PHP, tj. Suhosinem, który ma za zadanie uodparniać dziurawego PHP na różne ataki włamywaczy. I niestety tylko tyle. Trop się urwał, a ja nie wnikałem, skoro obejście zadziałało. Pewnie coś z parserem PHP nie tak (jak zwykle), albo Suhosin narobił jakiegoś bałaganu. Łorewer.

Kilka dni później, po dodaniu nowego argumentu do zupełnie innej metody:

ALERT - canary mismatch on efree() - heap overflow detected (attacker '127.0.0.1', file '/home/mcv/htdocs/cośtam/dispatch.php')

Co? Że jak? Niniejszy kod:

public function render ($partial_name, array $locals = array())
{ … }

zastąpiłem nastepującym:

public function render ($partial_name, array $locals = array(), array $options = array())
{ … }

Od tej pory biały ekran śmierci (+wpis w logu) powodowały wszelkie możliwe rodzaje wywołań:

$this->render ('xyz');
$this->render ('xyz', array ('var' => 'value'));
$this->render ('xyz', array ('var' => 'value'), array());
# itp.

Znów zacząłem tracić czas i nerwy, aż w końcu dobra dusza Zergu zasugerował, żebym zrobił to samo co ostatnio. Chwila szukania w pamięci dała poniższy efekt.

function render ($partial_name, array $locals = array(), array $options = array())
{
    $this->render_old ($partial_name, $locals, $options);
}

function render_old ($partial_name, array $locals = array(), array $options = array())
{ … }

Magia, proszę Państwa! Działa! Nigdy nie lekceważcie potęgi nic-nie-robiącej funkcji opakowującej. Ani niepotrzebnych zmiennych tymczasowych. Bo mogą jednak okazać się potrzebne. A może ktoś zna wytłumaczenie dla tego bezsensownego zachowania PHP? Bo ja tego zupełnie nie rozumiem. Ale wiem już, że gdy zobaczę czyjś kod podobny do powyższego, to wcale nie z programisty będę się śmiał, a z czego innego… :^)

Dobranoc państwu i Wesołych Świąt.

Kategorie:PHP Tagi:, , , ,

Aktualizacja oprogramowania a wydajność

Grudzień 19th, 2008 zergu 2 comments

Krótkie spostrzeżenie na temat wydajności aplikacji (praktycznie nie wypełnionej danymi) po następujących aktualizacjach:

  • Symfony 1.1 → Symfony 1.2
  • Propel 1.2 → Propel 1.3
  • PHP 5.2.0 → PHP 5.2.6
  • PostgreSQL 8.1 → PostgreSQL 8.3

Prosty test został wykonany za pomocą ApacheBenchmarka:
ab -c 5 -n 300 -H 'Connection: close'. Jak widać symulacja bazowała na 300 użytkownikach, przy czym do 5 na raz wchodziło na stronę.

Przed

Próba 1:

Requests per second:    4.34 [#/sec] (mean)
Time per request:       1152.770 [ms] (mean)
Time per request:       230.554 [ms] (mean, across all concurrent requests)

Próba 2:

Requests per second:    4.79 [#/sec] (mean)
Time per request:       1043.581 [ms] (mean)
Time per request:       208.716 [ms] (mean, across all concurrent requests)

Po

Próba 1:

Requests per second:    5.33 [#/sec] (mean)
Time per request:       937.318 [ms] (mean)
Time per request:       187.464 [ms] (mean, across all concurrent requests)

Próba 2:

Requests per second:    5.39 [#/sec] (mean)
Time per request:       927.979 [ms] (mean)
Time per request:       185.596 [ms] (mean, across all concurrent requests)

Oczywiście z uwagi na brak testów pomiędzy poszczególnymi zmianiami ciężko jest powiedzieć coś więcej, niż tyle że warto aktualizować, choćby o tego jednego requesta na sekundę ;). Ot, taka ciekawostka.