In een ander draadje opperde ik dat het veiliger was om prepared statements te gebruiken. Thomas reageerde hier op en ik herhaal dat hier om het andere topic niet te kapen.

Thomas van den Heuvel op 29/09/2019 16:49:24

[quote="Frank Nietbelangrijk op 29/09/2019 14:45:53"]Huh? prepared statements zijn een stuk veiliger als er zoals hier een get variabele in de query wordt opgenomen...

Dit is simpelweg niet waar. Het enige wat veiligheid ten goede komt is een juist gebruik. Wanneer je prepared statements verkeerd gebruikt is dit even onveilig als enige andere methode.

- Ariën - op 29/09/2019 14:51:10
Maar bij beiden moet je ervoor zorgen dat de invoer veilig is.

Ook dit klopt niet (helemaal). Je moet gewoon alle DATA in je query escapen. Het maakt dan niet uit of deze onveilig was of niet.[/quote]

Ik denk dat Thomas gelijk heeft in het opzicht van veiligheid. Toch ben ik wel een beetje nieuwsgierig waarom Thomas schrijft dat je gewoon alle DATA in je query moet escapen. Naar deze onderbouwing ben ik wel nieuwsgierig.

Ik denk dat het wel beter en makkelijker is om prepared statements te gebruiken omdat je niet na hoeft te denken over quotes en omdat je query zelf geen variabelen bevat maar placeholders. Placeholders zijn er overigens weer in twee of drie smaken: De vraagtekens waarbij de volgorde erg belangrijk is en de :name placeholders ofwel de named placeholders. Daarnaast zijn er nog de vraagtekens welke gevolgd kunnen worden door een nummer. (Deze informatie heb ik hier vandaan). Uit het artikel begrijp ik tevens dat de character encoding ook voor problemen kan zorgen. utf-8 was voor mij de afgelopen jaren de encoding om te gebruiken maar nu las ik in een ander artikel dat je voor mysql databases beter utf8mb4 kunt gebruiken. Ik ben dus benieuwd wie er liever named placehoders gebruikt en waarom en ook wie er met utf8mb4 werkt en wat de ervaringen zijn.
Persoonlijk wordt ik nooit zo vrolijk van de ORM aanpak.

Ik heb het idee dat er dan teveel "magic" tussen zit. In plaats van een direct SQL statement (wel zo leesbaar, makkelijk te testen) ga je via allerlei omwegen (query builder, annotation, enz) een query in mekaar zitten draaien (waar je uiteindelijk altijd via "raw" nog de puntjes op de i moet zetten), en dan hoop je maar dat ie precies doet wat je bedoelt (en als ie het niet doet zoek je jezelf een ongeluk).

Performance voor een enkel record is natuurlijk geen enkel probleem (en geeft natuurlijk ook wel wat programmeer gemak - als je het eenmaal allemaal ingeregeld/geconfigureerd hebt), maar ga je op deze manier "vele records" inlezen/bijwerken, dan gaat een ORM uiteindelijk over z'n nek (out of memory + traag). Dezelfde actie met gewoon een update statement in een transaction schaalt vele malen verder, en is veel sneller klaar (qua processortijd; qua programmeertijd is afhankelijk van de programmeur ;-) .

Nadeel is dat die performance in eerste instantie niet zo'n probleem is (kleine/lege database), maar als het project eenmaal begint te lopen (meer data) loop je op een gegeven moment tegen de limieten aan - en dan moet je de boel alsnog omschrijven naar "echte queries" of enorm gaan lopen "tweaken" in je ORM omgeving. De initiële winst van een paar regels code minder verdampt dan al weer snel.
Ik heb geen (echte) ervaring met Eloquent, wel met Doctrine (en nog een paar niet-PHP varianten). Uiteindelijk ga je voor elk te wijzigen record een object aanmaken. Op het moment dat je alles in een "transactie" wilt doen ga je die pas "flushen" (weer wegschrijven) als je alle records bewerkt hebt. Tot die tijd moet alles dus in je "PHP geheugen" blijven hangen. Dat past "vrij vlot" (paar duizend records) niet meer.

