PDO a další novinky v PHP 5.1

28. 11. 2005
Doba čtení: 7 minut

Sdílet

PHP 5.1 slibuje podobně jako každá větší verze téměř jakéhokoliv produktu významné zrychlení, aktualizované knihovny a spoustu dalších oprav a vylepšení. Některé novinky jsou ale významnější a vyplatí se na ně podívat podrobněji.

PDO

Asi nejvýznamnější novinkou v PHP 5.1 je rozšíření PDO umožňující jednotným způsobem pracovat s rozličnými databázemi. Na rozdíl od knihoven ADOdb nebo PEAR DB se nesnaží vytvořit kompletní abstrakci skrývající rozdíly mezi jednotlivými databázemi, ale naopak k nim poskytuje nízkoúrovňový přístup se všemi jejich specifickými vlastnostmi pomocí jednotného rozhraní. Používání PDO je samozřejmě doporučováno a přestože klasická rozšíření pro práci s databázemi zatím nejsou označena jako zastaralá (v některých případech obsahují širší funkčnost), jednou k tomu pravděpodobně dojde. Pokud chcete pracovat např. s databází SQLite 3, jiná možnost už ani neexistuje, pro práci s MySQL jsou naopak možnosti hned tři – klasické MySQL, MySQLi pokrývající funkčnost verze 4.1 a právě PDO.

Prvním příkazem při práci s databází je inicializace – u databázových serverů to je připojení k nim, u SQLite otevření souboru. V PDO k tomu slouží konstruktor, kterému se předává DSN a případně uživatelské jméno a heslo. Překvapuje mě, že některým databázím se jméno a heslo předává přímo v DSN a jiným jako další parametry, ale asi to je dáno způsobem, jakým se s databází komunikuje.

<?php
$pdo = new PDO("mysql:dbname=test");
?> 

Trochu mě také zaskočilo množství funkcí pro pokládání dotazů. Zatímco rozšíření MySQL nabízí jedinou funkci mysql_query, v PDO jsou k dispozici hned tři:

<?php
// vrátí počet ovlivněných řádek
echo $pdo->exec("INSERT INTO test (jmeno) VALUES ('Franta')");

// vrátí PDOStatement, který lze přímo procházet
foreach ($pdo->query("SELECT * FROM test") as $row) {
    echo "$row[jmeno]\n";
}

// vázání proměnných
$result = $pdo->prepare("SELECT * FROM test WHERE jmeno LIKE ?");
$result->execute(array('Franta'));
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
    echo "$row[jmeno]\n";
}
?> 

Za pozornost stojí, že výsledky lze procházet jak tradičním while ($row = $result->fetch()), tak přímo konstrukcí foreach. Myšlenka vázání proměnných se mi líbí, ale protože často kladu dotaz s jedinou sadou parametrů, postrádám možnost v jedné funkci určit jak SQL dotaz, tak předané parametry. Za účelem vyřešení těchto drobných nedostatků jsem vytvořil skromné rozšíření:

<?php
/// rozšíření třídy PDO, které umožňuje jednotné předávání uživatelského jména a hesla a předávání spouštěcích parametrů metodám query() a exec()
class MyPDO extends PDO {
    public function __construct($dsn, $username = "", $password = "", $driver_options = array())
    {
        switch (preg_replace('~:.*~', '', $dsn)) {
            case 'firebird': return parent::__construct("$dsn;User=$username;Password=$password", "", "", $driver_options);
            case 'odbc': return parent::__construct("$dsn;UID=$username;PWD=$password", "", "", $driver_options);
            case 'pgsql': return parent::__construct("$dsn user=$username password=$password", "", "", $driver_options);
            default: return parent::__construct($dsn, $username, $password, $driver_options);
        }
    }

    public function query($statement, $input_parameters = array())
    {
        $result = parent::prepare($statement);
        return ($result && $result->execute($input_parameters) ? $result : false);
    }

