mysqli_query_params()

Door Ad Fundum, 4 jaar geleden, 12.118x 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 13 reacties op 'Mysqliqueryparams'

PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Thomas van den Heuvel
Thomas van den Heuvel
4 jaar 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.
Willem vp
Willem vp
4 jaar geleden
 
0 +1 -0 -1
Nog een kleine aanvulling op het verhaal van Thomas: die opmerking over syntactic sugar geldt ook voor het keyword 'and' (regel 31).

Anders gezegd: 'and' is niet hetzelfde als '&&'. De operator precedence van 'and' is namelijk lager. In dit geval gaat het toevallig (in een kwade bui zou ik "per ongeluk" zeggen) goed, maar zodra er een andere operator bij komt, kan het verkeerd aflopen. Zo is bijvoorbeeld if($a and $b || $c) niet hetzelfde als if($a && $b || $c). Dit is het type fouten waar je heel lang naar kunt zoeken.

De operators 'and' en 'or' zijn mijns inziens beter geschikt voor flow control dan voor expressies. Oftewel: '$dbh = mysqli_connect() or die_gracefully();'. Als je het op een goede manier gebruikt, kan dat leesbaarder zijn dan er een if() omheen zetten.
Ad Fundum
Ad Fundum
4 jaar geleden
 
0 +1 -0 -1
Ik kan me toch niet aan de indruk onttrekken dat sommigen op alle slakken zout willen leggen, zelfs als er geen slakken zijn...

De functie is samengeraapt van verschillende functies uit mijn vorige raamwerk, toen ik nog niet de beschikking over PostgreSQL had. De code is bedoeld om te laten zien dat je het jezelf een stuk eenvoudiger kan maken veilige prepared statements te gebruiken, zoals je dat met PostgreSQL en pg_query_params() kunt doen.

Het verhaal over binair protocol doe vreemd aan, wat bedoel je eigenlijk?
De mysqli_*() -functies maken onder water gebruik van de MySQL client library. Daar zit geen verschil in protocol? En een functie als mysqli_prepare() voert volgens de documentatie geen query uit, dat doen alleen functies als mysqli_query() en mysqli_stmt_execute().

Het is gewoon zo dat alleen prepared statements een programmeur beschermen tegen SQL-injectie, daarover is (bijna) iedereen het eens. Zie bijvoorbeeld https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php en https://paragonie.com/blog/2015/05/preventing-sql-injection-in-php-applications-easy-and-definitive-guide . In overige gevallen ligt de verantwoordelijkheid van het scheiden van SQL-statements en data bij de programmeur, dat is veel foutgevoeliger. Je kunt daarin eigenwijs blijven en zelf correcte code blijven schrijven in de aanname dat je nooit een fout kunt maken. Maar fouten maken is nou eenmaal menselijk.
Misschien heb je ergens een keer per ongeluk dubbele aanhalingstekens gebruikt om een literal te quoten, en dan werkt dat ineens niet meer op een andere MySQL-server die ANSI_QUOTES aan heeft staan. Of je gebruikt mysql_real_escape_string() in combinatie met de LIKE operator, wat niet lekker werkt met '_' en '%'. Allerlei mogelijke problemen die je simpelweg niet kunt krijgen met de beveiligingsgarantie van prepared statements. Waarom zou je het anders willen?

De functie doet inderdaad iets onnodigs. Maar dat zit hem echt niet in de mysql_*() -functies. Het opbouwen van een string met parametertypen is overbodig en kan vervangen voor een str_repeat('s', count($params)), dat werkt net zo gemakkelijk vanwege de impliciete type casting in MySQL.

Maar niet alles is overbodig. Zo is het opnieuw opbouwen van het array $args nodig omdat array_unshift() de referenties corrumpeert. (PHP bug 44139 ?). De ABBA-volgorde is een kwestie van smaak, maar maakt technisch niet uit. Het gebruik van de operator 'and' ipv. '&&' maakt in dit geval ook geen verschil. Als je liever '&&' gebruikt kan dat natuurlijk ook.
En ik hoef toch niet uit te leggen dat je zelf error handling toe kan voegen als je een dergelijke constructie in je eigen code gebruikt? Ik wil liever ook geen heilige oorlog over trigger_error() vs. throw, kies zelf maar iets uit.
Dat gesputter over variabele length argument lists is leuk bedacht, maar niet in lijn met pg_query_params(), waardoor er je meer werk hebt aan refactoren wanneer je van database wijzigt, zoals ik heb gedaan. Stond overigens ook in de comments (maar oke, documentatie wordt zelden gelezen).
Maar het wordt wel bond gemaakt met de opmerking dat niet hergebruiken van een prepared statement 'indruist tegen de principes van prepared statements', alsof je alleen prepared statements mag gebruiken als je ze hergebruikt.
Daarbij wordt zonder gedegen performance test aangenomen dat enkele prepared statements van de MySQL client library trager zijn dan de eigen PHP-code. Over ongetest gesproken.

