Jmenné prostory a další novinky v PHP 5.3
Článek vyšel na serveru Root.cz.
PHP 5.3 obsahuje nejvíce změn ze všech minoritních verzí, které kdy PHP vydalo. Důvodem je skutečnost, že vývojáři PHP se rozhodli do této verze přesunout téměř všechny novinky původně plánované pro PHP 6 s výjimkou podpory Unicode. Vývoj trval velmi dlouho (původně bylo vydání naplánováno na podzim 2007) a byl poměrně bolestivý kvůli několika změnám v jeho průběhu.
Obsah článku
- Jmenné prostory
- Anonymní funkce
- Late static binding
- __callStatic
- Zkrácený podmíněný výraz
- Příkaz goto
- Nowdoc
- Nový garbage collector
- MySQLnd
- Nové extenze
- Nové funkce
- Konstanty
- Změny v
php.ini
- Interní změny
Jmenné prostory
Jmenné prostory dovolují oddělit jednotlivé části programu tak, že i když obsahují stejnojmenné identifikátory, nebudou vzájemně kolidovat. Potřeba jmenných prostorů se naléhavě ukázala při vydání PHP 5.1, které obsahovalo třídu date, která kolidovala s PEAR knihovnou Date.
Pro definici jmenného prostoru se používá nové klíčové slovo namespace. To lze použít buď v syntaxi se složenými závorkami nebo jednoduše ukončit středníkem – potom bude ve jmenném prostoru obsažen veškerý následující kód. Syntaxe je tedy stejná jako u málo známé konstrukce declare (tu je možno nově použít i k určení kódování souboru, bez speciální kompilace ale volba slouží zatím jen k získání dopředné kompatibility s PHP 6).
V jednom souboru lze definovat více jmenných prostorů, to se ale využije asi jen zřídka. Naopak stejný jmenný prostor může být nastaven ve více souborech, takže je možno zachovat konvenci jednoho souboru na jednu třídu a přitom všechny mohou sdílet stejný jmenný prostor.
Pokud chceme použít třídu, funkci nebo konstantu z cizího jmenného prostoru, můžeme tak učinit předřazením jeho identifikátoru a zpětného lomítka. Jako oddělovač se v průběhu vývoje používala čtyřtečka, kvůli nejednoznačnosti ale byla nahrazena (znamená A::f()
statickou metodu ve třídě A
nebo funkci ve jmenném prostoru A
?). Uvažovalo se o šestitečce, zaslechl jsem návrh ležaté dvojtečky (..
), ale zpětné lomítko nakonec vychází asi nejlépe. Jen jsem zvědav, jak si s ním poradí editory zdrojového kódu…
Jmenné prostory lze vytvářet hierarchicky oddělením jednotlivých komponent pomocí zpětného lomítka. Pro zjednodušení práce s takovýmito jmennými prostory lze použít klíčové slovo use které importuje jmenný prostor pod identifikátorem jeho poslední části. Za use je navíc možné přiřadit klauzuli as, která dovoluje zpřístupnit jmenný prostor pod aliasem.
<?php // knihovna.inc.php namespace Knihovna\Komponenta { class Trida { function __construct() { echo "OK\n"; } } } // index.php include "./knihovna.inc.php"; new Knihovna\Komponenta\Trida; // OK use Knihovna\Komponenta\Trida as T; new T; // OK ?>
Anonymní funkce
Některé funkce PHP přijímají tzv. callback parametry – funkce nebo metody, které se zavolají v určitém okamžiku. Jsou to např. funkce preg_replace_callback, array_map, ob_start nebo session_set_save_handler. PHP na místě těchto parametrů dovoluje předat globální funkci (předáním řetězce) nebo metodu nějakého objektu (předáním pole array($obj, 'metoda')
). Funkci je možné vytvořit i přímo na místě pomocí create_function, s tím je ale spojeno potenciální bezpečnostní riziko (pokud bychom v definici funkce použili neošetřená data od uživatele) a výkonnostní hendikep, kód se navíc zapisuje do řetězce, takže v něm v editorech nefunguje ani zvýrazňování syntaxe.
PHP 5.3 dovoluje funkci definovat přímo na místě podobně, jako to umožňuje např. JavaScript – uvedením klíčového slova function, vynecháním názvu funkce a připojením parametrů a těla funkce.
<?php $s = 'hello-world'; echo preg_replace_callback('~-([a-z])~', function ($match) { return strtoupper($match[1]); }, $s); ?>
Uplatnění vidím hlavně u jednoduchých jednořádkových operací, kde je definice globální funkce samoúčelná. Užitečné je samozřejmě i to, že jednoúčelová funkce nepřekáží mezi obecně použitelnými funkcemi.
Anonymní funkce mohou používat i proměnné nadřazeného objektu. Na rozdíl od JavaScriptu ale nejsou vidět všechny proměnné, nýbrž jen ty, které explicitně uvedeme v klauzuli use:
<?php $separator = "-"; $s = 'helloWorld'; echo preg_replace_callback('~[A-Z]~', function ($match) use ($separator) { return $separator . strtolower($match[0]); }, $s); ?>
Anonymní funkce jsou interně reprezentovány jako objekty třídy Closure
, která obsahuje metodu __invoke
. Tuto metodu můžeme nadefinovat i u vlastních tříd, takže pak objekty odvozené z těchto tříd můžeme používat v kontextu funkce:
<?php class Ahoj { function __invoke($s) { echo "Ahoj $s!\n"; } } $ahoj = new Ahoj; $ahoj("světe"); ?>
Late static binding
PHP vyhodnocuje klíčové slovo self při kompilaci, takže následující kód vypíše Trida
a nikoliv Potomek
, jak by se možná na první pohled zdálo:
<?php class Trida { static $a = 'Trida'; static function f() { return self::$a; } } class Potomek extends Trida { static $a = 'Potomek'; } echo Potomek::f(); // Trida ?>
K vyřešení tohoto problému dovoluje použít PHP 5.3 u statických metod místo self klíčové slovo static, které použitou třídu vyhodnotí až při běhu:
<?php class Trida { static $a = 'Trida'; static function f() { return static::$a; } } class Potomek extends Trida { static $a = 'Potomek'; } echo Potomek::f(); // Potomek ?>
__callStatic
Pro přetížení volání statických metod přibyla magická metoda __callStatic. Ta se zavolá vždy, když se pokusíme staticky zavolat nedefinovanou metodu nějaké třídy. Stejně jako metoda __call dostane název metody a předané argumenty.
<?php class Trida { static function __callStatic($nazev, $argumenty) { echo "$nazev\n"; } } Trida::f(); ?>
Zkrácený podmíněný výraz
Operátoru pro podmíněný výraz se obvykle říká ternární operátor, tento pojem ale vyjadřuje jen počet operandů (podobně jako u binárních a unárních operátorů) a je jen náhoda, že je v PHP jen jeden. Výraz ($if ? $true : $false)
vrátí $true
, pokud je podmínka $if
splněna, jinak vrátí $false
.
PHP 5.3 dovoluje prostřední část vynechat, takže výraz ($if ?: $false)
vrátí $if
, pokud je podmínka $if
splněna, jinak vrátí $false
. V JavaScriptu se stejně chová obyčejný operátor ||
, který vrátí první pravdivou složku (v PHP tento operátor vrací true nebo false).
Zkrácený podmíněný výraz bohužel není obdobou funkce IFNULL a když je podmínka nedefinovaná, tak vyvolá chybu úrovně E_NOTICE stejně jako jakákoliv jiná práce s neinicializovanou proměnnou. Obdobu této funkce si můžeme sami vytvořit pro pole, kde je tato vlastnost nejčastěji potřeba:
<?php function vychozi($pole, $klic, $vychozi) { return (isset($pole[$klic]) ? $pole[$klic] : $vychozi); } echo htmlspecialchars(vychozi($_GET, "search", "")); ?>
Příkaz goto
Po vášnivých debatách přibyl do PHP 5.3 příkaz goto. Slouží k přesunu na jiné místo kódu a uplatnění má především pro ošetření chybových stavů, osobně ho ale používat nebudu. Příkaz neumožňuje přeskočit dovnitř cyklu nebo příkazu switch.
<?php function f($filename) { $fp = fopen($filename, 'r'); $error1 = true; if ($error1) { goto clean; } return $fp; clean: fclose($fp); return false; } ?>
Nowdoc
PHP dovoluje řetězec zapsat třemi způsoby:
- Do apostrofů, kde se nedosazují proměnné a zpětné lomítko má zvláštní význam jen před apostrofem a zpětným lomítkem.
- Do uvozovek, kde se dosazují proměnné a zpětné lomítko má zvláštní význam před celou řadou znaků.
- V tzv. heredoc syntaxi, kde lze navíc psát uvozovky bez ošetřování.
Heredoc jsem nikdy neměl v oblibě, protože jako jediná syntaktická konstrukce v PHP je platformově závislá (používá nativní ukončovač řádku) a oproti uvozovkám přináší jen nepatrnou výhodu (konec řádku lze totiž na rozdíl od mnoha jiných jazyků v PHP použít i uvnitř apostrofů a uvozovek). Heredoc naopak nelze použít na všech místech (např. při deklaraci vlastností třídy).
V PHP 5.3 přibyl čtvrtý způsob definice řetězce, tzv. nowdoc syntaxe. Ta je podobná jako heredoc, ale neinterpretují se proměnné a zpětné lomítko nemá žádný význam. Heredoc bez proměnných lze navíc nově použít i u statických proměnných a třídních vlastností a konstant. Pro nowdoc se používá syntaxe <<<'EOT'
, pro heredoc přibyla vedle stávající <<<EOT
také alternativní syntaxe <<<"EOT"
.
<?php echo <<<'EOT' '$x:\' EOT; ?>
Nový garbage collector
PHP používá pro uvolňování paměti jednoduchý algoritmus počítání referencí. Pokud se na sebe vzájemně odkazují dvě proměnné, vzniká cyklus, který tento algoritmus nedokáže nalézt, takže proměnné zůstanou v paměti až do konce běhu skriptu. To je problém hlavně u dlouhoběžících skriptů.
V rámci Google Summer of Code byl implementován algoritmus, který tyto cykly dokáže detekovat. Tento algoritmus je součástí PHP 5.3 a protože skripty mírně zpomaluje, dá se vypnout funkcí gc_disable nebo konfigurační direktivou zend.enable_gc.
MySQLnd
PHP ve všech extenzích pro práci s MySQL (MySQL, MySQLi, PDO) používá univerzální knihovnu libmysql. Tato knihovna má několik nevýhod, proto PHP 5.3 přichází s novou knihovnou MySQLnd, kterou lze použít místo libmysql. Knihovna navenek nijak nemění API (kromě několika přidaných funkcí) a je tedy zpětně kompatibilní se staršími verzemi.
Hlavním přínosem knihovny MySQLnd je to, že data získaná od MySQL serveru ukládá přímo do struktur, které používá PHP. Knihovna libmysql naproti tomu data ukládá do vlastních struktur, které je před použitím v PHP potřeba převést, což stojí jednak čas a jednak paměť (data jsou uložena dvakrát). MySQLnd navíc data stejně jako PHP kopíruje až když to je potřeba, takže přiřazení řádku s výsledkem do proměnné nestojí téměř žádnou paměť. Podle dostupných informací by se navíc paměť zabraná MySQLnd měla počítat do memory_limit, to se mi ale nepodařilo potvrdit a i zdrojový kód vypadá, že místo emalloc používá malloc, které se do paměťového limitu nezapočítává. Nakonec to je možná dobře, protože řada hostingů má nastavené nesmyslně nízké paměťové limity a náhlé započítání paměti zabrané knihovnou (která se zabírá stejně, jen to není tak snadno vidět) by řadu aplikací vyřadilo z provozu.
Knihovna přidává i několik funkcí:
- Jednak jednoduchou mysqli_fetch_all, která efektivně získá všechny řádky výsledku, pokud je potřebujeme předat další vrstvě (např. šabloně)
- Dále mysqli_get_connection_stats, která dovoluje získat statistické informace o připojení
- A také mysqli_poll a mysqli_reap_async_query, pomocí kterých lze pokládat asynchronní dotazy – jejich výhoda spočívá v tom, že nemusíme čekat na odpověď z jednoho serveru a ještě než dorazí, můžeme položit dotaz dalšímu serveru
<?php $link1 = mysqli_connect(); $link2 = mysqli_connect(); $link1->query("SELECT 'test1'", MYSQLI_ASYNC); $link2->query("SELECT 'test2'", MYSQLI_ASYNC); $all_links = array($link1, $link2); $processed = 0; do { $links = $errors = $reject = array(); foreach ($all_links as $link) { $links[] = $errors[] = $reject[] = $link; } if (!mysqli_poll($links, $errors, $reject, 1)) { continue; } foreach ($links as $link) { if ($result = $link->reap_async_query()) { print_r($result->fetch_row()); mysqli_free_result($result); $processed++; } } } while ($processed < count($all_links)); ?>
Extenze MySQLi se přidává ke zbylým dvěma extenzím a zavádí možnost persistentního připojení. Nevznikla sice funkce mysqli_pconnect, ale názvu serveru u funkce mysqli_connect lze předřadit p:
, což vytvoření persistentního připojení zajistí. Většina nevýhod persistentního připojení zůstala, ale alespoň ta hlavní díky automatickému zavolání funkce mysqli_change_user zmizela (při připojení se ukončí transakce, odemknou tabulky a odstraní dočasné tabulky).
Nové extenze
- Phar
- Asi nejpřínosnější novou extenzí v PHP 5.3 je Phar. Jedná se o knihovnu, která dokáže do jednoho souboru zabalit více skriptů a pracovat s nimi, jedná se tedy o podobný koncept jako JAR. Soubor začíná výkonným PHP kódem (takže takto vytvořené archivy jsou spustitelné i bez extenze), kterému následuje volání funkce __halt_compiler a obsah dalších souborů (které mohou být třeba i komprimované). Předchůdcem této extenze byla PEAR knihovna PHP Archive a o zařazení extenze do základní distribuce PHP se dlouho debatovalo, nakonec se tak ale stalo. Phar registruje i vlastní protokol, který dovoluje přímo přistupovat k souborům uvnitř archivu. Formát dovoluje do archivu zabalit i celou webovou aplikaci a namapovat ji na URL.
- SQLite3
- Extenze SQLite podporuje pouze druhou verzi této databáze, třetí verze byla podporovaná jen přes PDO. Pro uživatele, kteří si PDO neoblíbili, je nově k dispozici extenze SQLite3. SQLite se tedy řadí k databázi MySQL, se kterou lze v PHP pracovat také pomocí tří různých extenzí…
- Fileinfo
- Knihovna Fileinfo nahrazuje starší Mimetype a slouží k získávání informací o typu souboru na základě jejich typu.
- Intl
- Extenze Intl je nadstavbou knihovny ICU, kterou bude používat PHP 6 pro podporu Unicode. Nabízí porovnávací funkce respektující odlišnosti jednotlivých jazyků, formátování čísel a jazykových hlášek a další obraty nutné pro internacionalizaci aplikace.
Několik extenzí bylo také odstraněno.
Nové funkce
- Do extenze OpenSSL přibyly nové obraty, které se dají použít pro OpenID autentizaci.
- Extenze SPL, která se používá především pro upravení interního chování PHP (např. pro změnu způsobu práce s objektem v kontextu pole), nově nejde zakázat. Kromě toho obsahuje řadu novinek, zejména datové struktury a třídy pro skalární typy, pomocí kterých se dá do PHP doplnit kontrola skalárních typů.
- Při eskalaci výjimek lze předat objekt s původní výjimkou. Hezky to je popsané na blogu phpFashion.
- Vznikla řada nových funkcí a nových metod. Několik funkcí dostalo nové parametry.
- Funkce getimagesize umí nově zpracovat ikony, na webu používané především v souboru
favicon.ico
. - Funkce getopt nyní nezávisí na externí knihovně, takže funguje i na Windows. To se týká i několika dalších funkcí.
Konstanty
- Pro zastaralé konstrukce byla zřízena konstanta E_DEPRECATED a její uživatelská varianta E_USER_DEPRECATED. (Co by to bylo za novou verzi PHP bez nové chybové úrovně, že?) Chybová úroveň E_STRICT zůstává vyčleněna z E_ALL, i když některé zdroje tvrdí opak.
- Místo obligátního
dirname(__FILE__)
, které se používá především při vkládání skriptů, lze nově použít konstanta __DIR__. - Aktuální jmenný prostor je k dispozici v konstantě __NAMESPACE__.
- Globální konstanty lze kromě tradiční funkce define nově definovat také pomocí klíčového slova const stejně jako u konstant tříd.
Změny v php.ini
- Vznikla nová konfigurační direktiva request_order, která se používá pro určení pořadí hodnot v poli $_REQUEST. Pokud není nastavena, použije se hodnota variables_order.
- Odesílání e-mailů lze nově logovat díky konfigurační direktivě mail.log.
- Byla odstraněna konfigurační direktiva zend.ze1_compatibility_mode, která sloužila pro emulaci chování objektů podle PHP 4. Neznám nikoho, kdo by si ji troufl používat…
- Do
php.ini
lze přidávat sekce, které dovolují upravit chování v závislosti na doméně nebo cestě k souboru. - Pro CGI byla přidaná podpora adresářové konfigurace (obdoba
.htaccess
). Slouží k tomu konfigurační direktivauser_ini.filename
, jejíž výchozí hodnota je.user.ini
. - Na extenze se lze odkazovat pomocí absolutní cesty.
- Dodávané konfigurační soubory byly přejmenovány na
php.ini-development
aphp.ini-production
a prodělaly několik změn, z nichž asi nejdůležitější je vypnutí magic_quotes_gpc i ve vývojářské konfiguraci. Výchozí hodnota session.use_only_cookies byla změněna na zapnuto.
Interní změny
V PHP 5.3 došlo také k několika interním změnám, které by měly vést ke zvýšení výkonnosti. Podle prvních testů se zdá, že se to podařilo.
- Pokud to je možné, tak se hodnota konstant v PHP 5.3 dosazuje už při kompilaci a konstanty se ukládají do paměti určené pouze pro čtení.
- Mělo by dojít ke zrychlení přístupu k proměnné
$this
. - Místo parseru flex se pro zpracování zdrojových kódů nově používá re2c.
Závěr
PHP 5.3 přináší neobvykle vysoké množství změn a dá se považovat téměř za plánované PHP 6 bez podpory Unicode a bez odstranění zastaralých obratů. Ještě více než kdy jindy se tedy nejspíš vyplatí s nasazením chvilku počkat, protože se jako již tradičně s nulovou verzí dají očekávat nějaké problémy (i když testovací fáze byla neobvykle dlouhá). Všechny změny se také postupně dostávají do oficiálního manuálu a jsou popsané v oficiální dokumentaci.
Diskuse
david@grudl.com:
Q1 2008 není podzim 2007 ;)

