Mysqli/PDO/Doctrine wel/geen prepared statements en character encoding

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Technisch Ontwerper / Applicatie Ontwikkelaar

Technisch Ontwerper / Applicatie Ontwikkelaar Actief Wat ga je doen? Als Technisch Ontwerper / Applicatie Ontwikkelaar kom je te werken bij onze gerenommeerde klanten op projecten of opdrachten van omvang en formaat. Je bent verantwoordelijk voor het omzetten van functionele specificaties naar een technisch ontwerp, het ontwerp van programmaspecificaties voor toepassingen, de realisatie van (gewijzigde) programmaonderdelen en databestanden van toepassingen en de technische systeemtest van applicatietoepassingen. Daarnaast geef je vorm aan webpagina’s en applicaties, stel je gebruikersdocumentatie op en verleen je ondersteuning bij het oplossen van productiefouten. Tevens ben je verantwoordelijk voor het samenstellen en onderhouden van de applicatie c.q.

Bekijk vacature »

Senior DevOps-ontwikkelaar eIDAS

Functie­omschrijving Burgers en bedrijven veilig en betrouwbaar digitaal toegang geven tot diensten en producten van het ministerie van Economische Zaken en Klimaat. Als senior DevOps-ontwikkelaar bouw je daar letterlijk aan mee. En dat doe je bij DICTU: een van de grootste en meest vooruitstrevende ICT-dienstverleners van de Rijksoverheid. Jij werkt mee aan de doorontwikkeling van eIDAS, dat staat voor Electronic IDentification Authentication and trust Services. Deze koppeling maakt de grensoverschrijdende authenticatie op overheidswebsites binnen de Europese Unie mogelijk. Het ministerie van Economische Zaken en Klimaat heeft één moderne toegangspoort voor zijn diensten en inspecties. Enkele daarvan zijn dankzij eIDAS inmiddels

Bekijk vacature »

Technisch Ontwerper / Applicatie Ontwikkelaar

Technisch Ontwerper / Applicatie Ontwikkelaar Actief Wat ga je doen? Als Technisch Ontwerper / Applicatie Ontwikkelaar kom je te werken bij onze gerenommeerde klanten op projecten of opdrachten van omvang en formaat. Je bent verantwoordelijk voor het omzetten van functionele specificaties naar een technisch ontwerp, het ontwerp van programmaspecificaties voor toepassingen, de realisatie van (gewijzigde) programmaonderdelen en databestanden van toepassingen en de technische systeemtest van applicatietoepassingen. Daarnaast geef je vorm aan webpagina’s en applicaties, stel je gebruikersdocumentatie op en verleen je ondersteuning bij het oplossen van productiefouten. Tevens ben je verantwoordelijk voor het samenstellen en onderhouden van de applicatie c.q.

Bekijk vacature »

Frank Nietbelangrijk

Frank Nietbelangrijk

30/09/2019 18:18:56
Quote Anchor link
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:
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.


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.
Gewijzigd op 30/09/2019 18:20:53 door Frank Nietbelangrijk
 
PHP hulp

PHP hulp

30/10/2020 23:30:20
 
Rob Doemaarwat

Rob Doemaarwat

30/09/2019 19:49:41
Quote Anchor link
utf8mb4 is aan PHP zijde volgens mij gewoon utf8, alleen kan ie nu echt alle karakters uit de Unicode set opslaan (waar dat voorheen beperkt was tot de karakters met max 3 bytes).

Zelf zit ik ook in het "altijd alles escapen" kamp. Gewoon omdat je dan niet na hoeft te denken. Zelf werk ik met een soort DB wrapper die dit allemaal redelijk makkelijk voor me maakt.

Daar werk ik dan met named placeholders omdat ik er dan gewoon een array met variabelen achteraan kan gooien (zonder dat ze allemaal strict noodzakelijk zijn, of in de goede volgorde in de array zitten). Stel dat ik een array $user heb, met alle data voor de ingelogde gebruiker. Als ik dan een query wil draaien waarbij ik "een" waarde (of meer) uit deze array nodig heb geef ik 'm gewoon integraal mee bij de argumenten.

