Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

DI: support for @inject annotations for setter & property injection

  • Loading branch information...
commit 5c75165257ff7e24478950aba9e76e8886c36c77 1 parent 6d28e39
@juzna authored
Showing with 153 additions and 1 deletion.
  1. +101 −0 Nette/DI/Container.php
  2. +52 −1 Nette/DI/ContainerBuilder.php
View
101 Nette/DI/Container.php
@@ -12,6 +12,7 @@
namespace Nette\DI;
use Nette;
+use Nette\Utils\Strings;
@@ -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**/
@@ -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);
+ }
}
View
53 Nette/DI/ContainerBuilder.php
@@ -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;
@@ -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;';
}
Please sign in to comment.
Something went wrong with that request. Please try again.