mysqli_query_params()

Door Ad Fundum, 2 weken geleden, 186x bekeken

Een (ongeteste) hulpfunctie om snel prepared statements te doen via een MySQLi-connectie. Niet bedoeld voor gebruik i.c.m. PDO.

Gesponsorde koppelingen

PHP script bestanden

  1. mysqli_query_params.php

 

Er zijn 1 reacties op 'Mysqliqueryparams'

PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Thomas van den Heuvel
Thomas van den Heuvel
1 week geleden
 
0 +1 -0 -1
Iedereen mag natuurlijk zelf weten hoe je queries afvuurt op de database, zolang je maar begrijpt dat MySQL echte native ondersteuning heeft voor prepared statements. Dit houdt in dat er, indien je gebruik maakt van deze constructie, op een compleet andere manier wordt gecommuniceerd (via het zogenaamde binaire protocol) met je database dan wanneer je geen prepared statements gebruikt. Dit is dus méér dan enkel syntactische suiker en dit heeft o.a. invloed op de vorm en de hoeveelheid data die over de lijn gaat, en ook op het aantal (onder water) uitgevoerde queries.

Zoals ook al elders is aangegeven zijn prepared statements niet de enige manier om queries veilig uit te voeren noch zou de keuze voor een aanpak zo eendimensionaal (enkel letten op "security") gemaakt moeten worden. Wat je uiteindelijk ook gebruikt, het is altijd een tradeoff tussen veiligheid, performance, gebruikersgemak et cetera. Probeer dus ook altijd zelf een mening te vormen over waarom je een bepaalde methodiek hanteert, en doe niet simpelweg iets "voor de vorm", zonder dat je eigenlijk (precies) weet waarom. Gewapend met kennis neem je over het algemeen betere beslissingen.

De rest van deze reactie zal voornamelijk gaan over de code zelf.

Om te beginnen, deze code werkt niet :p. Is weliswaar redelijk simpel te repareren door $mysqli_stmt te vervangen door $stmt, en $oResultaat te vervangen door $res, maar voordat je iets publiceert en wereldkundig maakt, loont het op zijn zachtst gezegd de moeite om je eigen zut even te proofreaden / te testen. Vooral als het een nogal belangrijke spil vormt in de communicatie met je database. Deze code zou dus eigenlijk uitgebreid getest moeten worden/zijn. Om op dit script nu een predikaat "ongetest" te plakken is misschien een beetje erg kort door de bocht. Je zou dit haast gelijk kunnen stellen aan "experimenteel", en dat zou voor mij geen uitnodiging zijn om dit zomaar over te nemen. Het lijkt mij de bedoeling dat het schrijven van code je op den duur werk bespaart, en dit geen extra werk oplevert :).

In de korte tijd waarin ik de functie heb getest kwamen de volgende dingen naar boven.

De functie doet (veels)te veel.
Deze functie verzorgt het aanmaken van het query-template (de prepare), het bepalen van de typen van de parameters, het koppelen hiervan aan de placeholders (bind_param), het uitvoeren van de query (execute), het ophalen van het resultaatobject/de resultaten (get_result), het itereren over deze resultaten en het toekennen hiervan aan een hulparray en het (wellicht nogal vroegtijdig, waarover later meer) vrijgeven van de gebruikte resources. Het is waarschijnlijk handiger om dit in meerdere logische(re) bouwblokken op te delen die je later weer combineert tot makkelijk bruikbare functies of methoden die bijvoorbeeld het uitlezen, toevoegen, wijzigen of verwijderen van records volledig verzorgen.

De functie is "niet in de haak".
Is misschien meer persoonlijk, maar ik vind het niet intuïtief dat je eerst een statement creëert (A), en vervolgens een result-resource (B), dan eerst het statement vrijgeeft (A) en dan pas de result-resource (B). Ook als je dit straks in meerdere stukken opdeelt is "ABBA" mogelijk een intuïtievere nesting. Ik heb eigenlijk niet gekeken naar de eventuele implicaties van deze volgorde/het vroegtijdig vrijgeven voor locking enzo.

De functie retourneert soms waarden die geen goed uitsluitsel bieden over het slagen van de query.
get_result() retourneert enkel een mysqli_result object indien het een SELECT-query betreft. Andere queries of queries die mislukken retourneren false. Indien deze functie false retourneert (wat op meerdere plaatsen kan gebeuren) dan weet je eigenlijk nog steeds niet zoveel over wat er mis is gegaan, en mogelijk gaat er helemaal niets fout. get_result() is ook blijkbaar alleen beschikbaar wanneer je van de MySQL native driver (mysqlnd) gebruik maakt. Er zijn een aantal voordelen om mysqlnd te gebruiken ten opzichte van de MySQL Client Library en is er eigenlijk ook geen reden om dit niet te doen, maar ik weet niet of het heel vanzelfsprekend is dat iedereen tegenwoordig mysqlnd gebruikt en/of dit overal out-of-the-box wordt ondersteund. Dus moet je dit misschien ergens vermelden.

De functie druist tegen de principes van prepared statements in.
Om toch nog even terug te keren naar situaties waarin prepared statements (extra) geschikt zouden kunnen zijn: een van de weinige toepassingsgebieden die ik kan bedenken waar ik prepared statements misschien in zou zetten is bij imports van (grote) hoeveelheden data. Daar zou je namelijk bij uitstek gebruik kunnen maken van het querysjabloon, die je vervolgens veelvuldig hergebruikt bij het executen van verschillende gebinde data op eenzelfde sjabloon. Helaas wordt het sjabloon elke functie-aanroep opgeschoond en dat is toch een beetje het kind met het badwater weggooien (daarnaast levert een test uit de losse pols dat dit ongeveer 40% trager is dan het sjabloon hergebruiken bij heel veel inserts). Je zult het sjabloon lang niet altijd hergebruiken, maar dat houdt niet in dat je deze dan maar altijd op voorhand weggooit.

En dan dus de realisatie dat je netto "per query" eigenlijk twee queries uitvoert (prepare + execute), waarbij je heel vaak het sjabloon niet hergebruikt omdat je normaliter bijna uitsluitend SELECT-queries gebruikt. Dus waarom gebruikte je ook alweer prepared statements? :p Maar dat terzijde.

Je zou ook kunnen overwegen om de formatteringsparameters mee te geven aan de functie, omdat deze in zekere zin al vastliggen door de query, dus is het eigenlijk niet echt aan de functie zelf om dit te "raden".

Dan nog wat code constructie dingetjes:
* Indien het een SELECT-query betrof retourneert get_result() (bij succes) een mysqli_result object. Deze implementeert de Traversable interface. Dit houdt in dat je simpelweg via foreach ($res as $row) { ... } de data kunt overhevelen in plaats van fetch_assoc(). Dit levert wat schonere code en lijkt ongeveer even snel.

* In mijn zoektocht naar informatie kwam ik het variable-length argument lists token tegen. In plaats van die call-by-reference en call_user_func_array() brei zou je simpelweg het volgende kunnen doen: if (FALSE === $stmt->bind_param($format, ...$params)) { return FALSE; }. Lijkt bovendien ook nog wat sneller te zijn dan het voorgaande. Beschikbaar vanaf PHP 5.6+.

Er zal best iets bruikbaars te maken zijn van deze functie / met prepared statements, maar die heeft dan wel nog wat iteraties nodig waarbij je dingen uit zult moeten splitsen en slim zult moeten hercombineren.

Om te reageren heb je een account nodig en je moet ingelogd zijn.

Inhoudsopgave

  1. mysqli_query_params.php

Labels

Navigatie

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.