Dependency Injection Top PHP tutorial

Door Pim -, 8 jaar geleden, 23.539x bekeken

Een tutorial over wat Dependency Injection is, hoe je het gebruikt, wat Dependency Injection Containers zijn en hoe de mijne eruit ziet.

Gesponsorde koppelingen

Inhoudsopgave

  1. Inleiding
  2. Dependency Injection
  3. Dependency Injection Container
  4. Pcms container in opbouw - 1
  5. Pcms container in opbouw - 2
  6. De Pcms container
  7. Conclusie

 

Er zijn 39 reacties op 'Dependency injection'

PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Wouter J
Wouter J
8 jaar geleden
 
Goede tutorial! Erg goed en duidelijk uitgelegd, genoeg code voorbeelden en comments. Precies zoals ik van je gewent ben!

Ik kijk wel uit naar een Unit testing tutorial.

Ik heb wel een heel klein typfoutje ontdekt:
Pcms (PIM cms :)) Container in opbouw -1
1e codeblok - regel 15 > \ voor InvalidArgumentException moet weg
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
Graag gedaan :)

Die \ hoort daar. In de laatste versie zit de container in de namespace Pcms. Om klasses uit de 'hoofdnamespace' aan te spreken moet er een backslash voor.

En het koekje is voor jou ;)
Kay Kay
Kay Kay
8 jaar geleden
 
0 +1 -0 -1
Waarom krijg ik email dat ik hier op gereageerd zou hebben, bij elke nieuwe reactie?
Jelle -
Jelle -
8 jaar geleden
 
0 +1 -0 -1
Interessante tutorial! Ik heb alleen nog een klein vraagje over het volgende (weet niet zeker of ik het helemaal begrijp):

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
22
23
24
25
26
27
28
29
<?php
$c
= new Container();
// Stel de parameters in
$c->set('mailer.username', 'foo');
$c->set('mailer.password', 'bar');
$c->set('mailer.class', 'Zend_Mail');

// Stel de transport service in
$c->set('mailer.transport', function($c) {
    return new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
      'auth'     => 'login',
      'username' => $c->get('mailer.username'),
      'password' => $c->get('mailer.password'),
      'ssl'      => 'ssl',
      'port'     => 465,
    ));
});


// Stel de mailer service in
$c->set('mailer.transport', function($c) {
    $class = $c->get('mailer.class');
    $mailer = new $class();
    $mailer->setDefaultTransport($c->get('mailer.transport'));
    return $mailer;
});


// De mailer roep je dan zo aan:
$mailer = $c->get('mailer');
?>


Als ik het goed begrijp denk ik dat je op regel 20 het volgende wou doen: $c->set('mailer' , func.....}); (aangezien je anders mailer.transport overschrijft)

Verder moet ik zeggen dat het me wel weer mooi aan het denken gezet, kan ik mijn structuur weer een stukje mooier maken :)
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
Je hebt gelijk. Bedankt.
Niels K
Niels K
8 jaar geleden
 
0 +1 -0 -1
Leuke / Mooie tut Pim!

Paar kleine dingen. Ik mis in de eerste voorbeelden de 'Method Visibility' ? Daarnaast, is het niet leuk / handig / makkelijk dat je in de container de magic functies __get, __set implementeert?

Over de (nieuwe) tutorials.

Wat dacht je van allebei? :-)
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
Method visibility is opgelost.
Een nadeel van get en set is dat je er geen derde parameter mee kan geven (shared). Ik ben er eigenlijk niet zo'n voorstander van... Ook is het vaak onduidelijk in API docs en voor IDEs. Pimple gebruikt ArrayAccess, dat vind ik nog net iets mooier.

Allebei... Wie weet ;)
Wouter J
Wouter J
8 jaar geleden
 
0 +1 -0 -1
Even kijken of ik het laatste gedeelte begrijp. Een Service Container is dus eigenlijk gewoon een Registery class?
Pim -
Pim -
8 jaar geleden
 
1 +1 -0 -1
Nee. Niet helemaal. Er zijn een paar verschillen.
- De service container is 'lazy loading'. De services worden pas bij een verzoek gemaakt.
- De service container is geen losse verzameling objecten, maar een samenhangend geheel waarbij de services dmv dependency injection aan elkaar hanen.
- De service container kan ook configuratie/parameters bevatten, de meeste registries niet.
- De service container is veel cooler :)
Wouter J
Wouter J
8 jaar geleden
 
