pad ophalen in OOP
Wat is eigenlijk de mooiste/handigste/meest gebruikte manier om in een OOP MVC framwork een pad op te halen?
Ik kan me grofweg 3 scenario's voorstellen en ik ben benieuwd wat volgens jullie (de OOP experts!) de beste manier is.
1) Je maakt een Paths class en die stop je in de Registry. Als je een path nodig hebt, haal je de Paths class op uit de Registry. Zoiets:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
$paths = new Paths();
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
$paths = new Paths();
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
2) Je gebruikt een singleton:
3) Je gebruikt een statische get method:
$path_library = Paths::get('library');
Wat vinden jullie? Misschien zijn er nog andere, betere methodes?
Alvast heel hartelijk dank voor jullie reacties!!!
Wat voor soort paden bedoel je? Kan je deze gewoon niet in je config bestand zetten?
Of snap ik je dan niet goed?
Niels
Stel je maakt een autoloader die een model moet inladen... dan heeft die autoloader een path naar de models nodig. Bijv. zoiets:
Het lijkt me netjes om die paden via een class te benaderen, maar welke mainer is dan de beste?
Maar wat veel mooier is in dit geval: autoloading!
Als je een goede autoloader gebruikt en deze goed configureert, zal je nooit meer een class-file hoeven te includen. Iedereen blij ;)
Dat autoloaden is geen probleem! Dat is ook hoe ik het nu doe... maar... in je autoload functie heb je ook een pad nodig, of misschien heb je ergens anders een pad nodig. Ik vind dat dan mooi als ik die uit een class kan ophalen... maar hoe?
Dat DI container heb ik wel eens doorgelezen, maar ik vind het vrij ingewikkeld. Vandaar dat ik dacht aan een van de 3 opties die ik zojuist noemde. Welke van die 3 is dan het beste? Wat zou jij doen?
(Mocht je uiteindelijk toch vinden dat zo'n DI container de enige juiste oplossing is, kun je dan via een klein codevoorbeeld laten zien hoe dat eruit zou zien op een zelfde manier zoals ik in de beginpost toepas?)
Maak een bestand autoload.php
Roep hierin de autoloader aan.
Simpel ;)
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'),
'Sensio' => __DIR__.'/../vendor/bundles',
'JMS' => __DIR__.'/../vendor/bundles',
'Doctrine' => __DIR__.'/../vendor/doctrine/lib',
));
$loader->registerPrefixes(array(
'Twig_Extensions_' => __DIR__.'/../vendor/twig-extensions/lib',
'Twig_' => __DIR__.'/../vendor/twig/lib',
));
use Symfony\Component\ClassLoader\UniversalClassLoader;
use Doctrine\Common\Annotations\AnnotationRegistry;
$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
'Symfony' => array(__DIR__.'/../vendor/symfony/src', __DIR__.'/../vendor/bundles'),
'Sensio' => __DIR__.'/../vendor/bundles',
'JMS' => __DIR__.'/../vendor/bundles',
'Doctrine' => __DIR__.'/../vendor/doctrine/lib',
));
$loader->registerPrefixes(array(
'Twig_Extensions_' => __DIR__.'/../vendor/twig-extensions/lib',
'Twig_' => __DIR__.'/../vendor/twig/lib',
));
Ik bedoelde eigenlijk of je dat DI gedoe kan uitleggen...
Dus zoiets als dit:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
$paths = new Paths();
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
$paths = new Paths();
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
alleen dan met DI...
Maar wellicht is dat te complex om 'even' uit te leggen.
Stel dat je idd een sort lib wil maken voor paden.
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
32
33
34
35
36
37
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
<?php
//Iets als dit
class PathRepository
{
protected $paths = array();
public function set($name, $path)
{
$this->paths[$name] = $path;
}
public function get($name)
{
return $this->paths[$name] ? $this->paths[$name] : null;
}
}
// Je kan daar nog wat pad gerelateerde dingen bij doen, maar daar gaat t nu even niet om.
// In eoa bootstrap file gaat dan: (vrij uitgebreid)
$c = new Container();
$c->set('pathRepository.config.file', __DIR__.'/config/paths.ini'); // Een parameter
$c->set('pathRepository.config', function($c) {
return parse_ini($c->get('pathRepository.config.file'));
}); // Een callback die bij aanroep de pathRepository.config.file parameter uit de container haalt en deze parsed en teruggeeft. Dit bestand is dan een ini bestand met
//name: path
$c->set('pathRepository', function($c) {
$repo = new PathRepository();
foreach($c->get('pathRepository.config') as $name => $path)
$repo->set($name, $path);
return $repo;
});
// Dit is je uiteindelijke service
// Er wordt dus een repository aangemaak en de entries uit de config worden ingesteld
// De repository wordt dan teruggegeven.
?>
//Iets als dit
class PathRepository
{
protected $paths = array();
public function set($name, $path)
{
$this->paths[$name] = $path;
}
public function get($name)
{
return $this->paths[$name] ? $this->paths[$name] : null;
}
}
// Je kan daar nog wat pad gerelateerde dingen bij doen, maar daar gaat t nu even niet om.
// In eoa bootstrap file gaat dan: (vrij uitgebreid)
$c = new Container();
$c->set('pathRepository.config.file', __DIR__.'/config/paths.ini'); // Een parameter
$c->set('pathRepository.config', function($c) {
return parse_ini($c->get('pathRepository.config.file'));
}); // Een callback die bij aanroep de pathRepository.config.file parameter uit de container haalt en deze parsed en teruggeeft. Dit bestand is dan een ini bestand met
//name: path
$c->set('pathRepository', function($c) {
$repo = new PathRepository();
foreach($c->get('pathRepository.config') as $name => $path)
$repo->set($name, $path);
return $repo;
});
// Dit is je uiteindelijke service
// Er wordt dus een repository aangemaak en de entries uit de config worden ingesteld
// De repository wordt dan teruggegeven.
?>
Bitte
Toevoeging op 19/04/2012 21:11:50:
Als je dat dan wil gebruiken doe je ergens:
Gewijzigd op 19/04/2012 22:38:23 door Pim -
Ehh, pim. Het lijkt me dat regel 22 niet kan?
Eh ja, uit de losse pols maakt iedereen fouten. Ik heb het verbeterd.
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
$config = 'pathsconfig.ini';
$paths = new Paths($config);
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
$config = 'pathsconfig.ini';
$paths = new Paths($config);
Registry::set('paths', $paths);
// pad ophalen:
$paths = Registry::get('paths');
$path_library = $paths->library;
?>
Dit is toch veel makkelijker? En komt volgens mij op hetzelfde neer?
(even ander offtopic vraagje... in jouw PathRepository class gebruikt je get() en set(). Gebruik je bewust niet de magic __set en __get?)
Het verschil zit hem in een paar dingen:
De container zoals ik hem net gebruikte is lazy: wat niet nodig is wordt niet geladen.
Ook is het modulair: als ik nou het configuratiebestandje zou willen aanpassen of de paden uit de database wil halen, hoef ik alleen maar repo.config.path te veranderen of repo.config aan te passen en kan ik de repo callback zelf met rust laten.
De container gebruikt geen globals, nu lijkt dat misschien niet heel belangrijk, maar als je wil werken met Unit tests of subrequests, kan dat heel handig zijn.
De container stimuleert Dependency Injection. Van elk object is duidelijk waar ze van af hangen. Als je dan het gedrag wil onderzoeken hoef je alleen maar de afhankelijkheden te bekijken, omdat de objecten niet 'out of the blue' informatie uit het register halen. Dit maakt debugging makkelijker.
Genoeg redenen?
Ik zie dat jij heel overtuigd bent van dat DI gebeuren en ik twijfel dus of ik het ook moet gaan toepassen. Die voordelen zijn ongetwijfeld erg nuttig... maar de logica vind ik nog behoorlijk lastig. Maar ik vrees dat dat gewoon aan mezelf ligt...
Ik ga toch maar eens een poging wagen om zo'n DI container te implementeren. Ik vraag me alleen af... in deze regel:
$c=>set('pathRepository.config.file', __DIR__.'/config/paths.ini');
Waarom gebruik jij puntjes in deze string 'pathRepository.config.file'. Heeft dat een bepaalde reden? Underscores lijkt me gebruikelijker? 'pathRepository_config_file'
Wat betreft de magic __set en __get... zijn er nog meer argumenten waarom dit niet goed is? Ik heb eens gehoord dat het trager is?
Gewijzigd op 24/04/2012 13:38:42 door Ozzie PHP
Wel of geen magic methods, dat mag je echt zelf weten. Doe gewoon wat je prettig lijkt. Of het sneller is of niet zou ik me iig 0 zogen maken.
Maar weet je heeel toevallig hoe de __set en __get werken?
Stel nu dat ik een class heb met daarin 20 functies en ik roep bijv. de __get functie aan, gaat ie dan eerst controleren of die 19 andere functies overeenkomen met de functienaam en roept ie daarna dan pas de __get functie aan? In dat geval kan ik me namelijk voorstellen dat het wel vertragend werkt...
Array zijn in PHP dictionaries en tegelijkertijd linked lists. De eerste zorgen voor snelle random access ($array[1010]) en de tweede voor snelle iteratie (foreach).
Objecten zijn ook dictionaries. Het opvragen van een parameter of methode (= random access) gebeurt dus razendsnel. Kijken of een param of methode wel of niet bestaat is dus ook heel snel en een fallback als __call() of __get() is dus helemaal niet (veel) langzamer. Er is natuurlijk wel wat extra overhead, maar ik zou me er niet al te veel zorgen over maken.
In het algemeen worden hier op PHPHulp.nl weinig applicaties gebouwd die echt razendsnel moeten zijn en mocht je dat toch willen, maakt een magische getter of setter geen enkel verschil (tenzij je met duizenden objecten gaat werken...)
Array zijn in PHP dictionaries en tegelijkertijd linked lists. De eerste zorgen voor snelle random access ($array[1010]) en de tweede voor snelle iteratie (foreach).
Kun je dit nader toelichten?
Wiki
Een dictionary is een datatype dat een key met een value koppelt. In PHP wordt dat geimplementeerd met een Hash Table. Dit is een array met de waarden. De index van de waarde is afhankelijk van de key. Een hashfunctie (zoals md5, maar dan veel sneller) mapt de key-string naar een numerieke index. In de array wordt dan bij deze index gekeken en de waarde opgezocht.
Maar dat zijn allemaal interne details. Wat ik probeerde te zeggen is dat dus random access heel snel is bij objecten en arrays :).
Een linked list is een aantal georderde elementen die steeds een verwijzing (pointer) bevatten naar het volgende element. Zo kan je makkelijk itereren door bij het eerste element te beginnen en zo de lijst te doorlopen tot je bij het laatste element komt. Een dictionary is een datatype dat een key met een value koppelt. In PHP wordt dat geimplementeerd met een Hash Table. Dit is een array met de waarden. De index van de waarde is afhankelijk van de key. Een hashfunctie (zoals md5, maar dan veel sneller) mapt de key-string naar een numerieke index. In de array wordt dan bij deze index gekeken en de waarde opgezocht.
Maar dat zijn allemaal interne details. Wat ik probeerde te zeggen is dat dus random access heel snel is bij objecten en arrays :).
Maareh... het ging er dus om of die __get en __set functies (merkbaar) langzamer zijn dan een "standaard" get() en set() functie. Maar als ik je goed begrijp is dat dus niet het geval?
TIP: Probeer artikelen / boeken te lezen die echt diep op PHP in gaan. Dan zul je zien, dat je andere problemen ook veel makkelijker gaat oplossen.
Gewijzigd op 24/04/2012 18:29:26 door Niels K