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.

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á.

Vložit komentář

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:

avatar © 2005-2018 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.