Andersom wordt een query vaak uit losse stukken samengesteld (afhankelijk van gemaakt keuzes). Placeholders komen dus niet altijd in de uiteindelijke query terecht. Met named placeholders hoef je je hier niet zo druk om te maken (gewoon meegeven, en de query wrapper zoekt wel uit of ie echt nodig is/was).

Merk op: met standaard PDO kom je hier niet zo makkelijk mee weg, omdat je geen argumenten mag binden die je vervolgens niet in de query gebruikt.
 
Frank Nietbelangrijk

Frank Nietbelangrijk

30/09/2019 20:35:12
Quote Anchor link
Rob Doemaarwat op 30/09/2019 19:49:41:
utf8mb4 is aan PHP zijde volgens mij gewoon utf8, alleen kan ie nu echt alle karakters uit de Unicode set opslaan (waar dat voorheen beperkt was tot de karakters met max 3 bytes).

Bedankt voor je reactie Rob.

Gebruik jij de utf8mb4 set in mysql? Het nadeel is volgens mij dat utf8mb4 dan maar 180 karakters kan plaatsen in een VARCHAR. En moet je dan met mysqli_set_charset() utf8 opgeven of utf8mb4? Hoe belangrijk is het om utf8mb4 te gebruiken voor laten we zeggen Nederlands of Engels?

Rob Doemaarwat op 30/09/2019 19:49:41:
Zelf zit ik ook in het "altijd alles escapen" kamp. Gewoon omdat je dan niet na hoeft te denken. Zelf werk ik met een soort DB wrapper die dit allemaal redelijk makkelijk voor me maakt.

Oke alles escapen om zeker te weten dat je je niet ergens per ongeluk vergist. Verder geen andere reden?
(Ik zeg niet dat het fout is maar ik wil dit draadje graag gebruiken om deze zaken zo veel mogelijk uit te diepen).

Rob Doemaarwat op 30/09/2019 19:49:41:
Daar werk ik dan met named placeholders omdat ik er dan gewoon een array met variabelen achteraan kan gooien (zonder dat ze allemaal strict noodzakelijk zijn, of in de goede volgorde in de array zitten). Stel dat ik een array $user heb, met alle data voor de ingelogde gebruiker. Als ik dan een query wil draaien waarbij ik "een" waarde (of meer) uit deze array nodig heb geef ik 'm gewoon integraal mee bij de argumenten.

Dat vind ik interessant. je geeft gewoon een associatieve array mee en dan vergelijkt de wrapper de array keys met de named placeholders? Jouw wrapper gebruikt uiteindelijk PDO?


Rob Doemaarwat op 30/09/2019 19:49:41:
Andersom wordt een query vaak uit losse stukken samengesteld (afhankelijk van gemaakt keuzes). Placeholders komen dus niet altijd in de uiteindelijke query terecht. Met named placeholders hoef je je hier niet zo druk om te maken (gewoon meegeven, en de query wrapper zoekt wel uit of ie echt nodig is/was).

Ik herken dat denk ik van filters op een listview. De basis SELECT query ga je dan uitbreiden met een WHERE claus met geen of één of meerdere ANDs afhankelijk van hoeveel filters er gebruikt worden. Dat deed ik dus in twee stappen. Als eerste bouwde ik de query string (met named placeholders) op en een array met de placeholders en de variabelen die dan met setParameters() doorgegeven kon worden. Maar nog steeds is de string die ik uiteindelijk door geef aan de prepare() functie één geheel.
 
Rob Doemaarwat

Rob Doemaarwat

