Skip to content

Commit

Permalink
DI: support for @Inject annotations for setter & property injection
Browse files Browse the repository at this point in the history
  • Loading branch information
juzna committed Oct 6, 2012
1 parent 6d28e39 commit 5c75165
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 1 deletion.
101 changes: 101 additions & 0 deletions Nette/DI/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Nette\DI;

use Nette;
use Nette\Utils\Strings;



Expand Down Expand Up @@ -301,6 +302,92 @@ public function callMethod($function, array $args = array())



/**
* Perform injections to an object created elsewhere
* @param object
* @return void
*/
public function inject($object) {
$className = get_class($object);
foreach (array_reverse(array_merge(array($className), class_parents($className))) as $className) {
$rc = new \Nette\Reflection\ClassType($className);
foreach ($rc->getMethods() as $rm) {
if ($rm->hasAnnotation('inject')) {
if (!$rm->isPublic()) throw new ServiceCreationException("Injection method $rc->name::$rm->name is not public");

$annot = $rm->getAnnotation('inject');
if ($annot === TRUE) $args = array();
elseif (is_string($annot)) $args = array($annot);
elseif ($annot instanceof \ArrayObject) $args = iterator_to_array($annot);
else throw new ServiceCreationException("Unknown parameters of annotation");

$callback = callback($object, $rm->name);
$callback->invokeArgs(Helpers::autowireArguments($rm, $args, $this));
}
}

foreach ($rc->getProperties() as $rp) {
if ($rp->hasAnnotation('inject')) {

// what is supposed to be injected
$annot = $rp->getAnnotation('inject');
if ($annot === TRUE) {
if ($annot = $rp->getAnnotation('var')) {
$value = "@$annot";
} else {
throw new ServiceCreationException("Type of parameter $rc->name::\$$rp->name is not known");
}
}
elseif (is_string($annot)) $value = $annot;
elseif ($annot instanceof \ArrayObject) throw new ServiceCreationException("Cam have only one value!");
else throw new ServiceCreationException("Unknown parameters of annotation");

$value = Helpers::expand($value, $this->parameters, TRUE);
if ($service = $this->getServiceByBuilder($value)) $value = $service;

if ($rp->isPublic()) {
$object->{$rp->name} = $value;

} else {
$this->injectProperty($object, $rc->name, $rp->name, $value);
}
}
}
}
}

/**
* Converts @service or @\Class -> service name and checks its existence.
* @param mixed
* @return string of FALSE, if argument is not service name
*/
public function getServiceByBuilder($arg, $self = NULL)
{
if (!is_string($arg) || !preg_match('#^@[\w\\\\.].+$#', $arg)) {
return FALSE;
}
$service = substr($arg, 1);
if ($service === 'self') {
$service = $self;
}
if (Strings::contains($service, '\\')) {
if ($this->classes === FALSE) { // may be disabled by prepareClassList
return $service;
}
$res = $this->getByType($service);
if (!$res) {
throw new ServiceCreationException("Reference to missing service of type $service.");
}
return $res;
}
if (!$this->hasService($service)) {
throw new ServiceCreationException("Reference to missing service '$service'.");
}
return $this->getService($service);
}



/********************* shortcuts ****************d*g**/


Expand Down Expand Up @@ -384,4 +471,18 @@ public static function getMethodName($name, $isService = TRUE)
return ($isService ? 'createService' : 'create') . ($name === $uname ? '__' : '') . str_replace('.', '__', $uname);
}


/**
* Inject property which is not publicly accessible
* @param object Target service
* @param string
* @param string
* @param mixed Value to be injected
*/
public function injectProperty($object, $className, $propertyName, $value)
{
$rp = new \Nette\Reflection\Property($className, $propertyName);
$rp->setAccessible(TRUE);
$rp->setValue($object, $value);
}
}
53 changes: 52 additions & 1 deletion Nette/DI/ContainerBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public function autowireArguments($class, $method, array $arguments)
public function prepareClassList()
{
// complete class-factory pairs; expand classes
foreach ($this->definitions as $name => $def) {
foreach ($this->definitions as $name => /** @var ServiceDefinition $def */ $def) {
if ($def->class === self::CREATED_SERVICE || ($def->factory && $def->factory->entity === self::CREATED_SERVICE)) {
$def->class = $name;
$def->internal = TRUE;
Expand Down Expand Up @@ -422,6 +422,57 @@ private function generateService($name)
$code .= $this->formatStatement($setup, $name) . ";\n";
}

// Injected methods and properties
if ($def->class && !$this->getServiceName($entity)) {
foreach (array_reverse(array_merge(array($def->class), class_parents($def->class))) as $className) {
$rc = new \Nette\Reflection\ClassType($className);
foreach ($rc->getMethods() as $rm) {
if ($rm->hasAnnotation('inject')) {
if (!$rm->isPublic()) throw new ServiceCreationException("Injection method $rc->name::$rm->name is not public");

$annot = $rm->getAnnotation('inject');
if ($annot === TRUE) $args = array();
elseif (is_string($annot)) $args = array($annot);
elseif ($annot instanceof \ArrayObject) $args = iterator_to_array($annot);
else throw new ServiceCreationException("Unknown parameters of annotation");

$setup = new Statement(array("@$name", $rm->name), $args);
$setup = Helpers::expand($setup, $parameters, TRUE);
$code .= $this->formatStatement($setup, $name) . ";\n";
}
}

foreach ($rc->getProperties() as $rp) {
if ($rp->hasAnnotation('inject')) {

// what is supposed to be injected
$annot = $rp->getAnnotation('inject');
if ($annot === TRUE) {
if ($annot = $rp->getAnnotation('var')) {
$value = "@$annot";
} else {
throw new ServiceCreationException("Type of parameter $rc->name::\$$rp->name is not known");
}
}
elseif (is_string($annot)) $value = $annot;
elseif ($annot instanceof \ArrayObject) throw new ServiceCreationException("Cam have only one value!");
else throw new ServiceCreationException("Unknown parameters of annotation");

if ($rp->isPublic()) {
$setup = new Statement(array("@$name", '$' . $rp->name), array($value));

} else {
$setup = new Statement(array('@container', 'injectProperty'), array("@$name", $rc->name, $rp->name, $value));
}

$setup = Helpers::expand($setup, $parameters, TRUE);
$code .= $this->formatStatement($setup, $name) . ";\n";
}
}
}
}


return $code .= 'return $service;';
}

Expand Down

0 comments on commit 5c75165

Please sign in to comment.