Traits a další novinky v PHP 5.4

Školení, která pořádám

Vyšlo PHP 5.4.0 alpha, takže je nejlepší čas se podívat na chystané změny. PHP 5.4 nepřinese tolik novinek jako PHP 5.3, změny mi ale povětšinou dělají radost.

Článek vyšel na serveru Zdroják.

Odstranění zastaralých obratů

PHP 5.3 označilo řadu obratů jako zastaralých, takže jejich odstranění z PHP 5.4 není žádným překvapením. Co zmizelo?

  1. Konstrukce break $var;
  2. Konfigurační direktiva safe_mode a související
  3. Konfigurační direktivy register_globals a register_long_arrays
  4. Konfigurační direktiva allow_call_time_pass_reference
  5. Konfigurační direktiva highlight.bg
  6. Konfigurační direktivy session.bug_compat_42 a session.bug_compat_warn
  7. Konfigurační direktiva y2k_compliance
  8. Funkce session_is_registered, session_register a session_unregister
  9. Funkce import_request_variables
  10. Funkce define_syslog_variables a stejnojmenná konfigurační direktiva

Žádný z těchto obratů už léta nepoužívám, takže jejich odstranění vítám, protože do budoucna umožní zjednodušení aplikací nebo snížení nároků na jejich konfiguraci. Konfigurační direktivy magic_quotes_gpc a magic_quotes_runtime kupodivu přežily, i když pro první jmenovanou existuje náhrada na úrovni konfigurace a druhá se prakticky nikdy nepoužívala.

Bez náhrady zdá se zmizela i užitečná direktiva safe_mode_exec_dir.

Nové vlastnosti

I v této části jde převážně o čištění jazyka a se změnami souhlasím. Mírně kontroverzní může být <?=, XML validita PHP skriptů ale stejně zaručena nikdy nebyla i při vypnutém short_open_tag (např. kód <?php echo "?><"; ?> je platný PHP skript, ale neplatný XML fragment).

Za zásadní změnu považuji zavedení konstrukce f()[0], která byla dlouho požadovaná a stejně dlouho odmítaná např. z důvodů horší čitelnosti. Možná se také ptáte, k čemu je z funkce vracet pole a použít z něj jediný prvek? Mně by se to kdysi hodilo v NotORM, kde byla původně metoda group dovolující provést třeba i více agregačních funkcí najednou: list($min_id, $max_id) = $table->group("MIN(id), MAX(id)"). Bohužel se ale neobešla bez dočasné proměnné ani v nejběžnějším případě, kdy se volala jen jedna agregační funkce. S PHP 5.4 by to šlo: $table->group("MIN(id)")["MIN(id)"]. Nové API $table->min("id") je ale stejně přehlednější.

Traits

Asi největší novinkou v PHP 5.4 je podpora takzvaných traits. Ty dovolují zmírnit nevýhody jednonásobné dědičnosti tím, že do třídy umožňují vložit definice metod ze společného zdroje nezávislého na dědičnosti. Hodí se to v situaci, kdy chci mít stejnou funkčnost ve více třídách bez společného rodiče. Někdy se to dá vyřešit pomocí metody __call:

<?php
class C {
    public $common;
    function __call($name, $args) {
        if (method_exists($this->common, $name)) {
            return call_user_func_array(array($this->common, $name), $args);
        }
        trigger_error("Call to undefined method " . __CLASS__ . "::$name()", E_USER_ERROR);
    }
}
?>

Zdaleka to ale nejde vždy, např. při implementaci rozhraní s tímto přístupem nepochodíme, protože tam musí být metody definované přímo. Pak nezbývá nic jiného, než je ve třídě všechny potupně zopakovat. PHP 5.4 řeší tento problém pomocí tzv. horizontal reuse:

<?php
trait T {
    function getIterator() {
        echo "T::getIterator()\n";
        return new ArrayIterator($this->data);
    }
}

class C implements IteratorAggregate {
    public $data = array();
    use T;
}

$c = new C;
var_dump($c instanceof T); // bool(false)
foreach ($c as $val) { // T::getIterator()
}
?>

Změnu považuji za největší i proto, že přidává nové klíčové slovo. Možnosti jsou ještě širší, především lze definovat řešení kolizí. Zatím je funkčnost popsaná v RFC, píše se ale už i dokumentace.