0 +1 -0 -1
Pim -:
- De service container is 'lazy loading'. De services worden pas bij een verzoek gemaakt.

Bedoel je hiermee dat een functie pas wordt aangeroepen in Container::get()?
Een registery class stored alleen maar dingen toch, als je er een functie instopt moet je die nog zelf aanroepen na Registery::get()? Of heb ik het nou helemaal mis, ben pas begonnen met design patterns...
Pim -
Pim -
8 jaar geleden
 
1 +1 -0 -1
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
$mailer
= new Mailer();
$registry->set('mailer', $mailer);
$container->set('mailer', function () {
return new Mailer();
});

?>

Stel nou dat het heel ingewikkeld is om een Mailer object aan te maken. Bij de registry wordt dat gedaan tijden de configuratie en het zal dus altijd gebeuren, of het nou gebruikt wordt of niet.
Bij de container wordt de mailer pas aangemaakt bij de eerste (in het geval van een gedeelde service) aanroep. Als je het niet gebruikt wordt het mailer object dus ook niet aangemaakt.
Pim -
Pim -
8 jaar geleden
 
1 +1 -0 -1
Bedenk trouwens dat een service container een vorm van een registry is en een registry een service container kan zijn, als het objecten opbouwt mbv dependency injection.
Kees Schepers
kees Schepers
8 jaar geleden
 
0 +1 -0 -1
Het heeft ook wat weg van het singleton pattern?, hoewel dat in Unit Testing land uit den boze is..
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
Nee, niet echt. De service container doet slechts dependency injection voor je. De services zelf krijgen dus netjes hun dependencies binnen en zo hoef je geen lelijke globals te gebruiken en kunnen de services dus ook goed getest worden.

De container zélf kan natuurlijk een singleton zijn, maar dat hoeft absoluut niet en dat raad ik dus niemand aan. Volgens mij is een goede maatstaf dat allen controllers (in een MVC omgeving) over de container beschikken. De rest krijgt zijn dependencies van de container, zonder weet te hebben ervan.

Of begrijp ik niet helemaal wat je bedoelt?
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
Let wel, dit is alleen een hele simpele variant van een container. Hetgeen wat iets vast houdt en geen dependency injection.

Dependency injection injecteerd de juiste objecten / variabelen in een class zie hier onder

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
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Service1
{

    protected $_dataLaag;

    public function __construct(IDataLaag $dataLaag)
    {
        $this->_dataLaag = $dataLaag    
    }
}

interface IDatalaag
{

}

interface IDatabase
{

}

class DataLaag1 implemenets IDataLaag
{
    protected $_database;

    public function __construct(IDatabase $database)
    {
        $this->_database = $database;        
    }
}

class DataLaag2 implemenets IDataLaag
{
    protected $_database;

    public function __construct(IDatabase $database)
    {
        $this->_database = $database;        
    }
}

class FileDatabase implements IDatabase
{

}

class SqlDatabase implemenets IDatebase
{

}

$container->Bind('IDatabase')->To('SqlDatabase');
$container->Bind('IDatalaag')->To('DataLaag1');

$container->Resolve('IDatalaag');  //  datalaag1 + sqldatabase in datalaag1 constructor

$container->Bind('IDatabase')->To('FileDatabase');
$container->Bind('IDatalaag')->To('DataLaag2');

$container->Resolve('IDatalaag');  //  datalaag2 + filedatabase in datalaag2 constructor


Omdat je alles met interfaces aan elkaar hangt heb je geen harde koppeling en kan je elke class los testen.

Ik raad juist wel mensen aan om een singleton voor container te hebben, sterker nog het zou raar zijn als je twee containers hebt voor je applicatie.

Wat deze tutorial mist zijn:
Dependency resolver,
Class resolver,
Uitgebreidere container: hoe injecteer je bijvoorbeeld extra variabelen of stel je in dat een class als singleton moet gedragen.
Kees Schepers
kees Schepers
8 jaar geleden
 
