argumenten in construct
Ik ben aan het knutselen aan m'n service container en nu loop ik tegen het volgende aan:
Stel ik heb een config bestand met daarin de service 'foo_bar':
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
arguments:
bar: abc
foo: xyz
services:
foo_bar:
class : FooBar
arguments: [bar, foo]
bar: abc
foo: xyz
services:
foo_bar:
class : FooBar
arguments: [bar, foo]
De class foo verwacht zoals je kunt zien 2 argumenten, namelijk 'bar' en 'foo'.
Normaal gesproken als ik nu een object zou aanmaken, dan doe ik dat zo:
$foo_bar = new FooBar($bar, $foo);
Maar ik loop nu tegen het volgende aan:
Als ik de service foo_bar vanuit het configuratiebestand inlees, dan staan de 2 argumenten 'bar' en 'foo' in een array. Hoe moet ik die 2 argumenten vanuit de array nu meegeven aan de class FooBar?
Als ik dit doe $this->container->get('foo_bar'); dan zou er dus zoiets moeten gebeuren:
return new FooBar($arguments[0], $arguments[1]);
Maar nu is mijn vraag hoe ik die 2 argumenten tussen de 2 haakjes krijg. Nu zijn het 2 argumenten, maar het kunnen er natuurlijk ook meer of minder zijn. Iemand een idee?
Maar begrijp ik nu goed dat je dan voor het aanmaken van een object altijd die Reflection class gebruikt door middel van $reflecton->newInstance of $reflection->newInstanceArgs($arguments)? En heeft dat dan hetzelfde effect als wanneer ik $object = new Object(arguments) zou doen?
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
<?php
public function instantiate( $classname, array $arguments = array(), $error = '' ){
try{
$reflection = new ReflectionClass( $classname );
$constructor = $reflection->getConstructor();
if ( $constructor === null ){
//no constructor, instantiate
$obj = $reflection->newInstance();
} else {
//there is a constructor, how the object is instantiated depends on the
//number of parameters and number of required parameters.
if ( $constructor->getNumberOfParameters() == 0 ){
$obj = $reflection->newInstance();
} elseif( $constructor->getNumberOfRequiredParameters() <= count( $arguments ) ){
$obj = $reflection->newInstanceArgs( $arguments );
} else {
//the number of given parameters doesn't match the number of needed
//parameters, throw an exception because in this case no instance can
//be created.
throw new Exception();
}
}
//return the object
return $obj;
} catch( Exception $e ){
throw new $this->exceptionClass( $error );
}
}
?>
public function instantiate( $classname, array $arguments = array(), $error = '' ){
try{
$reflection = new ReflectionClass( $classname );
$constructor = $reflection->getConstructor();
if ( $constructor === null ){
//no constructor, instantiate
$obj = $reflection->newInstance();
} else {
//there is a constructor, how the object is instantiated depends on the
//number of parameters and number of required parameters.
if ( $constructor->getNumberOfParameters() == 0 ){
$obj = $reflection->newInstance();
} elseif( $constructor->getNumberOfRequiredParameters() <= count( $arguments ) ){
$obj = $reflection->newInstanceArgs( $arguments );
} else {
//the number of given parameters doesn't match the number of needed
//parameters, throw an exception because in this case no instance can
//be created.
throw new Exception();
}
}
//return the object
return $obj;
} catch( Exception $e ){
throw new $this->exceptionClass( $error );
}
}
?>
Zoals je ziet maak ik het object nu aan via die reflection class die via newInstance (of newInstanceArgs) een object teruggeeft van het type dat ik heb aangevraagd. Dat obj wordt weer geretourneerd uit mijn Instantiator_Class en uiteindelijk heb ik in mijn applicatie dus het object dat ik nodig heb.
Daar kan ik wel mee uit de voeten!
Ik denk dat ik zelf iets minder controles ga inbouwen... ik ben zelf de enige die het framework gebruikt. Als ik een verkeerd aantal argumenten opgeef dan zie ik het direct omdat er een PHP foutmelding zal volgen. Ik denk dat ik het zo doe: als er argumenten aanwezig zijn dan gebruik ik deze $reflection->newInstanceArgs( $arguments ) en als er geen argumenten zijn, dan deze $reflection->newInstance().
Bedenk wel dat als je newInstanceArgs aanroept met parameters, maar de class heeft geen constructor, dan zal je een foutmelding krijgen. Heeft de class wel een constructor (maar zonder parameters) dan lukt het wel gewoon. Vandaar dat ik die check erin heb gebouwd voor de constructor. Op zich werkt het nu namelijk altijd, mits er genoeg parameters zijn meegegeven. Meer dan nodig is nu geen probleem. Uiteraard kan je ervoor kiezen sommige exception gewoon te laten bestaan. mijn idee is echter dat ik dit ook in de toekomst met een gerust hart wil kunnen gebruiken en dan niet nog eens moet gaan bedenken waarom het opeens weer fout gaat. Maar dat is een keuze :-)
Ik snap wat je bedoelt. Maar ik probeer tegenwoordig niet meer zoals vroeger alles dicht te timmeren. Als ik een fout maak, dan loopt de boel de soep in en dan kom ik er vanzelf wel achter dat ik een fout heb gemaakt. Op sommige punten is het goed om iets te controleren, maar ik leg ook graag een stukje verantwoordelijkheid bij mezelf neer. Anders worden er continu allerlei controles uitgevoerd die in feite helemaal niet nodig zijn. Vroeger controleerde ik ook alles... werd er een boolean als argument verwacht, en gaf ik een int? BOEM, foutmelding. Nu denk ik daar anders over. Gewoon goed kijken wellk argument er wordt verwacht, even testen of het werkt zoals verwacht. Ja, dan prima. Nee, dan heb ik een foutje gemaakt: herstellen! Als het eenmaal werkt dan werkt het, en heb ik niet meer al die honderden controles per request nodig. Scheelt toch weer wat in de performance, en het houdt je scherp :)
Is zo'n reflection class altijd de beste manier om dynamisch een class aan te maken?
Is het beter/handiger dan bijvoorbeeld "call_user_func_array"?
En stel dat er geen argumenten zijn, is het dan ook beter/mooier om de reflection class te gebruiken in plaats van simpelweg zoiets:
$class = 'SomeClass';
$object = new $class();
En een klasse aanmaken met call_user_func_array, ik denk niet dat dat kan...
Alleen wat ik me afvraag... stel ik heb geen argumenten, zou het dan zonder reflection class sneller werken?
Gewijzigd op 12/03/2013 23:41:23 door Ozzie PHP
nee, want je hebt al een reflection class. En mocht je anders denken: benchmarken!
Stel ik heb geen argumenten, dan doe ik dit:
$class = 'SomeClass';
$object = new $class();
En als ik wel argumenten heb dan gebruik ik de reflection class...
Ik zal eens even benchmarken en zo dadelijk laten weten wat sneller is... momentje...
Maar om te kijken of je wel of geen argumenten hebt gebruik je toch al een reflection?
Wouter J op 12/03/2013 23:51:57:
Maar om te kijken of je wel of geen argumenten hebt gebruik je toch al een reflection?
Nou, dat hoeft niet... stel ik zet bij een service in mijn configuratiebestand geen argumenten, dan hoef ik blijkbaar dus ook geen argumenten mee te geven aan de betreffende class.
Benchmark gedaan:
Conclusie... als je geen argumenten hebt, dan kun je beter NIET de reflection class gebruiken.
Ik heb 1000 loops uitgevoerd.
zonder reflection class: 0,00063
met reflection class: 0,00243
Scheelt toch wel wat...