30/09/2019 22:40:32
Quote Anchor link
* utf8mb4: Bij nieuwe databases (op zich ben ik nog niet tegen problemen aangelopen met plain utf8, maar ik zie dat steeds meer mensen emoji's in "normale" communicatie gaan gebruiken, dus kan het geen kwaad om er "klaar" voor te zijn).

* alles escapen: Ja, puur gemak. Ook bij teksten die je volledig zelf in de hand hebt, en waar nooooit een quootje in zal staan zul je zien dat er op den duur een quootje in komt ... (de "Kia Cee'd" was in mijn branche bijvoorbeeld een "leuke" eye opener).

* wrapper: Die gebruikt inderdaad PDO. Meestal heb je te maken met MySQL, maar soms komt er een ander "merk" voorbij (MS-SQL, Oracle), dus ik wil me niet vastpinnen op de mysqli extensie (en PDO en MySQL gaat prima).
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

//single row = assoc.array retourneren
$user = $db->single('select * from user where id = :id',['id' => 5]);
/* array(
  'id' => 5,
  'name' => 'Rob',
  'profile_id' => 21,
  ...
) */

//array met key = right.id en value = right.code

$rights = $db->record('
  select r.id,r.code
  from `profile_right` pr
    join `right` r on r.id = pr.right_id
  where pr.profile_id = :profile_id'
,
  $user //argumenten; hier zit dus veel meer in, maar in ieder geval 'profile_id'
);

?>


* samenstellen: Inderdaad, uiteindelijk houd je 1 stuk SQL over, maar omdat de inhoud en volgorde van de argumenten niet van belang is hoef je er niet zo panisch mee te doen.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php

$sql
= 'select * from items i';
$where = $args = [];

if($args['foo'] = $_GET['foo'] ?? null)
  $where[] = 'i.foo = :foo';
if($args['bar'] = $_GET['bar'] ?? null)
  $sql .= "\n  join `other` o on o.id = i.other_id and o.bar = :bar";
if($args['enz'] = $_GET['enz'] ?? null)
  $where[] = 'i.enz = :enz';

if($where)
  $sql .= "\nwhere " . implode("\n  and ",$where);

$items = $db->all($sql,$args);

?>
 
Thomas van den Heuvel

Thomas van den Heuvel

01/10/2019 00:31:08
Quote Anchor link
Ff wat Googlen :).

Quote:
utf8mb4 is aan PHP zijde volgens mij gewoon utf8

Waarschijnlijk bedoel je:
Quote:
utf8mb4 in MySQL is aan de PHP zijde "equivalent" aan UTF-8

UTF-8 karakters bestaan maximaal uit 4 octets en utf8mb4 biedt hier ruimte aan:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
> show character set where Charset like 'utf%';
+---------+------------------+--------------------+--------+
| Charset | Description      | Default collation  | Maxlen |
+---------+------------------+--------------------+--------+
| utf8    | UTF-8 Unicode    | utf8_general_ci    |      3 |
| utf8mb4 | UTF-8 Unicode    | utf8mb4_general_ci |      4 |
| utf16   | UTF-16 Unicode   | utf16_general_ci   |      4 |
| utf16le | UTF-16LE Unicode | utf16le_general_ci |      4 |
| utf32   | UTF-32 Unicode   | utf32_general_ci   |      4 |
+---------+------------------+--------------------+--------+

(maar let dus op het verschil tussen UTF-8 en utf8, deze zijn niet equivalent, het tweede artikel waar je naar linkt gebruikt deze twee termen nogal klakkeloos door elkaar...)

Dan bestaat er nog het idee dat VARCHAR nog rekent met bytes, maar vanaf MySQL versie 5 worden karakters gebruikt. Let wel op dat de totale mogelijke geheugenruimte voor VARCHAR vastligt (65.535 bytes). Dat bepaalt op zijn beurt hoeveel karakters deze (worst case) kan bevatten (ca. 16382).

Vervolgens heerst er nog steeds een misverstand over set_charset(). Je moet het als volgt zien. set_charset() is in wezen een contract tussen jouw PHP-applicatie en de database, die uit twee delen bestaat:
- enerzijds dien jij er zorg voor te dragen dat alle data die je vanuit je applicatie de database in schiet van deze voorgeschreven character encoding is, en
- anderzijds doet MySQL haar best om de data in de database in de voorgeschreven character encoding (terug) te serveren

