PDO::PARAM_INT
Wat ik me echter afvraag... stel nu de bezoeker van mijn website moet ergens zijn geboortejaar invullen, bijv. 1980.
1980 is uiteraard een getal, maar omdat het via een formulier wordt gePOST zal het binnenkomen als een string. Mijn vraag is nu of ik dan PDO::PARAM_INT of PDO::PARAM_STR?
INT
Thanks Erwin. Kun je ook uitleggen waarom? Zover ik begreep worden integers niet gequote, en dus vraag ik me af of ik op deze manier niet de poorten openzet voor sql injectie.
En nee, je kan je query er niet mee open zetten voor een sql injectie. Als je een string meegeeft zal die worden gecast naar een integer (en wordt dan 0).
if (is_string($value)) $data_type = PDO::PARAM_STR;
Nu had ik dus zo'n zelfde controle gedaan met is_int() en dan als $data_type PDO::PARAM_INT. Echter zou ik dan in plaats van is_int() beter is_numeric() kunnen doen? Zodat als er via een formulier een getal wordt meegegeven dit ook als een int wordt gezien? (terwijl het feitelijk een string is)
Doe je die check nu in je database class om te kijken welk type param je moet gebruiken? Dat lijkt me niet handig. Want dan zou je kunnen krijgen dat je een int wil, de gebruiker iets raars invult en je dan alsnog een string param doorgeeft. Nou kan het op zich geen kwaad (geen sql injectie), maar het kan wel voor langzamere queries zorgen omdat je database engine dan nog de juiste typecasts moet gaan uitvoeren.
Stel de query is:
SELECT type, sort WHERE type = :type AND sort = :sort
Stel dat type een naam is en sort een getal... dan wil ik eigenlijk gewoon dit kunnen doen:
setValues('type' => $type, 'sort' => $sort);
En de database class zou dan moeten zien... aha $type is een string, dus PDO::PARAM_STR.
Aha... $sort is numeriek, dus PDO::PARAM_INT.
Dat was eigenlijk mijn gedachte.
Toevoeging op 25/05/2013 20:43:25:
Overigens...
Ik controleer natuurlijk wel eerst in de code of type een string is en of sort numeriek is. Pas daarna stuur ik het dan door naar de database class. Dus als ik ergens een int verwacht dan kan dat niet ineens iets anders worden.
Lijkt me op zich gevaarlijk. Er zijn namelijk types die niet makkelijk te ondescheiden zijn. Makkelijker lijkt me dan om een multidimensionele array mee te geven, waarin je ook de types zet. Het lijkt me namelijk niet echt de taak van de database class om te gaan bepalen welk type een variabele is.
Heb je een voorbeeld?
Maar eigenlijk vind ik het belangrijker dat je nu een dubbele check gaat uitvoeren die nergens voor nodig is en je gaat willes en wetens mogelijke fouten introduceren die niet nodig zijn.
De dubbele check komt omdat je nu in de aanroepende class extra moet checken op data type en in de database class nog eens. Geef je gewoon het parameter type mee dan doet PDO het grotendeels voor je. De fouten komen doordat je nu in sommige gevallen gewoon verkeerde types gaat meegeven terwijl je weet wat het moet zijn.
Eerst even een vraag tussendoor. Speelt dit alleen bij prepared statements? Of moet je bij een "normale" query ook een datatype meegeven. Volgens mij niet?? Echter, bij de quote() functie kun je ook een datatype meegeven die default op PDO::PARAM_STR staat. Maar dit snap ik niet. Je hoeft toch alleen maar strings te quoten? Waarom zou je dan een ander datatype willen meegeven?
Terugkomend op de types... ik kan in de database class toch gewoon controleren wat voor type het is? Als het een int is gebruik ik PDO::PARAM_INT, voor een strig PDO::PARAM_STR, voor een boolean PDO::PARAM_BOOl en voor null PDO::PARAM_NULL.
Vanuit een formulier controleer ik de geposte values. Dat zijn allemaal strings. En als er dan een bepaalde waarde is die een integer zou moeten zijn, dan cast ik die naar een integer en in de database class krijgt ie automatisch de juiste constante. Waar zit dan precies het gevaar?
Ozzie PHP op 26/05/2013 00:46:06:
Maar als het niet hoeft, dan ga ik liever niet telkens die types meegeven.
Soms kan ik je echt niet volgen....
Altijd overdreven gefixeerd op performance, tot in micro optimalisatie, maar wanneer je twee overbodige checks kan voorkomen per variabele dan doe je dat niet omdat je geen parameter mee wil geven! Het spijt me, ik kan het niet helderder zeggen dan dat.
Ozzie PHP op 26/05/2013 00:46:06:
Eerst even een vraag tussendoor. Speelt dit alleen bij prepared statements? Of moet je bij een "normale" query ook een datatype meegeven. Volgens mij niet?? Echter, bij de quote() functie kun je ook een datatype meegeven die default op PDO::PARAM_STR staat. Maar dit snap ik niet. Je hoeft toch alleen maar strings te quoten? Waarom zou je dan een ander datatype willen meegeven?
Ik heb er geen 100% zeker antwoord op, maar volgens mij is nuttig als je een integer meegeeft zodat quote weet dat er geen quotes omheen hoeven. Slimmer is om dan gewoon quote niet aan te roepen en te typecasten overigens.
setValues(['foo' => $foo]);
... dan dat ik iedere keer dit moet doen:
setValues(['foo' => ['value' => $foo, 'type' => PDO::PARAM_STR]]);
Die 1e optie lijkt me prettiger. Bij de 2e optie moet ik met keys gaan werken (value en type) en bovendien moet ik dan weer een contructie maken waardoor ik niet PDO::PARAM_STR kan gebruiken maar een OZZIE::PARAM_STR zodat het in toekomstige interfaces ook goed werkt.
Ik wil niet zeggen dat ik jouw manier niet wil / ga toepassen, maar ik zie het voordeel nog niet. Daarnaast snap ik niet wanneer ik 2 overbodige checks heb. Ik zie er maar 1, namelijk die in de database class. In het formulier zul je toch sowieso je data moeten valideren?
Gewijzigd op 26/05/2013 01:25:07 door Ozzie PHP
1) controller leest alle user input uit en checkt op compleetheid en sql injectie (en andere hack) pogingen om te loggen
2) query class checkt en typecast parameters
3) database class checkt op type van parameter en gokt welk parameter type nodig is
Checks 2 en 3 zijn overbodig als je een PARAM::XXXX meegeeft omdat het typecasten/escapen dan automatisch door de database server gebeurt.
De rest ga ik niet nog een keer herhalen.
Wat betreft jouw punten.
1) Ik zou de controller niet laten checken op sql injectie. De database class doet dit door gebruik te maken van prepared statements of de functie quote().
2) Zoals ik eerder vertelde heb ik geen aparte query class. Dit punt is dus niet van toepassing.
3) De database class checkt inderdaad het type. Wat kan daarmee mis gaan als ik zorg dat het juiste type binnenkomt.
Toelichting op punt 3: via user input komt alles binnen als een string. Dit betekent dat als ik deze values doorstuur naar de database class ze altijd als een string zullen worden geinterpreteerd. Correct? Als ik ergens in een formulier een integer verwacht, dan valideer ik of het inderdaad een integer is. Zo ja, dan typecast ik de value naar een integer. Op deze manier krijgt de database dus altijd het juiste datatype binnen.
Waar zit hier dan concreet de mogelijkheid waar het fout kan gaan?
Maar dit gaat in principe buiten het punt om, ik gaf het alleen mee om aan te geven welk serie aan checks er zouden komen mocht ik jou gang van zaken overnemen.
Punt 2) dan noem je het anders, maar feit is dat je een check MOET uitvoeren voor je de data aan de database class geeft. De database class moet namelijk aan de hand van het type variabele dat het binnen krijgt bepalen wat voor param type gebruikt moet worden. Doe je deze check niet, dan wordt het een gok en dat kan nooit de bedoeling zijn. Geef je het type mee dan is deze check onnodig.
Punt 3) ALS je het juiste type binnen laat komen. Je zegt net dat je voor de database class niets checkt, hoe weet je dan dat je het juiste binnen krijgt? Daarnaast is het niet alleen de vraag of je problemen krijgt, maar dat je een extra serie evaluaties moet doen om te bepalen welk type het is. Iets wat onnodig is als je gewoon het type meegeeft.
Bij je toelichting zeg je dus wel weer dat je punt 2 uitvoert.
Maar goed, het blijft voor mij onbegrijpelijk hoe je altijd totaal gefixeerd bent op performance en dan de volgende keer dit weggooit omdat je iets anders 'mooier' vind. Probeer eens consequent te zijn.
Voor mij is het nu wel over. Ik heb het punt gemaakt, doe er je voordeel mee, of niet.
Wellicht was mijn uitleg niet helemaal duidelijk, of heb jij deze anders geinterpreteerd. Het controleren (op inhoud) doe ik wel degelijk. Ik krijg een POST waarde en ik kijk of die aan de verwachtingen voldoet. Alle POST waardes zijn per definitie strings. Op het moment dat deze de database class binnenkomen worden ze dus ook als dusdanig geinterpreteerd. Op het moment dat via een formulier een integer verwacht wordt, doe ik een controle of het inderdaad een getal is (zo niet, formulier opnieuw laden met foutmelding). Indien het een getal is typecast ik het naar een integer (anders zou het namelijk nog steeds een string zijn). De database ontvangt dus een echte integer en zal deze dan ook als dusdanig interpreteren.
In mijn optiek vindt er dus 1x een extra controle plaats. Namelijk in de database class die op basis van het aangeleverde datatype, de juiste PDO::PARAM_XXX kiest. Dit is de enige extra controle.
Nu zou ik inderdaad het datatype PDO::PARAM_XXX rechtstreeks kunnen meegeven, maar dan kunnen het niet de PDO constantes zijn, want dan zou het voor een andere database interface niet meer werken. Dus ik zal een abstracte class moeten maken met OZZIE::PARAM_XXX. Dat betekent dus dat ik telkens een extra waarde op moet halen en die moet koppelen aan een class constant (idem als de fetch methode uit een eerder topic). Qua performance zal dit weinig verschil maken met de check die ik uitvoer.
Ik vind het jammer dat je in je laatste topic zegt "Voor mij is het nu wel over." want ik vind het juist interessant om van jou te horen, afgezien van het argument over performance, waar het in mijn methode mis zou kunnen gaan. Want dat is nu juist waarnaar ik benieuwd ben.
Een e-mailadres is een string maar niet elke string is een e-mailadres.
Vraag is dus wat je eigenlijk met "een database class" wilt bereiken. Een algemene "connector" voor een groot aantal databaseservers is iets anders dan een specifieke "mapper" voor een vast omlijnd datamodel.
Met andere woorden: als "geboortejaar" in jouw oplossing een specifiek datatype is, dan moet je dat in een interface formaliseren. En komt een MySQLi, een PDO, of een wat-dan-ook niet verder dan een integer, dan is er dus werk aan de winkel.
Ward, het gaat erom... als ik ervoor zorg dat in de database class altijd het juiste type wordt aangeleverd, dan kan PDO op de juiste manier de value "binden". Als ik zorg dat er een string binnenkomt, dan zal PDO het als string behandelen. Als ik zorg dat er een int binnenkomt, zal PDO het als een int behandelen. De vraag is of hier een bepaald risico aan vastzit. Ik zie het niet, maar Erwin wel.
Daarom noemde ik, ook omdat je het in de startpost noemt, een fundamentele twijfel: een geboortedatum is een integer, maar wel eentje met speciale karakteristieken. Dáárover zal een class die meer doet dan PDO zelf zich dus moeten buigen. Doet de class niets dan PDO nabootsen, dan heeft de class geen bestaansrecht.
Ik zie dat in zowel PDO als MySqli geen mogelijkheden zijn om een parameter van het datatype date(/time) te binden.