Jakub Vrána
:
Jako že vydání bety :-).


mark56:
prosim at uz je z php plne objektovy jazyk ... kez by jednou, nejlepsi by bylo kdyby php vzniklo ve zcela nove verzi vytvorene tak rikajic z gruntu. neco jako php reborn :-) mohla by to byt ta sestka podle vas ?
Karel Dytrych:
Rozhodne to bude krok kupredu ale neco ve stylu C# necekejte... Ale ta predstava se mi taky libi..

Jakub Vrána
:
Ne, šestka to nebude. Od šestky se dá očekávat zatím jen přidání podpory Unicode a odstranění zastaralých věcí.
A osobně jsem rád, že PHP zůstane takto univerzální.


Nishkam:
Ja taky :)
optik:
Obávám se, že se tak se nikdy nestane, to si PHP vývojáři prostě nemohou dovolit.Osobně mi vadí to, že čím dál více kódu v core PHP se dělá přepisem PHP prototypu do C. To je podle mě strašně neefektivní z hlediska vývoje a udržování do budoucna, viz Phar. Spíš bych byl proto, aby se předělal virtuální stroj na něco s JITem + nějaké rychlé malé jádro a pak základní knihovna přímo v PHP. Tak, jak to dělají v nějaké té alternativní Ruby variantě. To by pak vývoj mohl jít mnohem rychleji. Wrappery pro Cčkové knihovny mít v core je taky strašná brzda, měli by to všechno vyndat do PECLu.
JimX:
nechci slovickarit ... ale jste si jisty, ze vite, jak vypada plne objektovy jazyk? Ne, Java ani C# to neni :)Matej:
Pokud chces pouzivat robusni, objektovy jazyk, nepouzivej PHP. Nechtej neco od neceho, co k tomu neni urceno. Easy...Visitor:
Souhlas ale nyní mi vadí, že je PHP někde mezi. Každý se tlačí do objektů a zároveň v PHP pořádně nefungují a v každé verzi ještě jinak.Diskuse je zrušena z důvodu spamu.

