Co mi vadí na PDO

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

Extenze PDO, která se od PHP 5.1 dá použít pro práci s databázemi, se mi v zásadě líbí, přesto mám několik výhrad.

Líbí se mi, že jde o nízkoúrovňové řešení, takže s ním není spojena žádná zásadní režie stejně jako u tradičních extenzí (jako např. MySQL). Líbí se mi vázání proměnných, které zjednodušuje zabezpečení aplikace, a PDO zároveň nabízí jejich efektivní řešení (vázání proměnných na straně serveru je za určitých okolností pomalé, proto ho PDO dokáže emulovat). Zásadní výhrady nemám ani proti API.

Vadí mi několik věcí, které lze vyřešit pomocí dědičnosti:

Přestože lze tyto nedostatky vyřešit vlastní třídou odvozenou ze základní PDO, tak to znesnadňuje psaní ukázek využívajích PDO, které si nemohou dovolit předpokládat existenci tohoto potomka.

Nemožnost vázání polí

Zásadní problém, který mi na PDO vadí, je absence vázání polí. To je potřeba např. u dotazů typu id IN (1, 2, 3). Vyzkoušel jsem několik způsobů, jak to obejít, a žádný se mi nelíbí:

Zvažoval jsem dokonce, že bych zástupný symbol pro pole v dotazu vyhledal, to ale není tak jednoduché, protože např. uvnitř řetězců se zástupné symboly interpretovat nesmějí. To by znamenalo dotaz ručně parsovat, což by byla duplikace práce PDO.

Závěr

Všechny zmíněné problémy řeší pokročilejší knihovny pro práci s databází, zejména dibi. Jsem také zvědav, co přinese PDO 2, zatím jsem zaregistroval pouze spory ohledně CLA.

Jakub Vrána, Dobře míněné rady, 12.10.2009, diskuse: 10 (nové: 0)

Diskuse

ikona pepak:

Mám za to, že omezení PDO je dané primárně tím, co dovolují databáze samotné - nevím o tom, že by to některá z nich podporovala. PDO by to _muselo_ řešit emulací, ale to by odstraňovalo hlavní výhodu prepared statements - jejich rychlost (odpadá syntaktická analýza).

ikona Pavel Stehule:

Zdar

jednoparametricky prepared statement pro vzor neco IN (?) dost dobre nejde. Pokud vim, tak urcita shoda mezi databazemi je pouze ve skalarnich parametrech. MSSQL podporuje typ tabulka, PostgreSQL a ORACLE zase pole. Navic PostgreSQL zapis promenna in (pole) vyhodi jako syntakticky nespravny - protoze se snazi porovnavat pole a nikoliv skalar v poli - viz syntax: skalar IN (skalar, skalar, ...). Musi se pouzit syntaxe skalar = ANY(pole). V PostgreSQL se v teto situaci pouziva parsovani pole a pretypovani na cilovy typ

WHERE a = ANY (string_to_array($1,',')::int[])

tato konstrukce je bezpecna vuci sql injection a i rychla. Pokud by PDO preneslo i pole, pak muzu vynechat parsovani pole - ale to snad jeste nejde).

ikona Jakub Vrána OpenID:

Univerzální řešení by hodnotu proměnné mohlo dosadit už na straně PHP. Druhou možností by bylo expandovat jeden zástupný symbol na více a poslat jednotlivé prvky pole.

ikona Pavel Stehule:

Expandovat jeden placeholder na vicero v podstate nejde. Pocet parametru je zafixovan v provadecim planu. Tady bych se i branil proti vlastni inteligenci PDO. Od takoveto vrstvy ocekavam, ze posle do databaze presne to, co ji urcim. Pokud mozno s minimalnimi, nejlepe zadnymi upravami. V prepade, kdy je nejaka vrstva inteligentni, a upravuje SQL prikazy, tak se hrozne spatne hledaji chyby. Je ale fakt, ze pokud databaze nepodporuje pole, tak podminka WHERE neco IN (vycet) se pomoci Prepared Statements implementuje dost neohrabane. V MySQL mne napada pouzit funkci find_in_set, tj nahradit podminku za
WHERE find_in_set(sloupec, ?) <> 0

Ale planner dostane dost na frak :(

ikona Techi:

Všechny zmíněné problémy taktéž řeší i Zend_Db, což mi připomíná, že bych mohl napsat článek, co mi vadí na dibi :)

ikona Jakub Vrána OpenID:

To bych si velice rád přečetl. Až článek bude, tak sem dej prosím odkaz.

Toby77:

Podle této starší rfc stránky byl operátor IN (?) pro pole odmítnut v podstatě z důvodů, které už popsal Pavel Stěhule.
http://wiki.php.net/rfc/pdov1

Megaloman:

"Vadí mi, že neexistuje jedna metoda pro položení dotazu s navázanými proměnnými."

Ale ona je jen jedna :-)

Chyba je spíš v tom, že nerozlišuješ mezi prepared a jednorázovými dotazy. Jak by potom PDO rozlišovalo mezi prepared dotazy, klasickými dotazy s result setem a klasickými dotazy vracejícími počet řádků?

S prepared statementem vždy souvisí nějaká režie na straně serveru navíc, ale může dojít i k úspoře přenosu po síti díky použití binárního protokolu client/server. Proto nemusí být vždy vhodné používat prepared statements pro jednorázově vykonávané dotazy (jednorázové z pohledu života jednoho připojení k DB).

Proto je principiálně špatné i řešení rozšíření v předchozím článku o PDO, kde jsou přepsány metody query a exec tak, aby přijímaly parametry. Tím netvrdím, že v praxi není lepší.

Sám toto rozšíření používám, ale napadlo mě metody query a exec přepsat buď na variantu, která parametry v $statement nahradí a pak výsledek předá parent::query, nebo na variantu, kde bude nepovinný třetí parametr s volbou režimu (Jakubův prepared / klasický PDOvský).

Otázkou ovšem je, zda by nebyla emulace parametrů v query mnou navrženým způsobem delší, než režie s prepared statementem. Ale i tak by se našla situace, kdy by to mělo svůj smysl (víceméně čistě teoretická).

Nějak mě nenapadá, jak bych otestoval časovou náročnost přípravy prepared dotazu.

Celou dobu se tu bavím o MySQL.

"To by znamenalo dotaz ručně parsovat, což by byla duplikace práce PDO."

Používám verzi IN(implode(..., ale čekám na nápad, jak to přepsat. Třeba před spuštěním by se mohl odpovídající '?' mimo řetězec nahradit za str_repeat('?, ', count($pole) - 1) . '?' a array($pole) za $pole, samozřejmě s ošetřením situací, kdy je v poli více parametrů.

I když jde o duplikaci práce PDO, ale když použiju
<?php
$pdo
->query("id IN (" . implode(",", array_map('intval', $pole)) . ")");
?>
, tak stejně dotaz ručně parsuju.

ikona Jakub Vrána OpenID:

Všechny uvedené problémy PDO řeší moje nová knihovna NotORM, která PDO využívá. http://www.notorm.com/

Kit:

<?php
$pole
=array(1,2,3);
$stmt=$pdo->prepare("id IN (?".str_repeat(",?",sizeof($pole)-1).");";
$stmt->execute($pole);
?>

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.