Afgezien van het feit dat Thomas alles liever handmatig wil blijven doen, kan eenieder met een kleine aanpassing ($oResultaat vervangen voor $res) de functie gebruiken zoals pg_query_params(), en heb je - zonder verder hierover na te hoeven denken - nooit meer last van SQL-injecties.
Thomas van den Heuvel
Thomas van den Heuvel
4 jaar geleden
 
0 +1 -0 -1
Tenzij je de oorspronkelijke code niet meer aan kunt passen is het nogal ironisch dat je meer moeite lijkt te hebben gestoken in de reactie om mijn argumenten te ontkrachten dan in de code zelf.

Ook lijkt het er niet echt op dat je reacties/argumenten goed hebt bestudeerd.

Voor wildcards zijn simpele fixes te verzinnen, hiervoor hoef je niet over te stappen naar prepared statements.

Ongeacht de methodiek die je toepast zul je moeten weten wat SQL-injectie omvat. Geen enkel verkeerd gebruik van wat voor "veilige" techniek gaat je hiervan redden. Niets is in die zin dus "veilig". Als je de principes van prepared statements niet snapt en stukken SQL aan queries gaat breien dan gaan je queries net zo goed nat. Zou overigens niet de eerste keer zijn dat dat gebeurt, heb dit vaak genoeg gezien.

Laten we het eens hebben over debugging van queries. Hoe stel jij je dat voor bij prepared statements? Zet jij daarvoor logging aan en ga je dan lekker door je logs heengraven? Succes.

Wanneer je van prepared statements gebruik maakt schakelt het MySQL Client Server Protocol over naar het binaire protocol. Om ook de legitimiteit van mijn antwoord te bumpen, en het staat ook gewoon een stuk interessanter, hierbij een link naar de documentatie.
Ad Fundum
Ad Fundum
4 jaar geleden
 
0 +1 -0 -1
Had jij het eerder over wildcards dan, of over debugging?

Over 'de principes' van prepared statements: de enige reden waarom ze veilig zijn is dat SQL-queries van bijbehorende data gescheiden worden door de data in parameters te stoppen.
Tegen dat iemand SQL aan elkaar gaat plakken vanuit ongecontroleerde data is geen kruid gewassen, je moet wel weten hoe SQL werkt om queries te kunnen schrijven.

Maar wat heeft dat te maken met een voorbeeldfunctie om SQL-injecties te voorkomen? Misschien kan je beter een tutorial schrijven over hoe je vanuit PHP goed kunt communiceren met de database.

De link naar de documentatie is handig. Ik had van het binaire protocol nog nooit last, omdat mijn applicatie overal dezelfde encoding heeft (unicode). Was er verschil in encoding, dan was het probleem vanzelf zichtbaar geworden en had ik het opgelost. Geen groot issue verder.
De documentatie laat vooral de inconsistentie van MySQL zien, in dit geval dat de functie set_charset() niet overal werkt. MySQL kent veel van dit soort verrassingen waardoor je iedereen aan zou willen raden om PostgreSQL te gaan gebruiken in plaats van MySQL.
Thomas van den Heuvel
Thomas van den Heuvel
4 jaar geleden
 
0 +1 -0 -1
> encoding
dit staat hier verder los van lijkt mij

> de functie set_charset() niet overal werkt
hier heb ik nog nooit van gehoord, bron? voorbeelden?

> MySQL kent veel van dit soort verrassingen
mja, zo heeft elke techniek wel zo zijn makken

> waardoor je iedereen aan zou willen raden om PostgreSQL te gaan gebruiken in plaats van MySQL.
Misschien is het inderdaad verstandiger om van PostgresSQL gebruik te maken dan van deze functie.
Ad Fundum
Ad Fundum
4 jaar geleden
 