0 +1 -0 -1
Wat ik met het Singelton gebeuren bedoel is dat Singleton ook een beetje lazy-loading is. Het object wordt pas echt aangemaakt als het nodig is. Verder niets. Zend Framework maakt nog geen gebruik van DI als in Service Containers e.d. maar ZF2 geloof ik wel, weet het niet zeker. Maargoed op dit moment doe ik een freelance opdracht waar ze dingen in SF2 gaan bouwen dus wie weet :)
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
@daan
'design by contract' kan je toch gewoon met een strong typed argument bij injectie method afdwingen?
En natuurlijk is deze container niet zo uitgebreid als mogelijk is, maar het is wel, net zoals bijvoorbeeld pimple en al helemaal met die configure methode die ik erbij bedacht heb, een volledig functionerende container.
Ook ondersteund de container gedeelde services, zoals ik dat beschreven heb. Waarom zou je dan nog (lelijke en ontestbare) singletons nodig hebben?
Hetzelfde geldt voor een singleton container. Als je dat gebruikt doe je je testbaarheid bij slordig gebruik al snel te niet en waarom zou je het voor jezelf onmogelijk maken twee containers te draaien (voor een test tussen twee chat clients bijvoorbeeld) als dat helemaal niet nodig is?

Als je me wil helpen de container tut uit te breiden tot sf2 dic formaat, heel graag, maar anders snap ik je kritiek niet echt...
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
@pim,

Wat jij beschrijft is geen dependency injection maar de titel heet wel zo. Als je dat niet snapt weet ik het ook niet meer, je injecteerd namelijk nergens een dependency maar bouwt alleen een simpele container.

Ik werk bijvoorbeeld ook nooit met harde implementaties behalve in mijn domain.

Je houdt bijvoorbeeld bij de container ook niet rekening met dat het geen wat je op vraagt ook weer dependencies heeft. Deze wil je niet, zoals jij doet elke keer zelf injecteren maar gewoon 1 keer instellen.

Sinds wanneer zijn singletons lelijk en onhandig? Een settings object wil je niet twee keer laden, zo zijn er nog vele andere voorbeelden.

Een class als singleton instellen, betekend niet meteen dat deze ontestbaar is. Bij mijn DI container zeg ik gewoon ->AsSingleton(), zo zorg ik er voor dat elke class zich als singleton kan gedragen zolang de DI container maar gebruikt wordt.


In een test gebruik je geen dependency injection dus dat argument gaat niet op maar dat snap je zelf ook wel.

Probeer je te verdiepen in patterns als je daar een tutorial voor schrijft. En kijk ook naar andere talen: java, c#, ruby.

Overigens ben ik geen voorstander van symfony of zend.
Kees Schepers
kees Schepers
8 jaar geleden
 
0 +1 -0 -1
@Daan, ik denk dat jij het niet helemaal snapt? Met testen bedoelen we unit testing. Dan wordt het lastig testen met singletons. Als je dit leest begrijp je wel waarom: http://sebastian-bergmann.de/archives/882-Testing-Code-That-Uses-Singletons.html

Dat je geen voorstander bent van Zend of Symfony verbaast me wel, je schrijft zelf een framework ofzo? Of je bent fan van een ander framework?
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
@kees vergis je niet ik snap heel erg goed waar jullie het over hebben.
Het is hoe je de singleton zelf in regelt he, op de manier zoals hun dat doen gaat dat natuurlijk niet werken dat is niet meer dan logisch lijkt me?

Maar als je het instelt op de DI container heb je dit probleem niet, overigens kan je een singleton ook weer injecteren.

Ik vind de frameworks te groot, ik hou liever van iets kleins wat in een paar regels te snappen is als je de bekende patterns kent bijvoorbeeld: MVC, Ioc (Inversion Of Control, waar DI gebruikt wordt), Domain driven design, Repository pattern etc...

Ik maak wel gebruik van delen van het Zend Framework maar dat is meer hun libraries bijvoorbeeld Soap Server WSDL generator is erg sterk.
Kees Schepers
kees Schepers
8 jaar geleden
 
0 +1 -0 -1
Dat wordt ook uitgelegd in het artikel Daan, dat het met een DI container wel te testen is.