Maar dit is niet noodzakelijkerwijs de character encoding van de gebruikte database-tabellen of -kolommen! Natuurlijk is het handiger dat alles qua character encoding in de pas loopt (en daar zou je dus ook naar moeten streven) maar dit is geen noodzakelijke voorwaarde. MySQL voert, zo goed en zo kwaad als dat kan uiteraard, zelf vertalingen uit tussen character encodingen als die ziet dat er een discrepantie bestaat tussen de definities en de waarde in set_charset(). En ja, als dat niet past kan dat resulteren in vraagtekens - karakters die niet ondersteund worden in de gewenste character encoding. Maar je zou dus prima een UTF-8 applicatie kunnen hebben die middels set_charset('utf8') communiceert met een sec latin1 database. latin1 is in zekere zin een "subset" van utf8 dus dit levert bij het uitlezen waarschijnlijk geen problemen op. Maar als je dus utf8 (of dus zelfs UTF-8) dingen probeert weg te schrijven in een latin1 database, dat is natuurlijk niet echt echt niet optimaal.

Quote:
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.

Heel simpel. Je wilt niet dat DATA als SQL geïnterpreteerd kan worden. Dat is namelijk de definitie van SQL-injectie. Alles wat je niet kunt escapen zou je via whitelists moeten laten verlopen (lijst van toegestane waarden). En dat valt eigenlijk gewoon onder validatie, die altijd, waar dat relevant is, plaats zou moeten vinden nog voordat je een query uitvoert. Als de validatie mislukt hoef je niet eens te proberen om een query te draaien en dat zou je dan dus ook nooit moeten doen.

Het eerste artikel waar je naar linkte gaat van simpel naar geavanceerd en legt uit wat de voordelen van prepared statements zijn. Maar hier kleven weer andere "nadelen" aan. De prepared statements variant van mysqli is wat mij betreft veel te "clunky".
http://fangorn.thijma.nl/images/phphulp/aint-nobody.png
Dan de PDO versie (waarbij de bovenstaande afbeelding weer min of meer van toepassing is). Dat lijkt allemaal simpel, een handjevol classes etc, maar de echte leercurve zit in de PDO_MYSQL driver, waar legio instellingen in zitten waar je vertrouwd mee dient te zijn. Daarnaast simuleert PDO standaard de "native" prepared statements voorziening die MySQL zelf heeft, dus dat zijn in wezen geen "echte" prepared statements. Wat op zich niet erg is, want de meeste queries herhaal je toch niet waarbij je 1x een template naar de database stuurt en vervolgens meerdere queries uitvoert met gebruikmaking van dat template.

De "gripes" die ik heb met PDO zijn als volgt:
- PDO is niet specifiek geschreven voor MySQLi, en als zodanig ook niet (out-of-the-box) geoptimaliseerd voor MySQL
- als je toch alleen maar van MySQL gebruik maakt in je applicatie, waarom zou je dan geen gebruik maken van een extensie die specifiek geschreven is voor MySQL (mysqli)
- debugging van queries; geen idee hoe dat gaat in mysqli+prepared statements / PDO? moet je dan je query log aanzetten en dan in je logs duiken om een (concrete) query op te snorren? f*ck that :)

Iedereen moet zelf maar weten wat ie gebruikt (choose your poison), maar met een eenvoudige wrapper om mysqli die werk uit handen neemt kom je echt al een heel eind. Wat je ook gebruikt, het is natuurlijk wel zaak dat je heel goed vertrouwd bent met de spelregels van de constructie die je gebruikt. En je kunt dan in principe altijd nog besluiten om deze wrapper te implementeren via PDO, of je gaat nog een stap verder en je gaat met Database Abstraction Layers aan de slag. Je kunt dan in principe steeds meer dingen doen op een abstract niveau, maar deze abstractie heeft ook een prijs (en mogelijk een snel afnemende meerwaarde). Voor een heleboel applicaties is die abstractie gewoon niet interessant (genoeg).
Gewijzigd op 01/10/2019 00:42:35 door Thomas van den Heuvel
 
Jelle Dnw

Jelle Dnw

01/10/2019 13:06:34
Quote Anchor link
Ik zie Doctrine in de titel maar niemand die het gebruik ervan aanmoedigd? Als er één goede ORM is out there, dan is het wel Doctrine. Hoef je zelf niet overbodige wrappers te schrijven of in te zitten om data te escapen al dan niet.