0 +1 -0 -1
> dit staat hier verder los van lijkt mij
Waarom haal je het binaire protocol er dan bij?

De documentatie onder jouw link zegt: "The MySQL server sends result set data "as is" in binary format.". Onder "as is" begrijp ik "zonder transcoderen". Dus zou set_charset() geen effect hebben.

Maar als ik dan probeer te varen op jouw kennis, zeg je dat jij nog nooit van hebt gehoord. Dus kom ik weer terug waar ik begon, namelijk dat er helemaal geen probleem is met mijn functie.
En dan denk ik dat je gewoon weer wat parate kennis moest etaleren.
Thomas van den Heuvel
Thomas van den Heuvel
4 jaar geleden
 
0 +1 -0 -1
> Waarom haal je het binaire protocol er dan bij?
Dit was om toe te lichten wat de implicaties van het gebruik van prepared statements zijn. Het lijkt mij belangrijk om ook te weten wat er onder water gebeurt als je kiest voor een bepaalde aanpak en wat hier de gevolgen van zijn.

De (impliciete) vorm van het transport en het uiterlijk van de inhoud hebben verder geen invloed op elkaar, dat is wat ik bedoelde met "staat er los van"; het heeft niets te maken met character encoding.

set_charset() is een contract tussen de applicatie en de database. Enerzijds is dat een wens van de gebruiker (opvragen van data in een bepaalde vorm) en anderzijds min of meer een eis van de database (aanleveren van data in de afgesproken encoding). Dit gebeurt -zo goed als dat kan- in de voorgeschreven encoding. Normaal gesproken resulteert dit niet in vertalingen (omdat idealiter alles gelijkgeschakeld is qua encoderingen) maar als er verschillen zijn zal MySQL zo goed mogelijk proberen om deze te overbruggen. De transportvorm staat verder los van dit contract lijkt mij.

> zeg je dat jij nog nooit van hebt gehoord.
In de zin dat het mij vreemd overkwam en hier nog nooit tegenaan ben gelopen. En het mij eigenlijk ook niet voor kan stellen.

> Dus kom ik weer terug waar ik begon, namelijk dat er helemaal geen probleem is met mijn functie.
Nice try, maar dit rechtvaardigt niet automatisch de knelpunten waar ik het eerder over had :).

> En dan denk ik dat je gewoon weer wat parate kennis moest etaleren.
Niet als doel, maar als middel om mijn besluitvorming te onderbouwen. Dit heet in de volksmond ook wel discussiëren.

Wanneer je enkel vindt dat ik dit doe vanwege een soort van interessantdoenerij, en daarbij verder niet meer kijkt naar wat ik inhoudelijk probeer over te brengen dan is de discussie natuurlijk snel klaar.
Ad Fundum
Ad Fundum
4 jaar geleden
 
0 +1 -0 -1
Ik ben het met je eens dat het nu wel snel klaar is.

Je gaat namelijk niet in op mijn punten. Het enige dat er uit komt is een lange, onsamenhangende brei, met loze argumenten over "niet in de haak", "variabele argumentenlijst", "druist in tegen principes".

En je haalt er andere dingen bij die er ook niet toe doen, zoals het protocol onder water, alsof je iemand met een vraag over $_SERVER meteen het complete OSI-model moet uitleggen.

Met alle respect, maar dat is niet wat mensen verstaan onder "discussiëren". Hoe dat wel in de volksmond wordt genoemd laat ik aan de fantasie van de lezer.

Misschien moet je je eens afvragen of je jouw inhoud iets aangenamer kunt brengen, door minder te zenden, meer te vragen en te luisteren.
Willem vp
Willem vp
4 jaar geleden
 
0 +1 -0 -1
> Ik kan me toch niet aan de indruk onttrekken dat
> sommigen op alle slakken zout willen leggen

Een goed kok weet dat je zout het beste zo vroeg mogelijk tijdens het bereiden van je eten kunt toevoegen. Dat is lekkerder dan achteraf. Met programmeren is het niet veel anders. ;-)

