een paar regels die ik volg zijn(voor PHP vooral, niet bewust in deze volgorde).
# 1 functie doet 1 ding en doet dat goed
# een class/functie is geheel voor zichzelf verantwoordelijk.
in PHP5 kun je private gebruiken maar in PHP4 is de regel gewoon nooit direct met de variable van een class werken. maak daar een simpele get/set method voor. ook andere methode's van de zelfde class kunnen die methode gebruiken om met class variable werken. dus i.p.v $this->fp gebruik je $this->get_fp();. op die manier kun je ook de naam van je variable aanpassen zonder dat de andere methode's hier iets van werken.
# check, check, double check.
naast dat je altijd moet kijken of de argumenten die een functie ontvangt wel van het juiste type moet je ook kijken of de inhoudt relevant is. dus een bijvoorbeeld een strpos functie controlleert niet alleen of
haystack een string is maar ook of
haystack wel enige bytes bevat. dus
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
| function strpos($haystack, $needle, $offset)
{
if(!is_string($haystack)){
trigger_error("Haystack is not an string but if it was an empty string i would't know.", E_USER_WARNING);
return false;
}
if((!is_string($haystack)) OR (strlen($haystack) < 1)){
trigger_error("Haystack is not an string or its an empty string.", E_USER_WARNING);
return false;
}
} |
# als je een object bijvoorbeeld als reference moet terug geven is het makkelijker een array terug te geven in plaats van de functie een return-by-refernce te maken. niet alleen zijn dan alle functie's homogeen en geven dus altijd kopieen terug ook maakt het het makkelijker om bijvoorbeeld extra info mee te geven. daarnaast is het zo gedaan: $return_array = array(0 => &$object, 1 => $debug); return $return_array. in PHP5 is dit geloof ik niet meer nodig maar het blijft een nette methode om objecten en andere reference's terug te geven.
# gebruik E_USER_WARNING en E_USER_NOTICE.
E_WARNING is bedoeld voor interne PHP functies. E_USER_WARNING is voor user functies. zelfde gaat op voor E_NOTICE en E_USER_NOTICE.
# geef boolean FALSE terug in als er een error was, TRUE of een waarde voor success.
door consistent FALSE terug te geven en een E_USER_WARNING error te geven wordt error-handling een heel stuk makkelijk omdat het in 1 opslag duidelijk is of een functie heeft gewerkt. vergeet niet met de === operator te checken dat je FALSE hebt en niet bijvoorbeeld de waarde 0 wat een success kan zijn. als FALSE een geldige return waarde kan zijn gebruik ik meestal NULL hoewel ik niet zeker weet hoe === NULL reageert wordt NULL wel gezien als een apart type geloof ik.
# vermijd type-juggeling.
hoewel dit een welkome functie is voor beginnend programmeurs is het een kwaal geest voor de meer complexere functies. regelmatige checks op type zoals in punt 3 voorkomen dat je bijvoorbeeld een float geeft aan een functie die een integer verwacht. door expliciet naar integer te casten voordat je de parameter doorgeeft voorkom je dit soort fouten een float wordt ge-trunked. een string wordt ge-interpeteerd en boolean's worden een 1 of een 0. de rest wordt dan 0. door in de functie niet alleen te kijken naar type maar ook inhoudt kun je dit soort onverwachte cast vroegtijdig stoppen en afhandelen.
# 1 class per file
omdat php classess niet kan opdelen in meedere files wordt het helemaal een ramp om meedere classess te defineeren in 1 enkele file.
# stop functies met soortgelijke functionaliteit in een class zonder constructor of anderre OO functionaliteit. dus geen $this gaan gebruiken. omdat PHP geen verschill maakt tussen een globale functie en een methode die statische wordt aangeroepen je deze functies in een class stoppen en zo krijg je een soort namespace effect waarbij je dus heel makkelijk een soort van liberies kunt gaan makken die je dan aanroept via (libery name)::(function name). op die manier vervuil je de globale namespace niet en hoe je geen prefix te bedenken voor je functies. daarnaast staat heel duidelijk in de manual dat de global namespace aan PHP is voorbehouden en PHP kan dus zo een functie toevoegen met dezelfde naam waarna je applicatie het dus niet meer doet.
# noem class bestanden 'classname.class.php' en liberies 'libery.lib.php'.
hoewel dit meer een persoonlijke voorkeur is doe je er goed aan om je bronbestanden duidelijk te verdelen in 'classes', 'liberies', 'functies' en 'direct uitvoerbaar' welke dus direct door php mogen worden geopened. de andere zijn bedoeld voor eenmalige inclusion met include_once.
# 1 include header voor je hele project.
zoals elke goede programmeur probeer je presentatie en functie zoveel mogelijk te scheiden. door 1 header te maken welke al je classes en functies include wordt het erg makkelijk om je gehele codebase in 1 keer te laden en voortebereiden.
# gebruik constanten waarvoor ze bedoeld zijn.
constanten zijn uitermate geschikt om globale configuratie's te regelen zoals de include directory of de standaard plaatjes directory. door gebruik te maken van een enkele include header kun je al je constants op 1 plek defineeren.
# het eerste wat je moet doen is je error_reporting op E_ALL zetten.
zet op de volgende regel alvast je productie reporting maar comment het uit zoals:
PHP:
1
2
| error_reporting(E_ALL);
//error_reporting(E_ALL ^ E_NOTICE ^ E_USER_NOTICE); |
# wees kort maar duidelijk met documentatie in de broncode.
geef bij elke class definitie een kort stukje text over wat de class moet doen en voeg bij elke functie een kort stukje text met wat de functie moet doen. deze documentatie is natuurlijk geen vervanging maar meer een bij fail-safe voor mensen die je code doorspitten. echte documentatie over hoe jou code werkt en wat welke functie/class doet moet natuurlijk in een documentatie bestand.
# definieer altijd al je variable voordat je begint met de werkelijk uitvoer van een functie. de de logische structuur van elke functie is:
1) check argumenten
2) defineer variable en initaliseerd deze met een default waarde.
3) voer de functie uit.
4) verzamel het resultaat en geeft dat terug.
# vermijd het gebruik van }else{ clausule's.
in plaats van een else te defineeren maak je van de IF self de else. dus je check of het tegenover gestelde, dat wat de else zou uitvoeren waar is. zoniet dan skip je de if en ga je verder. op die manier vermijd je onodig indenten en wordt je code een heel stuk duidelijker. hier een voorbeeld.
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
32
33
34
35
| // hoe het dus niet moet
$fp = fopen("filename.txt", "rb");
if($fp !== false){
$data = fread($fp, filesize($filename));
if(is_string($data)){
return $data;
}else{
trigger_error("Failed to read data from file-pointer.", E_USER_WARNING);
return false;
}
}else{
trigger_error("Failed to open file-pointer.", E_USER_WARNING);
return false;
}
// hoe het ook kan
$fp = fopen("filename.txt", "rb");
if($fp === false){
trigger_error("Failed to open the file-pointer.", E_USER_WARNING);
return false;
}
$data = fread($fp, filesize("filename.txt"));
if((!is_string($data)) OR (strlen($data) < 1)){
trigger_error("Failed to read data from file-pointer.", E_USER_WARNING);
return false;
}
if(!fclose($fp)){
trigger_error("Sluiten is wel zo netjes. ik geeft ik een notice omdat een warning soms wat overdreven is.", E_USER_NOTICE);
}
return $data; |
en je ziet dat het een veel schonere manier van omgaan met logische structuren is omdat de consiqentie wordt getest. natuurlijk werkt dit alleen met boolean logica dus waarbij als het het een niet is dan moet het wel het ander zijn.
ik zou zo nog wel even door kunnen gaan met 'good coding habbits' voor PHP. veel ervan geldt ook voor andere programmeer talen zoals C of Java. eigenlijk zouden we hier een stiky van moeten maken omdat veel van de problemen die we terug zien in PHP-topics zijn wel terug te lijden zijn naar 1 van deze adviezen. ook geeft ik toe dat sommige mischien wat overdreven zijn maar over het algemeen lijdt dit wel tot structureel nette code waar bug's makkelijk zijn te vinden aanpassingen vlot zijn gemaakt.