Mijn tip: gebruik gewoon een reeds bestaande ORM, bij voorkeur Doctrine (of Eloquent). Zitten een hoop zaken in die zeker interessant zijn voor je webapplicatie.
 
Frank Nietbelangrijk

Frank Nietbelangrijk

01/10/2019 18:28:41
Quote Anchor link
Rob, bedankt voor de toelichting. Dus voor de emoji's zou ik kunnen kiezen voor utf8mb4... Het verhaal van die max 180 karakters in een VARCHAR blijkt niet op te gaan nu ik naar aanleiding van Thomas zijn reactie er nog eens ingedoken ben. Blijkt weer eens dat internet vooral vooral vol staat met onwaarheden en te oude informatie die al achterhaald is...

Thomas bedankt voor je uitgebreide reactie.
Thomas van den Heuvel op 01/10/2019 00:31:08:
(maar let dus op het verschil tussen UTF-8 en utf8, deze zijn niet equivalent, het tweede artikel waar je naar linkt gebruikt deze twee termen nogal klakkeloos door elkaar...)

Deze begrijp ik even niet. UTF8 utf8 UTF-8 en utf-8... Waar zit het verschil in behalve dan in de hoofdletters en al dan geen minteken?

Thomas van den Heuvel op 01/10/2019 00:31:08:
Dan bestaat er nog het idee dat VARCHAR nog rekent met bytes, maar vanaf MySQL versie 5 worden karakters gebruikt. Let wel op dat de totale mogelijke geheugenruimte voor VARCHAR vastligt (65.535 bytes). Dat bepaalt op zijn beurt hoeveel karakters deze (worst case) kan bevatten (ca. 16382).

Hier lees ik weer iets nieuws waarvoor dank. Echter begrijp ik van stackoverflow dat de 65.535 bytes de maximale lengte is van alle kolommen bij elkaar. Een soort maximale regellengte dus.

Over de set_charset() functie binnen PHP zou je dus kunnen zeggen dat als je database is ingesteld utf8 of utf8mb4 je utf8 meegeeft als parameter:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
<?php mysqli_set_charset($con,"utf8"); ?>


Dat de mysqli prepared statements een beetje klungelig in elkaar steken dat is nu wel duidelijk.

Thomas van den Heuvel op 01/10/2019 00:31:08:
- debugging van queries; geen idee hoe dat gaat in mysqli+prepared statements / PDO? moet je dan je query log aanzetten en dan in je logs duiken om een (concrete) query op te snorren? f*ck that :)

In PDO kun je de exceptions natuurlijk zien als je ze niet opvangt. Bij mysqli blijf je telkens met onhandige if statements klooien zover ik het kan beoordelen. Of doel je hier op iets anders?

Jij gebruikt dus een eigen mysqli wrapper?

Jelle,
Fijn om te lezen dat er nog een Doctrine fan aanwezig is :-) Eloquent ken ik van naam maar ik heb er nog nooit naar gekeken. Doctrine daartegen ken ik ondertussen zeer goed. Dit komt denk ik vooral omdat ik graag met Symfony werk. Wel herken ik iets in het laatste stukje van Thomas zijn opmerking als we het over Doctrine hebben: Het feit dat je een prijs betaald voor een abstraction layer. Maar met alle voordelen daar tegenover vind ik Doctrine nog steeds erg fijn om mee te werken. Vooral omdat de data in een Entity (een class) wordt aangeleverd. En met doctrine ORM werk je erg makkelijk met prepared statements.
 
Thomas van den Heuvel

Thomas van den Heuvel

01/10/2019 21:34:02
Quote Anchor link
Quote:
UTF8 utf8 UTF-8 en utf-8... Waar zit het verschil in behalve dan in de hoofdletters en al dan geen minteken?

UTF-8 is een ISO-standaard, utf8 is een interpretatie (subset) hiervan specifiek voor MySQL. Ging niet zozeer over case maar meer over het minteken.

