PDO a další novinky v PHP 5.1

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

Článek vyšel v rámci PHP okénka na serveru Root.cz.

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čované a přestože klasická rozšíření pro práci s databázemi zatím nejsou označená 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ů, tak 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 předávání spouštěcích parametrů metodám query() a exec()
class MyPDO extends PDO {
    public function query($statement, $input_parameters = array()) {
        $result = $this->prepare($statement);
        $result->setFetchMode(PDO::FETCH_ASSOC);
        return ($result && $result->execute($input_parameters) ? $result : false);
    }
    
    public function exec($statement, $input_parameters = array()) {
        $result = $this->query($statement, $input_parameters);
        return ($result ? $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::bindParam a PDOStatement::bindColumn. 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_STATEMENT_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řepracovaná 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_timezone_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 zakompilovaná 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_HALT_OFFSET__. Konstanta odkazuje na první znak za koncem příkazu, který obvykle ukončuje středník, takže by data musela začínat ještě na stejném řádku. Využít se dá toho, že příkaz lze ukončit i značkou ?> a volitelným koncem řádku.

<?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

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, tak se vydání opravy doufejme příliš neprotáhne.

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

Diskuse

phonik:

PHP5.1.1 je fajn, ale mam pocit ze PDO nefunguje jeste docela dobre. Napriklad nejsem schopen prijit na to proc pri konfiguraci PHP 5.1.1 a Firebird 1.5.2 mi kterakoliv metoda pro fetch resultu prvni record neda, ostatni jiz v poradku ano. Pro lepsi priklad uvedu

Array
(
    [0] => stdClass Object
        (
            [name] =>
        )

    [1] => stdClass Object
        (
            [name] => lukas
        )

)

ackoliv radek v TBL 100% je a IBExpert i dalsi tooly mi jej v poradku zobrazi.

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL: Reakce na: phonik

ikona Jakub Vrána OpenID:

http://bugs.php.net/bug.php?id=35386

MAto:

Chcem sa opytat ci ma nejaky zmysel ten return v konstruktore MyPDO triedy.

ikona Jakub Vrána OpenID:

Slouží jen k ukončení funkce (jinak by za větvemi switche musel být break), návratová hodnota konstruktoru se na nic nepoužívá.
avatar © 2005-2021 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.