Jsem zvědav, který framework začne traits využívat jako první. A také který dokumentační nástroj a které IDE se s touto novou vlastností dokáže se ctí vypořádat.

Zákaz změny parametrů u abstraktních konstruktorů

Poměrně zásadní zpětně nekompatibilní změna se týká sjednocení chování při dědění abstraktních konstruktorů s ostatními metodami. Jde o to, že když v potomkovi definujeme abstraktní metodu, musíme zachovat její parametry (můžeme jim přidat výchozí hodnotu, můžeme přidat další parametry s výchozí hodnotou, ale nemůžeme parametry odebrat nebo jim změnit typ):

<?php
abstract class A {
    abstract function __construct($a);
}
class C extends A {
    function __construct() {
    }
}
// v PHP < 5.4 prošlo, nově vyvolá
// Fatal error: Declaration of C::__construct() must be compatible with that of A::__construct()
?>

Na konstruktory se toto omezení nevztahovalo, což nová verze opravuje (a tím zároveň sjednocuje chování s deklarováním konstruktoru pomocí rozhraní). Tento obrat se přitom občas používal, do dokumentace první alpha verze se ale popis této změny bohužel nedostal.

Informace o průběhu uploadu souborů

Už delší dobu se dají informace o průběhu nahrávání souborů od uživatele zobrazovat pomocí extenze APC. Nově to jde i jen pomocí session proměnných. Vzniklo k tomu několik konfiguračních direktiv začínajících na session.upload_progress., pomocí kterých se dá chování ovlivnit.

Pokud je sledování průběhu nahrávání povoleno, tak už stačí jen metodou POST poslat pole s názvem ini_get("session.upload_progress.name"). To způsobí vytvoření proměnné $_SESSION[ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")]], která bude obsahovat pole se všemi dostupnými informacemi. Detailně to rozebírá příklad.

Změny v extenzích

V extenzi JSON, která je v poslední době velmi populární, je změn více:

Změny příkazové řádky

Další novinky

Kromě těchto změn, které jsou důležité při vytváření aplikací, došlo také k řadě vylepšení ve výkonnosti a paměťové náročnosti PHP. V méně používaných extenzích došlo k několika dalším změnám, které tento článek nepopisuje.

Co mi ještě chybí

Když už lze volat f()[], tak bych také ocenil obrat (new C)->f(), který by se hodil u fluent interface. Nemožnost pracovat s výsledkem operátoru new se dá různými způsoby obejít, je to ale značně krkolomné:

<?php
// první možnost
function identity($x) {
    return $x;
}
identity(new C)->f();

// druhá možnost
class C {
    static function create() {
        return new self;
    }
}
C::create()->f();
?>

Této vlastnosti bychom se v PHP 5.4 měli ještě dočkat.

Občas by se mi také hodilo deklarovat návratový typ metod. Užitečné by to bylo hlavně u dědičnosti, kdy by se dalo odvozeným třídám přikázat, aby vracely stejný typ objektu. Této změny se podle mě zatím nedočkáme.

Závěr

Vydaná alpha na mě působí celkem vyzrálým dojmem. Pár chyb se sice již objevilo, ale věřím, že nebudou nijak závažné a další vývojová verze vyjde poměrně brzy. Změny na mě také působí poměrně uceleným dojmem, takže si myslím, že příliš dalších novinek se v PHP 5.4.0 už neobjeví.

Jakub Vrána, Seznámení s oblastí, 4.7.2011, diskuse: 5 (nové: 0)

Diskuse

Emil:

Hustej mazec..

Hacafrakus:

V knize je bohužel už napsáno v příkladu 273
<?php
// returnArray()[0]; // Syntaktická chyba
?>
Připsal bych to někam do chyb v knize - není to teda chyba, ale hodí se vědět, že to teď jde.

ikona Jakub Vrána OpenID:

Díky za upozornění. Opravil jsem si to ve zdrojácích knihy (které by se případně použily pro druhé vydání).

korek:

f()[0]  konečně, good job. traits jistě taky najdou svoje využití, ale že by konečně zavedli named parameters u funkcí, to ne

ikona Jakub Vrána OpenID:

set_magic_quotes_runtime(0) přežilo, set_magic_quotes_runtime(1) hází fatální chybu. To je myslím rozumné řešení.

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.