Natuurlijk heeft het als voordeel dat je getter/setter code ook uitgevoerd wordt, en dus alle randvoorwaarden gecontroleerd/bijgewerkt, maar voor grote operaties/aanpassingen is het niet altijd handig. Een trigger in een database is dan veel efficiënter (maar die heeft weer als nadeel dat je code over meerdere lagen verdeel wordt - zo is het altijd wat :-) ).
Ik wordt wél vrolijk van de ORM aanpak (van Doctrine) omdat het een mooi stuk gereedschap is voor het snel in elkaar zetten van onder andere een CRUD systeem. Maar dit is eigenlijk ook door de samenwerking van Symfony(4) met Doctrine.

Het werkt met een reeks CLI commando's:
1) bin/console make:entity Maak een entity. Geef op welke variabelen er in de entity mogen komen en van welk type deze variabelen zijn. En dat is inclusief relaties met andere tabellen. en je geeft ook per variabele de lengte op en of hij ook leeg mag zijn.
2) bin/console make:migration Dit maakt een migration bestand met daarin de query om de Product tabel aan te maken of te wijzigen.
3) bin/console doctrine:migrations:migrate Execute the query. De tabel is nu aangemaakt in de database.
4) bin/console make:crud Er wordt een controller, een formulier en vier templates aangemaakt. De vier zorgen ieder voor een deel van de CRUD. Het formulier wordt gebruikt door de CREATE en UPDATE pagina. De controller kennen we allemaal wel van het CMV model.

Andere veel gebruikte commando's zijn ook
1) bin/console make:controller Deze maakt een controller class aan
2) bin/console make:form Deze maakt een formulier aan

De commando's creëren automatisch een boel code en je gaat dan eigenlijk aan een flinke verbouwing beginnen om het naar je eigen hand te zetten. Het formulier bevat meestal te veel velden en soms ook te weinig. De pagina's zijn nog zonder CSS dus daar moet je ook wat mee doen. En ga zo maar door. Maar het scheelt wel tijd.

Erg fijn is dat de database voor je wordt ingericht zonder dat je daar verder iets voor nodig hebt.

Dit is allemaal prima voor een CRUD applicatie en dus vaak prima voor de voorkant van een website.
Wil je zoals Rob aangeeft grote hoeveelheden DATA uitlezen, kopiëren of aanpassen dan is dit veel minder geschikt. Ook kom je met Doctrine soms in de problemen (althans met het ORM gedeelte van Doctrine) als je een heel erg fancy query wilt schrijven. In deze gevallen kun je terugvallen op DBAL wat weer een gewone wrapper is en dus een stuk flexibeler is. Het leuke is dan dat Doctrine wat tools heeft om de data die je ophaalt uit de database alsnog in je entity te zetten.

Kortom is er veel mogelijk met Doctrine maar voor een aantal gevallen is het niet geschikt.
Ook hier is het (zoals Thomas dat mooi kan zeggen) een kwestie van het juiste gereedschap kiezen voor de klus die geklaard moet worden.
O ja, dat was het: code genereren, ook al zo'n reden waarom ik niet zo'n fan ben van dit soort dingen.

Maak gewoon een basis class waar je tabelnaam + velden in kunt stellen (in een of andere definitie vorm*, zodat je weet dat het een integer, boolean, datum, enz is). En vervolgens maak je je aanpassingen in een afgeleide (veldje d'r bij, veldje d'r af, dwarsverbanden, getters, setters, enz - kan allemaal).

Voordeel is dat als je een keer wat in de basis wilt veranderen je niet al je reeds gegenereerde classes af moet om die aanpassing ook daar door te voeren. Maar gewoon OO: basis class veranderen = alle afgeleiden profiteren in 1 klap mee.

