Validatie, tot hoe ver ga je?
Op dit moment ben ik bezig met een aantal classes en ik snap even niet tot in hoeverre ik moet valideren.
Stel je hebt een class die een ID en een naam verwachten als parameters.
Controleer je in de class dan ook of:
- Het ID een number is
- Deze number een integer is
- De naam een string is
- De string alleen uit letters bestaat
Of gaat dit te ver?
Ik hoop dat iemand me hier antwoord op kan geven.
Alvast bedankt!
Edit:
De parameters zijn geen user input maar worden door de programmeur (in dit geval ik) zelf ingevoerd.
Gewijzigd op 19/06/2014 10:00:28 door Lord Gaga
Ik kijk ook vaak de strlen na.
Het liefst 2 keer. Ook al is dat overbodig.
Gewijzigd op 19/06/2014 10:01:12 door Koen Hollander
- De parameter NIET null is (Want null is ook een object en null.nodeType geeft een error)
- De parameter een object is (Want node is een object)
- Het object een nodeType heeft (Als het object een nodeType heeft, is dit een node)
- Het object de nodeName 'CANVAS' heeft (Het is dus een canvas element)
Koen Hollander op 19/06/2014 09:59:57:
Het liefst 2 keer. Ook al is dat overbodig.
Hoe, en op welke manier?
Bij multi-byte karaktersets kan de stringlengte wel eens anders uitvallen dan je verwacht. Vandaar dat sommigen strlen() bijvoorbeeld door een mb_strlen() of strlen_utf8() vervangen.
Koen Hollander op 19/06/2014 09:59:57:
Het liefst 2 keer. Ook al is dat overbodig.
Dat lijkt me niet zinvol. Je zegt zelf al dat het overbodig is, dus dan heeft het geen enkele toegevoegde waarde.
Mijns insziens moet je vooral controleren wat "van buitenaf" komt en wat je als programmeur dus niet zelf in de hand hebt.
Op het moment dat een ID een class binnenkomt, dan moet die ID al kloppen. Het moet niet zo zijn dat de class die ID nog eens moet gaan controleren.
Jij als programmeur weet wat voor ID er verwacht wordt, dus op het moment dat je die zelf intypt, weet je dat het klopt: $foo = new Foo(12);
Echter, op het moment dat een bezoeker een ID opvraagt, zou je deze wel kunnen controleren. Als jij weet dat het ID een getal moet zijn, kun je controleren of het inderdaad een getal is.
Als je alles zou controleren, dan zou je alles wat je zelf doet ook moeten gaan controleren, en dat wordt een beetje belachelijk.
Stel dat ik in mijn code zet:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
$admin = new Admin();
$admin->setName('Ozzie PHP');
en dan vervolgens...
$validator = new AdminValidator();
$validator=>validate($admin); // dus nu ga ik controleren wat ik zelf heb ingetypt
?>
$admin = new Admin();
$admin->setName('Ozzie PHP');
en dan vervolgens...
$validator = new AdminValidator();
$validator=>validate($admin); // dus nu ga ik controleren wat ik zelf heb ingetypt
?>
Dit lijkt me nogal overkill. Nee, vooral dus de dingen controleren die van buitenaf komen.
Ozzie PHP op 19/06/2014 13:06:43:
Op het moment dat een ID een class binnenkomt, dan moet die ID al kloppen. Het moet niet zo zijn dat de class die ID nog eens moet gaan controleren.
Jij als programmeur weet wat voor ID er verwacht wordt, dus op het moment dat je die zelf intypt, weet je dat het klopt: $foo = new Foo(12);
Jij als programmeur weet wat voor ID er verwacht wordt, dus op het moment dat je die zelf intypt, weet je dat het klopt: $foo = new Foo(12);
Dan moet je toch nog ergens formaliseren wat een geldige ID is. De class Product weet zelf niet dat de ID bijvoorbeeld slechts een UNSIGNED SMALLINT is in de database en dus niet groter dan 65535 mag zien. Dat weet een class ProductMapper misschien wel, maar je wilt voor validaties niet afhankelijk zijn van exceptions van een mapper.
Omgekeerd kan de product-ID misschien een UNSIGNED BIGINT zijn. Dan heeft een class ProductMapper daar geen enkele moeite mee, maar loop je misschien ergens vast omdat PHP 18446744073709551615 niet als integer kan behappen.
Kortom: een apart data-object voor de product-ID zou dan niet verkeerd zijn.
Lijkt mij dat je dan enkel wanneer het gaat om user input vanuit een formulier of URL een validatie gaat toepassen. Het lijkt me niet de bedoeling dat je jezelf gaat controleren. Als ik als programmeur in mijn code ergens typ $id = 12, dan kun je er vanuitgaan dat dat klopt, en dat je niet hoeft te controleren of 12 inderdaad een getal is groter dan 0 en kleiner dan x. Dat is aan de programmeur.
Dan zit het alleen in jouw hoofd en is het nergens in de software terug te vinden. Wat je niet formaliseert door het in te bouwen, wordt niet gebruikt.
>> Als ik als programmeur in mijn code ergens typ $id = 12 [...]
Dat lijkt me een hoogst zeldzame uitzondering, want wanneer ga je nu een applicatie bouwen waar je zó specifiek een "hard coded" integer instelt? Dat zou dan op zijn minst een constante moeten zijn.
$site_admin->setName('Ward van der Put')
Dan ga ik niet controleren of die naam wel klopt.
Als je een schaalbaar systeem bouwt met een groeiende en veranderende database, dan zul je toch ergens moeten vastleggen wat een valide product-ID is. En dat wil je maar op één plaats doen, niet alleen omdat je het maar één keer wilt vastleggen, maar vooral omdat alle samenwerkende klassen dan exact dezelfde "definitie" van een product-ID hanteren.
Dat zou ik niet beperken tot validaties van externe input. Het systeem heeft meer "check & balances" nodig — ook intern.
Gewijzigd op 19/06/2014 14:17:45 door Ward van der Put
Maar dan zou je een aparte class moeten maken die valideert of het id klopt:
ProductID::validate($id)
zoiets dan?
Code (php)
Hiermee gebeurt het automatisch en kan niemand het dus nog vergeten of het verbouwen tot iets dat het systeem ondermijnt. We hebben één class ProductIdentifier die bepaalt wat een geldige product-ID is én alle klassen houden zich daar aan.
>> Maar met een ID is het lastig vind ik. ALs een ID niet klopt wordt ie simpelweg niet gevonden.
Dan kan ook. En gebeurt ook: het bestaat gewoon niet als een query 0 retourneert. Dat zegt dan echter meer iets over het bestaan van de ID, niet over de geldigheid van die ID.
Offtopic:
In het Groot Handboek Micro-optimalisatie zou je een hoofdstuk kunnen opnemen over redenen waarom je géén databaseverbinding moet openen als je op voorhand al weet dat iets toch niet gevonden zal worden in de database.
Gewijzigd op 19/06/2014 15:32:13 door Ward van der Put
Je maakt nu gebruik van een ProductIdentifier, maar die moet je toch ook actief aanroepen?
$product_identifier = new ProductIdentifier($id);
Waar zit dan het verschil?
En nu ga je dus vanuit de mapper een object dat enkel een ID met zich meedraagt meegeven aan het product? En dat object ga je dan weer als property instellen?
Is dat niet vreemd? Het Product moet gewoon een geldig ID krijgen. Jij maakt er nu een object om te zorgen dat het ID is gevalideerd, waarmee je de bevoegdheid van het valideren toch enigszins bij de Product class legt. Mijns insziens moet de ID van de Product class ook daadwerkelijk een ID zijn, en niet een "controle object".
Om vergelijkbare redenen kun je een class ProductIdentifier opvoeren, zodat iedereen en alle klassen het over hetzelfde heeft zodra een Product-ID nodig is.
Je hebt gelijk: ergens moet die $id worden aangemaakt. Maar zo heel veel werk is dat niet, terwijl je er veel voor terugkrijgt. Dit kan van alles betekenen:
$id = 12;
En dit kan echter maar één ding betekenen, voor de klasse zelf én voor alle klassen die de klasse gebruiken:
$id = new ProductIdentifier(12);
Gewijzigd op 19/06/2014 16:00:45 door Ward van der Put
Maar dat neemt niet weg dat het een handige oplossing zou kunnen zijn. Maar maak je dan voor ieder object wat een ID vereist een apart Identifier object? En als je dan die lijn even doortrekt...
Zou volgens jouw beredenering dus dit worden?
Correct? En idem voor e-mailadres, leeftijd enz. ?
In plaats van:
public function Foo(array $product_data)
krijg je:
public function Foo(ProductIdentifier $id)
Zo heel spannend is dat niet. In plaats van "het moet een array zijn" formaliseer je gewoon "het moet een Product-ID van de klasse ProductIdentifier zijn".
Op dit moment heb ik het volgende:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Client.prototype.setCanvas = function setCanvas(canvas)
{
try
{
if (Common.isNodeSpecific(canvas, 'canvas'))
{
this._canvas = canvas;
return true;
}
else
{
if (DEBUG_MODE)
{
console.error('Failed to set canvas: ' + canvas + ' is not a valid canvas element');
}
return false;
}
}
catch(error)
{
if (DEBUG_MODE)
{
console.error('Failed to set canvas');
console.error(error.stack);
}
return false;
}
};
{
try
{
if (Common.isNodeSpecific(canvas, 'canvas'))
{
this._canvas = canvas;
return true;
}
else
{
if (DEBUG_MODE)
{
console.error('Failed to set canvas: ' + canvas + ' is not a valid canvas element');
}
return false;
}
}
catch(error)
{
if (DEBUG_MODE)
{
console.error('Failed to set canvas');
console.error(error.stack);
}
return false;
}
};
Is doet een goede manier, of moet die hele validatie ergens anders?
Ergens vind ik namelijk nogal een overkill aan validatie, maar dat kan aan mij liggen.
Zou iemand me voor kunnen doen hoe ik dit het best aanpak?
Edit:
De Common.isNodeSpecific is de volgende functie:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Common.isNode = function isNode(input)
{
return !Common.isNull(input) && Common.isObject(input) && !!input.nodeType;
};
Common.isNodeSpecific = function isNodeSpecific(input, nodeName)
{
return Common.isNode(input) && input.nodeName.toLowerCase() === nodeName;
};
{
return !Common.isNull(input) && Common.isObject(input) && !!input.nodeType;
};
Common.isNodeSpecific = function isNodeSpecific(input, nodeName)
{
return Common.isNode(input) && input.nodeName.toLowerCase() === nodeName;
};
Gewijzigd op 19/06/2014 17:23:13 door Lord Gaga