Om even bij mijn eigen slak te beginnen: je zegt dat het in dit geval geen verschil maakt of je 'and' of '&&' gebruikt. Dat is overigens precies wat ik zelf ook zei. In *deze* situatie gaat het goed, maar dat wil nog steeds niet zeggen dat 'and' het juiste gereedschap is. Je kan ook met een schroevendraaier een spijker in de muur slaan. Gaat vaak ook wel goed, maar om altijd een schroevendraaier te gebruiken wordt toch echt niet aanbevolen. Als je jezelf dit soort dingen aanleert, ga je ze ook gebruiken in situaties waar het wél verschil maakt en dan vertoont je software op de raarste momenten onverwacht gedrag. En omdat het soms wel en soms niet werkt als verwacht, is dat soort situaties lastig te debuggen. Daar komt nog bij dat op dit forum ook minder ervaren programmeurs rondlopen die zich helemaal niet bewust zijn van een precedentieverschil tussen de operatoren en -onder het mom van 'leesbaarheid'- nog veel sneller geneigd zullen zijn om 'and' te gebruiken in situaties waar ze zichzelf in de voet schieten. Met 'or' is dat gevaar overigens nog iets groter dan met 'and'.

Je zegt dat je zelf error handling toe kan voegen als je een dergelijke constructie gebruikt. Het probleem is, dat dit niet iets is dat je eenvoudig (of überhaupt) met error handling kan oplossen, aangezien het een logische error is. De beste error handling is om de operator niet te gebruiken in situaties waar 'ie niet geschikt voor is. Op het moment dat een constructie 'syntactic sugar' is, maakt het niet uit of je het een of het ander gebruikt. In dit geval is er een wezenlijk verschil in gedrag tussen de operatoren en welke je dan gebruikt is daarmee niet meer simpelweg een kwestie van smaak.

Wel ben ik het met je eens dat het een goed idee is om prepared statements te gebruiken. Wat ik dan weer mis in bovenstaande discussie, is de voorwaarde dat alle gebruikers-input via placeholders en parameter binding uit de query moet worden gehouden. PHP moedigt met zijn beschikbare functies het gebruik van placeholders niet aan; het voordeel van je _params-functie is dat die drempel wordt weggenomen. Echter, wanneer de programmeur geen placeholders gebruikt, zal het gebruik van jouw functie nog steeds geen bescherming bieden tegen SQL-injectie. Simpel voorbeeld waarin het mis kan gaan: mysqli_query_params($dbh, "select * from users where id=$_GET[id]", array()).

In je eerste reactie gaf je aan dat "in overige gevallen de verantwoordelijkheid van het scheiden van SQL-statements en data bij de programmeur" ligt, maar dat ligt het met jouw oplossing dus nog steeds, want in de functie zit geen code die waardes uit de query vervangt door placeholders om die waardes vervolgens expliciet te binden. Je uitspraak dat je zonder verder na te hoeven te denken geen last meer hebt van SQL-injectie is dan ook niet realistisch.

In principe zie ik voornamelijk voordelen van prepared statements. De veiligheid is beter omdat het *bij gebruik van placeholders* geen SQL-injectie mogelijk maakt, en het gebruiksgemak is beter omdat je -ook bij gebruik van placeholders- niet meer hoeft te pielen met controleren en escapen van je input, wat je code ook meteen een stuk leesbaarder maakt. Wanneer je een query slechts eenmalig gebruikt, zoals in dit geval, kan de performance iets minder zijn, aangezien er een extra round-trip naar de server gemaakt moet worden (dat wil overigens niet zeggen dat er een extra query wordt uitgevoerd; het is voornamelijk een extra communicatiemoment). Die performance-hit is trouwens in de orde van microsecondes, dus redelijk verwaarloosbaar. De tradeoff waar Thomas het over heeft, valt wat mij betreft dan ook eigenlijk altijd uit in het voordeel van prepared statements. Het voornaamste nadeel is dat parameter binding in PHP bij mysqli-functies expliciet moet worden uitgevoerd, maar daar is jouw functie dan weer handig voor. Overigens zijn niet alle queries te preparen, maar in de praktijk zul je daar denk ik vrijwel geen last van hebben.