* maar het liefst wel gewoon in PHP, en niet in een of andere tekst formaat (docblock annotation, XML, YAML) wat daarna weer geparsed + gecached; en dus gerefreshed na aanpassen) moet worden, en waar je stiekem ook in kunt "programmeren", maar dan zonder hulp van de syntax check / code completion / enz van je editor (ja ik weet het, die zijn er wel, maar waarom allemaal met van die omwegen terwijl het allemaal zo simpel kan!?)

</rant>
Rob Doemaarwat op 02/10/2019 16:24:30

O ja, dat was het: code genereren, ook al zo'n reden waarom ik niet zo'n fan ben van dit soort dingen.

Maak gewoon een basis class waar je tabelnaam + velden in kunt stellen (in een of andere definitie vorm*, zodat je weet dat het een integer, boolean, datum, enz is). En vervolgens maak je je aanpassingen in een afgeleide (veldje d'r bij, veldje d'r af, dwarsverbanden, getters, setters, enz - kan allemaal).

Voordeel is dat als je een keer wat in de basis wilt veranderen je niet al je reeds gegenereerde classes af moet om die aanpassing ook daar door te voeren. Maar gewoon OO: basis class veranderen = alle afgeleiden profiteren in 1 klap mee.

* maar het liefst wel gewoon in PHP, en niet in een of andere tekst formaat (docblock annotation, XML, YAML) wat daarna weer geparsed + gecached; en dus gerefreshed na aanpassen) moet worden, en waar je stiekem ook in kunt "programmeren", maar dan zonder hulp van de syntax check / code completion / enz van je editor (ja ik weet het, die zijn er wel, maar waarom allemaal met van die omwegen terwijl het allemaal zo simpel kan!?)

</rant>


Ja, voor een simpele website is PDO meer dan voldoende en een ORM waarschijnlijk wel een overkill. Maar dan nog wegen de voordelen van een Doctrine ORM niet op tegen je eigen database wrapper schrijven. Je kan met Doctrine ook nog perfect plain SQL executen als je dat per sé wil.


Snap niet waarom je het warm water opnieuw zou willen uitvinden als er dergelijke ORM's al het werk doen voor je.
Ik zal wel te oud worden. Ik draai al een tijdje mee. Toen ik met m'n "wrapper" begon bestond Doctrine nog helemaal niet (natuurlijk wel blijven schaven in de loop der jaren). Daarnaast moge ondertussen duidelijk zijn dat ik het overkill vind voor wat het uiteindelijk voor je doet*, en dat je veel teveel op een black box moet vertrouwen dat ie maar precies doet waarvan ik precies weet wat ie moet gaan doen en het dan net zo goed gewoon zelf in kan typen. Anderzijds kom je met dit soort dingen altijd een heel eind, maar als het uiteindelijk "spannend" wordt moet je toch weer "met de hand ingrijpen" (omschrijven**, "raw" stukken aan de query builder toevoegen, of helemaal "plain SQL").

Ik snap dat het een mooie drop-in is om snel op weg te komen. Zeker bij nieuwe projecten, waarbij je het datamodel helemaal naar je hand kunt zetten (en niet aan een reeds bestaande database vast zit, met alle - met de kennis van nu - onlogische keuzes die daarbij gemaakt zijn). Ik maak ook graag gebruik van componenten die anderen volledig voor me uitgewerkt hebben, en waar ik dus met een eenvoudige Composer require een enorme klap code "cadeau" krijg***. Maar specifiek in dit geval (ORM) heb ik altijd het idee dat ik aan een spons sta te draaien, en dan maar hoop dat het andere uiteinde op de juiste manier meedraait. Ik zit graag wat directer aan de knoppen, heb er ook geen probleem mee om SQL queries te schrijven (en optimaliseren!), en dan werkt een minimale tussenlaag toch net even lekkerder.

<opa vertelt>

*) Ik heb nog in assembler geprogrammeerd waarbij je elke instructie afwoog om te kijken welke combinatie de minste cycles koste.