    public function exec($statement, $input_parameters = array())
    {
        $result = parent::prepare($statement);
        return ($result && $result->execute($input_parameters) ? $result->rowCount() : false);
    }
}
?> 

Při použití vázání proměnných je záhodno vypnout direktivu magic_quotes_gpc, jinak se do databáze uloží nadbytečná zpětná lomítka.

Vázání proměnných je velice užitečný koncept zvyšující bezpečnost skriptů, ale doporučuji se obloukem vyhnout jeho implementaci funkcemi PDOStatement::bin­dParam a PDOStatement::bin­dColumn. Funkce by měly mít jasně definovaný vstup v podobě parametrů a výstup v podobě návratové hodnoty. V odůvodněných případech lze výstup předávat i skrze parametry, ale rozhodně je lepší se uvnitř funkce vyhnout používání globálních proměnných a obzvláště jejich modifikaci. Zmíněné dvě funkce způsobí, že různé PDO funkce budou měnit hodnotu nastavených globálních proměnných, což může u trochu složitějších skriptů vést k nepříjemným a těžko odhalitelným chybám.

Do PHP 5.1.0 se bohužel přes všech 6 RC dostala chyba (v CVS už opravená) způsobující, že exec v MySQL vrací vždy pouze 0 nebo –1. Představené rozšíření používá metodu rowCount, která touto chybou netrpí.

Pro vytváření složitějších rozšíření se může hodit atribut PDO::ATTR_STA­TEMENT_CLASS, který dovoluje nastavit potomka třídy PDOStatement, který následně bude vytvářen místo PDOStatement. Hodnota parametru je pole array("Název třídy", array("parametry konstruktoru")). Použití bez přidání jakékoliv funkčnosti je tedy následující:

<?php
class MyPDOStatement extends PDOStatement {
    private function __construct() { }
}

$pdo = new PDO("mysql:dbname=test");
$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("MyPDOStatement", array()));
?> 

Práce s datem

V PHP 5.1 byla z velké části přepracována práce s datem, díky čemuž např. začal fungovat negativní timestamp i pod Windows, které ho nativně nepodporují. Funkce by se navenek měly chovat stejně, ale nově respektují časové pásmo nastavené direktivou date.timezone nebo novou funkcí date_default_ti­mezone_set. Doposud bylo možné časové pásmo ovlivnit pouze proměnnou prostředí TZ a k práci s časem se používaly systémové knihovny, což způsobovalo nekompatibilitu mezi různými platformami. Nová je i funkce strptime převádějící řetězec na datum. Na rozdíl od funkce strtotime je u ní možné nastavit formát data.

Nově je také definováno několik konstant pro pohodlnější generování standardních formátů dat. Tyto konstanty jsou umístěny do třídy date, jejíž název bohužel koliduje s knihovnou PEAR Date, která existuje už tři a půl roku a která je poměrně hojně používaná. Je to o to smutnější, že třída date zatím nedefinuje žádné metody (byť je to v plánu). Pokud tuto nebo jinou stejnojmennou třídu používáte, můžete do konfigurace přidat  disable_classes = date.

Nové schopnosti regulárních výrazů

Knihovna PCRE, která se v PHP používá pro kontrolu Perl-kompatibilních regulárních výrazů, je průběžně vylepšována. V dokumentaci například chyběla zmínka o tom, že konstrukci \x je možné v UTF-8 režimu (při použití přepínače u) používat ve tvaru \x{...}, kde uvnitř složených závorek je hexadecimální číslo Unicodového znaku.

Do PHP 4.4.0 a 5.1.0 byla zakompilována verze s rozšířenou podporou Unicode, takže je možné kontrolovat rozličné vlastnosti Unicodových znaků. Například \pL strefí jakékoliv písmeno (s diakritikou i bez).

__halt_compiler

Poměrně nenápadnou a asi zřídka používanou novinkou bude nová řídící struktura __halt_compiler, která zastaví zpracování PHP instrukcí. Hodí se především pro vytváření skriptů přímo obsahujících data, např. instalačních programů. Na rozdíl od konstrukce exit, která ukončí zpracování skriptu, se za konstrukcí __halt_compiler nepokračuje ani v parsování, takže následující data nemusí vyhovovat PHP syntaxi. Na začátek dat odkazuje konstanta __COMPILER_HAL­T_OFFSET__.