Dat je frameworks groot vindt is een beetje een raar argument, wat maakt diskspace of grote nou uit? En groot als in zwaar zou ook een loos argument zijn omdat alleen dingen geladen worden die worden gebruikt.

Ik weet niet of je aan grote projecten werkt maar in mijn ogen als je dat zonder een goed framework doet snijd je zelf (al-dan-niet later) in je vingers.
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
Kees ik werk alleen maar aan grote projecten met main focus op architectuur. Je hoeft me daar dus niks over te vertellen en nee nog nooit in mn vingers gesneden zolang architectuur goed is heb ik geen zend of symfony nodig.
Kees Schepers
kees Schepers
8 jaar geleden
 
0 +1 -0 -1
Bijzonder, ik werk namelijk ook alleen aan grootschalige projecten en houdt me ook met name bezig met architectuur (speel daarbij ook een consultancy rol) en ik denk dat daar open-source frameworks in meerdere opzichten een essientieel belang vormen voor bedrijven.

Maargoed, ieder zo zijn eigen keuzes ondanks ik me niet in jouw beweging kan vinden.
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
@daan,
Sorry, maar ik snap er niet veel van. Of je nou een framework gebruikt of niet maakt niet zo veel uit, maar wat klopt er nou niet aan mijn tut?
In het eerste hoofdstuk introduceer ik het DI patroon. Dat patroon is simpel: het injecteren van afhankelijkheden ipv ze ter plekke instantiëren. Niet meer, niet minder. Je kan dan met interfaces werken, maar dat is niet de essentie, hooguit een nuttige toevoeging.

Mijn container heeft weinig magie. De injecties doe je gewoon zelf binnen de definitie van de service. Hierdoor moet je meer zelf doen, maar blijft je container zeer simpel, flexibel en kan het elke vorm van di aan. Je schrijft de injectie code immers zelf. De definitie wordt meer 'verbose' (vertaling?), maar hierdoor is het bouwen van de container wel eenvoudig en heel overzichtelijk. Dat was toch precies wat je graag hebt, zo zei je zelf?

En wat er mis is met een static container: je beperkt jezelf. Stel dat je een interactie tussen twee clients wil simuleren en functioneel wil unit testen, bijvoorbeeld in een chat app. Normaliter hebben die elk een eigen request en dus een eigen container. In de test wil je dat dus ook.
Dit lijkt misschien een onwaarschijnlijke situatie, maar het laat wel zien dat statics de testbaarheid kunnen verminderen. En wat is trouwens het verschil tussen een static container en een traditionele global var, waarvan we het allemaal eens zijn dat dat niet mooi is?
En over static services: wat is het verschil met mijn gedeelde services, afgezien van dat de deling niet global is, maar container gebonden, wat zoals ik net zei alleen maar voordelig is?

Je zegt verder dat services geen andere services kunnen aanroepen, maar dat is toch precies wat ik in mijn voorbeeld doe? De mailer.transport service is een dependency die voor meerdere services gebruikt kan worden.

En tot slot: de tutorial, en vooral de eerste twee delen, is gebaseerd op een blogpost van Fabien Potencier, de lead dev van een van de twee grote 'enterprise' frameworks: symfony. Ook is pimple, de container waarop ik de mijne heb gebaseerd, de kern van silex, een niet onbekend micro framework.
Zo erg mis kan ik het dan toch niet hebben?

En als laatste: di wordt niet bij testing gebruikt? Wtf?
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
@pim, dat je er niet veel van snapt dat zijn we beide eens. Het lijkt me verstandig om gewoon eens wat boeken te lezen over patterns. Voordat je uberhaupt zon tutorial post, een DI container heeft geen magie, die heeft de Resolver al is dat ook geen magie want daar stel je ook zelf op in.

Dat je meer vrijheid hebt slaat natuurlijk nergens op. Op jouw manier ga ik voor elke class alle dependencies injecteren. Das leuk voor 1 class maar niet voor 50, laat staan als er iets veranderd.

Het maakt er ook niet overzichtelijker op dat is een ding wat ik met je eens ben, voor 2 classes is leuk maar niet voor 50.

Hier uit blijkt wel dat je de patterns niet goed onder de knie hebt. Je hebt over Unit testen terwijl je intergratie tests bedoelt. Bij unit tests test je alleen de functionaliteit van 1 class niet interactie tussen twee clients.