Wat ik in de eerste reactie van Thomas nogal merkwaardig vind, is dat hij zo nadrukkelijk suggereert dat het eenmalig gebruiken van prepared statements een slecht idee is en indruist tegen de principes van prepared statements. Ik ben het daar niet mee eens. Het idee achter prepared statements is inderdaad dat je ze kunt hergebruiken, maar ze worden juist ook gebruikt om waardes te scheiden van de query teneinde SQL-injectie tegen te gaan. In dat kader vind ik het dus geen probleem wanneer ze eenmalig worden gebruikt. Sterker: als je naar andere talen als Perl gaat kijken, dan zul je zien dat een query soms al standaard wordt opgedeeld in een prepare/execute/fetch-sequentie. Eigenlijk precies wat deze _params()-functie ook doet. Het maakt de communicatie met de server bovendien uniform, en daar is ook iets voor te zeggen.

Als je het helemaal mooi wilt doen, zou je in je functie nog een statement-cache kunnen inbouwen; wanneer een query vaker voorkomt, kun je dan het eerder geprepareerde statement hergebruiken. Je zou dat kunnen doen door regel 14 te vervangen door iets als:

static $stmt_cache;
$stmt = $stmt_cache[$query] ??= mysqli_prepare($connection, $query);
if (!stmt) { return FALSE; }

Disclaimer: ongetest. Ik kan me voorstellen dat je ook $stmt als static moet declareren omdat er anders een impliciete close() wordt gedaan zodra $stmt out of scope raakt. En ook de $stmt->close() op regel 29 is wellicht niet zo handig als je wilt cachen.

Het opbouwen van de string met parametertypes is iets wat ik zelf achterwege zou laten, omdat ik teveel praktijkvoorbeelden ken waar een dergelijke kunstmatige intelligentie meer problemen geeft dan oplost. Ik zou het gewoon zelf laten uitzoeken door MySQL. Het zou bijvoorbeeld problemen kunnen opleveren als je een string hebt die uitsluitend uit cijfers bestaat. Die string zou hier als "i" gebind worden. Een string als "007" wordt dan geconverteerd naar de integer "7", waardoor je de voorloopnullen kwijtraakt. (Overigens heb ik dit niet getest, maar het zou me verbazen als het anders werkt.) Ik vind het daarom zuiverder om de parametertypes te laten afhangen van de tabeldefinitie, maar dat kost een extra query (plus nabewerking) en dat is weer niet bevorderlijk voor de performance. Bovendien zijn er ook in dat geval situaties te bedenken waar het alsnog mis kan gaan. Mijn voorkeur zou dan ook uitgaan naar standaard alles binden als een string. Nog mooier is het als de gebruiker zelf (optioneel) een string met parametertypes kan meegeven, zoals Thomas al had gesuggereerd, maar als je de compatibiliteit met pg_query_params() wilt behouden is dat geen optie.

Verder heb ik nog een fittie gezien over ABBA vs. ABAB. Technisch gezien zal het -meestal- niet uitmaken welke volgorde je hanteert. Zelf vind ik ABBA-intuïtiever, maar dat komt wellicht ook doordat ik tijdens mijn studie helemaal murw ben gebeukt met functionele decomposities en het in dat kader veel handiger was een ABBA-hiërarchie aan te houden. Eigenlijk is dat hetzelfde als wat Thomas erover zegt. :-)

Concluderend: ik vind deze functie in principe een nuttige toevoeging aan de gereedschapskist, maar ik ben het niet eens met de bewering dat hij SQL-injectie voorkomt zonder dat de gebruiker er verder over hoeft na te denken.
Ad Fundum
Ad Fundum
4 jaar geleden
 
0 +1 -0 -1
Goeie post.
Het klopt dat 'niet meer na hoeven te denken' ruim op te vatten is.

Natuurlijk werkt het alleen als de gegevens in aparte variabelen worden meegegeven, zodat ze nooit verward kunnen worden met de SQL statements. Dat is ook een essentie van prepared statements.

Maar het werkt niet in uitgebreidere gevallen, bijvoorbeeld als je identifiers (kolomnamen, tabelnamen, kortom namen) variabel wilt maken.
Voor PostgreSQL is er pg_escape_identifier().
Voor MySQL / MariaDB is er nog niets voor zover ik weet.
Ad Fundum
Ad Fundum
4 weken geleden
 
0 +1 -0 -1
PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
- Ariën  -
- Ariën -
3 weken geleden
 
0 +1 -0 -1
Het klinkt als een utopie, niet elke source wordt nog onderhouden. Of je vind ergens legacy code. Auwtsch!

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.