bitcoin_skoleni

<?php
// jednoduchý instalátor
$fp = fopen(__FILE__, "r");
fseek($fp, __COMPILER_HALT_OFFSET__);
while (($filename = trim(fgets($fp)))) {
    $length = trim(fgets($fp));
    file_put_contents($filename, fread($fp, $length));
}
fclose($fp);
__halt_compiler();a.php
18
<?php
echo "a";
?> 

Další novinky

  • PHP 5.1 také pokračuje v opravách práce s referencemi. Změny jsou poměrně rozsáhlé a bohužel i poněkud chaotické. Tak třeba příklad z dokumentace proběhne v PHP 4 včetně 4.4 bez jakéhokoliv varování, v PHP 5.0.5 způsobí fatální chybu, ale v PHP 5.1.0 pouze striktní varování. Ne nadarmo je těmto změnám věnována téměř polovina oficiálních poznámek k aktualizaci.
  • Konstrukce instanceof, is_subclass_of a catch už také nevolají magickou funkci __autoload, takže jsou použitelné i bez volání funkce class_exists.
  • Interní funkce, které používají standardní parsování parametrů, nyní proběhnou i v případě, že je na místě číselného parametru uveden řetězec, který sice je převoditelný na číslo, ale obsahuje i nečíselné znaky. V tomto případě je vyvolána chyba úrovně E_NOTICE. Dřívější verze funkci nevykonaly a vypsaly E_WARNING.
  • Některé funkce dostaly nové parametry rozšiřující jejich schopnosti, z těch nejpoužívanějších zmíním alespoň file_get_contents (parametry offset a maxlen), preg_replace a preg_replace_ca­llback (parametr count), explode (negativní hodnota parametru limit) a substr_count (parametry offsetlength).
  • Z PHP 5.0.5 byla odstraněna funkce php_check_syntax, která kromě kontroly syntaxe modifikovala také tabulku funkcí. Místo této funkce lze použít tradiční `php -l` nebo novou funkci runkit_lint, pro tu je ale potřeba povolit samostatné rozšíření.
  • V INI souborech se lze odkazovat na již definované direktivy pomocí  ${direktiva}.
  • PHP 5.1 nově podporuje kromě Basic HTTP autentizace také bezpečnější variantu Digest, takže již není třeba ošetřovat tuto variantu uživatelskými skripty. Verze PHP 5.1.0 se bohužel chová jinak v Apache 1 a 2, takže je o něco hůř použitelná. Tato chyba už je v CVS opravena.
  • magickým metodám __get a __set přibyly logické doplňky __isset a __unset sloužící k testování existence, resp. ke zrušení virtuálních vlastností třídy.
  • Funkce var_export nově umožňuje exportovat i třídy. Využívá k tomu novou magickou metodu __set_state.

Závěr

I přes velkou snahu QA týmu vydat verzi, která by byla bez chyb, se to bohužel nepodařilo. Kromě zmíněných chyb v PDO::exec a Digest autentizaci bude v PHP 5.1.1 možná odstraněn nebo přejmenován konfliktní objekt date. Kontroverzní upozornění na zastaralost {} pro přístup ke znakům řetězce naštěstí mezi RC 6 a ostrou verzí zmizelo.

Přestože tedy PHP 5.1 přináší poměrně užitečné novinky, vyplatí se s přechodem myslím počkat až na PHP 5.1.1. Vzhledem k tomu, že současná verze obsahuje i bezpečnostní záplaty, se vydání opravy doufejme příliš neprotáhne.

Autor článku

Autor se živí programováním v PHP, podílí se na jeho oficiální dokumentaci, vyučuje ho na MFF UK a vede odborná školení. Poznámky si zapisuje na weblog PHP triky.