Je had bijvoorbeeld de "mul" instructie om een vermedigvuldiging te doen. Maar dat koste iets van 42 klok cycles op een 486 CPU. Voor grafische schermen moest je vaak met 320 vermenigvuldigen (de breedte van het scherm) om een x,y coördinaat naar een plek in het geheugen om te rekenen. daar was een "trucje" voor:

    mov ax,x //ax is een 16 bit register = plekje in de CPU
    mov bx,y //bx dito; y is max 200, dus de bovenste 8 bit zijn altijd leeg
    add ah,bl //bl is de onderste 8 bit van bx = gewoon y
              //door deze nu bij de bovenste 8 bit (ah) van de x waarde op te tellen
              //hebben we nu y * 256 bij x opgeteld
    shl bx,6 //de y waarde 6 posities naar links shiften = met 64 vermedigvuldigen
    add ax,bx //en dat ook bij x optellen
              //effectief heb je nu x + 256 * y + 64 * y = x + 320 * y

Al die shift en add instructies gaan allemaal binnen een klok cycle, dus dit is vele malen efficiënter dan die "mul".

**) Het ergste vond ik dit altijd bij Visual Basic: in eerste instantie klikte je een lekker eind weg, componentjes op hun plek slepen, snel ontwikkelen, super - klant tevreden, alles toppie. Vervolgens moest er nog "een klein dingetje anders", maar dat kon dan niet in die VB component (want: geen broncode), en dan moest je weer helemaal van voor af aan beginnen om het "zelf" te doen.

</opa vertelt>

***) Maar de autoloader van Composer is (was) dan ook weer zo'n draak. D'r werden veel te veel files al op voorhand ingeladen (de autoload_files.php). Het is maar een paar ms, maar wel een paar ms voor elke call. Dus zelf maar wat voor gemaakt. Inmiddels is het wel verbeterd geloof ik, maar dan blijf ik toch maar op m'n eigen ding hangen. Niet omdat ik graag het "warm water opnieuw uitvind", maar omdat ik "warmer water" wil(de) hebben.
Als je zoals Max Verstappen de GP wilt winnen dan moet je niet met een auto komen waar veel comfort in of een aantal groene labels aan hangen. Maar met een auto waar je je hele ziel en zaligheid in hebt geduwd om te zorgen dat je er het uiterste uit perst op de baan. Ook op de openbare weg komen we veel verschillende auto's tegen. de één daar past een hoop in en de ander is snel of juist zuinig etc.

Ik denk dat je programmeren daar een beetje mee kunt vergelijken. Ik zie mezelf in ieder geval niet een eigen database wrapper (en wat the heck nog meer allemaal) bijvijlen en schaven tot ik er die F1 racewagen wrapper van gemaakt heb. Ik heb daar simpelweg geen tijd voor (over). Ik wil gewoon binnen een redelijke tijd iets opleveren wat veilig en goed is. @Rob: Ik snap enerzijds je gedachte gang wel maar anderzijds kan ik het met een aantal dingen niet met je eens zijn. Ik wil iedereen in zijn waarde laten maar een voorbeeldje is de configuratie van een website. Die moet mij inziens echt niet in een PHP array. Dat is zo anno 1995 :p. XML is ook veel te omslachtig. Slechtste uitvinding ooit als je het mij vraagt.

Ben zelf ooit begonnen met een eigen framework. Ik heb er één website mee gemaakt daarna liep ik tegen Symfony op. Versie 4 heeft me helemaal blij gemaakt. Wat ik ook een hele fijne gedachte vind is dat de structuur van mappen en classen min of meer vast ligt. Als ik morgen niet meer wakker wordt kan een ander het van me overnemen. Wat ik ben tegen gekomen aan spaghetti html+code van anderen is werkelijk verschrikkelijk.
Je kunt de broncode meteen wegkiepen en mag je klant vertellen dat je opnieuw gaat beginnen. En dat is wat mij betreft een groot probleem in PHP land: de standaardisatie. Hoe grappig dat velen hun eigen wieltjes blijven uitvinden...

Reageren