Uiteraard kan je mailer service gebruikt worden, maar je opzet is gewoon niet geschikt voor grote applicaties.

Bijvoorbeeld meerlaagse dependency moet je zelf regelen terwijl je dit juist met een DI container uit handen wilt geven.

Anders gebruik je gewoon het factory pattern wat je eigenlijk nu doet, je container is 1 grote factory.

Je zult vast niet helemaal mis hebben, maar essenieele dingen kunnen toch beter. Wat ik ook zei kijk ook naar andere talen, PHP is nou niet echt een object georrienteerde taal.

Ik mag hopen dat je een DI container niet gebruikt bij het unit testen anders sla je de plak echt helemaal mis.
Daan l
Daan l
8 jaar geleden
 
0 +1 -0 -1
@kees

Ik maak wel gebruik van opensource dingen, eigenlijk alleen maar. Alleen hou niet van grote frameworks is gewoon persoonlijk voorkeur.
Pim -
Pim -
8 jaar geleden
 
1 +1 -0 -1
Natuurlijk is het zo dat een container complexer kan, maar waarom zou dat noodzakelijk zijn? Nu is de container klein en overzichtelijk. Onthoud, dit is een tutorial, geen script. Ik probeer hier uit te leggen hoe je de simpelst mogelijke container maakt, niet hoe je een full-featured container maakt. Deze container is echter wel degelijk volledig en daarom in mijn ogen zeer geschikt voor een tutorial.
Jij hebt het voortdurend over een bepaalde implementatie van de container, maar wat is het in essentie anders dan een factory die aan DI doet?

En zoals ik zei, parafraseer ik iemand die weet waar hij het over heeft. Stellen dat hij geen idee heeft waar hij het over heeft, is een beetje zwak zonder een hoop argumentatie.
Pim -
Pim -
8 jaar geleden
 
0 +1 -0 -1
Om even wat referentie te gebruiken: lees dit eens. In dit artikel wordt de term 'dependency injection' voor het eerst geïntroduceerd en zijn omschrijving komt toch wel sterk overeen met de manier zoals ik het van Fabien Potencier heb 'gejat'.
De container die Fowler beschrijft lijkt inderdaad min of meer op datgeen jij omschrijft, maar is qua functionaliteit hetzelfde als Pimple of bovenstaande container. Slechts de vorm is anders. Deze containers bouwen elk gebruikmakend van DI services op zoals een factory.
Pieter Jansen
Pieter Jansen
8 jaar geleden
 
0 +1 -0 -1
Ik vind het een leuk concept, DI. Doet me sterk denken aan Java`s Service containers. Maar ik mis hier wel de daadwerkelijke DI zelf. Dit is meer een container dan dat er daadwerkelijk DI plaatsvindt. Desalniettemin een erg leuke klasse. Ik zou hem persoonlijk als Singleton gebruiken, dat scheelt heel wat rondgooien van overerven en includen. Althans, laat ik het anders uitleggen, ik zou een abstracte klasse maken, vandaaruit de hoofdcontainer welke een singleton is en een losse container klasse welke vrij te gebruiken is. De hoofdcontainer is dan wel hanteerbaar. Ik zou ook containers als return willen zien, sub containers voor bijvoorbeeld mail. Hierdoor heb je tenminste ook onderscheid in alle dependency`s voor services qua mail etc.

Ik heb wel nog 1 opmerking, de set() methode kijkt niet of er al een key bestaat met x waarde, hierdoor kun je sleutels overschrijven wat misschien niet de bedoeling is? Misschien een exception gooien of een warning genereren dat er al een sleutel als $key bestaat?

Los daarvan vond ik het een erg interessante tutorial, doet me goed dat er eindelijk weer eens goede tutorials te vinden zijn hier :)

ps. is niet om te zeiken, DI wordt namelijk prima uitgelegd, heb het artikel van Fabien ook gelezen en je hebt het prima weten te verwoorden naar bruikbare, goed-te-begrijpen Nederlands.
Pim -
Pim -
8 jaar geleden
 
1 +1 -0 -1
Je hebt gelijk in zoverre dat je zelf de DI moet implementeren en dat de container dat niet voor je doet. Daarom is idd service container misschien een betere naam, ookal kan je met de zelf-gespecificeerde factories zeker aan DI doen.