Neemt niet weg dat je de case ook overal consequent zou moeten gebruiken, zoals:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
<meta charset="UTF-8">

In een HTML-document maakt het niet zoveel uit, maar in andere gevallen (XML?) kan het mogelijk weer wel uitmaken. Als je hier eens op Googled zijn de resultaten min of meer "maakt niet uit / zou niet uit moeten maken, maar UTF-8 verdient de voorkeur boven utf-8". Als je verwarring kunt voorkomen door gewoon gebruik te maken van UTF-8 lijkt mij dat gratis winst.

Quote:
Een soort maximale regellengte dus.

Precies, en hoeveel karakters je hier (theoretisch) in kunt proppen hangt van het maximaal aantal bytes af waar een karakter uit opgebouwd is. Als je niet tegen deze restricties aan wilt of dreigt te hikken kun je beter een ander kolomtype gebruiken die deze harde bovengrens niet heeft (of iig veel ruimer is).

Quote:
In PDO kun je de exceptions natuurlijk zien als je ze niet opvangt. Bij mysqli blijf je telkens met onhandige if statements klooien zover ik het kan beoordelen. Of doel je hier op iets anders?

Ik bedoel meer: je kunt met prepared statements niet (of iig verre van makkelijk) direct zien welke query uiteindelijk wordt uitgevoerd. Misschien is dat inmiddels veranderd, maar prepared statements in mysqli ga ik niet gebruiken en in PDO moet (zou) je eigenlijk een heleboel (moeten) inregelen om dit intuïtief in MySQL te laten werken want zoals gezegd is PDO hier niet specifiek voor geschreven.

Quote:
Jij gebruikt dus een eigen mysqli wrapper?

Ja, en als ik de keuze heb zou ik deze ook gewoon altijd gebruiken. Omdat deze gewoon het meeste rechttoe rechtaan is.
 
- Ariën -
Beheerder

- Ariën -

01/10/2019 21:44:30
Quote Anchor link
@Thomas: Bereid om deze te delen?
Ik zie vaak wat nuttige code van je in het forum komen die naar mijn idee prima in de scripts-database past.
 
Thomas van den Heuvel

Thomas van den Heuvel

01/10/2019 22:07:11
Quote Anchor link
Deze staat in principe al een tijdje op PHPhulp. Heb deze ondertussen wel wat uitgebouwd en uitgebreid met het tracken van (specifieke) queries en een wat andere aanpak qua transacties maar deze is in grote lijnen hetzelfde. Kan de laatste versie desgewenst hier plaatsen als men interesse heeft.
 
- Ariën -
Beheerder

- Ariën -

01/10/2019 22:11:04
Quote Anchor link
Mja, ik kan het je niet verplichten, maar je snapt hopelijk wel dat het forum niet echt de plek is waar iemand een kant-en-klaar script vindt. Juist daarvoor hebben we de scripts-pagina. ;-)
Dus als je daar het script wilt plaatsen, graag :-)
 
Rob Doemaarwat

Rob Doemaarwat

01/10/2019 23:10:23
Quote Anchor link
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.
 
Thomas van den Heuvel

Thomas van den Heuvel

01/10/2019 23:30:47
Quote Anchor link
ORM is dus slecht schaalbaar?
 
Rob Doemaarwat

Rob Doemaarwat

02/10/2019 07:45:49
Quote Anchor link
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 :-) ).
 
Frank Nietbelangrijk

Frank Nietbelangrijk

02/10/2019 14:26:14
Quote Anchor link
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.
Gewijzigd op 02/10/2019 14:50:19 door Frank Nietbelangrijk
 
Rob Doemaarwat

Rob Doemaarwat

02/10/2019 16:24:30
Quote Anchor link
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>
 
Jelle Dnw

Jelle Dnw

02/10/2019 19:35:56
Quote Anchor link
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.
 
Rob Doemaarwat

Rob Doemaarwat

03/10/2019 00:03:13
Quote Anchor link
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:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
    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.
 
Frank Nietbelangrijk

Frank Nietbelangrijk

03/10/2019 18:39:35
Quote Anchor link
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...
 



Overzicht Reageren

 
 

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.