Dat er geen exception wordt gegooid is bewust. Op die manier is het mogelijk core-services aan te passen en dat lijkt me vrij noodzakelijk in een modulaire applicatie.

Maar bedankt voor je feedback, die wordt erg op prijs gesteld :-)
Ivar Koster
ivar Koster
7 jaar geleden
 
0 +1 -0 -1
Is dit slechts een vertaling van http://fabien.potencier.org/article/11/what-is-dependency-injection ? Of gaat het verder? Heb de 1e en 2e pagina gelzen... maar heb het idee dat ik niets nieuws ga leren dan.
Daan l
Daan l
7 jaar geleden
 
Sterker nog je leert helemaal niks nieuws en dit is geen dependency injection maar een simpele container en heeft niks met het injecteren van dependencies te maken.
Ozzie PHP
Ozzie PHP
7 jaar geleden
 
0 +1 -0 -1
@iVar: zoals Pim (de schrijver van de tutorial) in de inleiding al schrijft...

"De tutorial is gebaseerd op deze van Fabien Potencier...". Je kunt ook even verder lezen dan pagina 1 en 2 dan had je je eigen vraag kunnen beantwoorden.

@Daan: typisch hoe jij er als de kippen bij bent om hier een negatieve reactie te plaatsen, terwijl je voor de rest vrijwel nauwelijks actief bent op het forum. Je laat je wel kennen. Gelukkig zijn er andere mensen die wel iets kunnen leren van deze tutorial en die een wat positievere kijk op het leven hebben.
Ivar Koster
ivar Koster
7 jaar geleden
 
0 +1 -0 -1
@Ozzie Geïnspireerd is 1 ding, maar het lijkt wel letterlijk vertaald.
Wouter J
Wouter J
7 jaar geleden
 
0 +1 -0 -1
Dat is het ook, daarom heet het ook niet geïnspireerd maar gebaseerd.. :) Overigens wordt het in hoofdstuk 4, waar hij over Pcms's controller praat, wel anders dan Fabien deed.

@daan, jammer.
Ivar Koster
ivar Koster
7 jaar geleden
 
0 +1 -0 -1
@wouter Dank je. Bij pagina 2 had ik namelijk al het idee dat het verspilde tijd was. Maar wellicht kan ik er nog wat uithalen.
Pim -
Pim -
7 jaar geleden
 
Interessante reacties. Ik heb het idee dat het mij wordt aangerekend dat ik een tutorial heb geschreven/vertaald waar sommige mensen niets van kunnen leren.
Deze site wordt voornamelijk bevolkt door mensen die beginners zijn en hen hoop ik iets te leren. Vind je deze tekst niet interessant, lees hem dan vooral niet.

Wat betreft het inhoudelijke punt, Daan, ik hoor graag jouw omschrijving van het begrip Dependency Injection. Ik heb het idee dat ik het op pagina 2 redelijk heb omschreven.

En tot slot: ja, dit is een vrije vertaling met een kleine toevoeging. Op dit moment leer ik vooral uit Engelse teksten en ik denk ook dat beheersing van de Engelse taal heel handig is bij het leren programmeren. Dit is echter een Nederlandse site, die mij ooit veel geleerd heeft. Daar wou ik iets voor terugdoen en daarom heb ik deze tekst vertaald/geschreven en dat heb ik duidelijk vermeld. Wat is daar mis mee?
Ozzie PHP
Ozzie PHP
7 jaar geleden
 
0 +1 -0 -1
Pim, er is helemaal niks mee! Je hoeft jezelf absoluut niks te verwijten. Ik ben blij met deze tutorial.

Mensen die iets te zeuren hebben, zul je altijd blijven houden. Trek er je niks van aan.
PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Niels K
Niels K
7 jaar geleden
 
0 +1 -0 -1
Inderdaad Ozzie helemaal gelijk.

Wanneer je commentaar hebt op Pims verhaal, beargumenteer je stelling zodat Pim, en andere mensen, weten wat er (misschien) verbeterd kan worden.

Niels

Om te reageren heb je een account nodig en je moet ingelogd zijn.

 
 

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.