Skip to content

Commit

Permalink
#1 Use statements are taken into account for class resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli committed Jan 27, 2013
1 parent 5f7a1bc commit 5fde3ac
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 13 deletions.
1 change: 1 addition & 0 deletions change-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 2.1

* `use` statements to import classes from other namespaces are now taken into account with the `@var` annotation
* Updated and lightened the dependencies : `doctrine/common` has been replaced with more specific `doctrine/annotations` and `doctrine/cache`

## 2.0
Expand Down
74 changes: 69 additions & 5 deletions src/DI/MetadataReader/DefaultMetadataReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use InvalidArgumentException;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\PhpParser;
use Doctrine\Common\Annotations\Reader;

/**
Expand All @@ -29,6 +30,15 @@ class DefaultMetadataReader implements MetadataReader
*/
private $annotationReader;

/**
* @var \Doctrine\Common\Annotations\PhpParser
*/
private $phpParser;

public function __construct() {
$this->phpParser = new PhpParser;
}

/**
* Returns DI annotations found in the class
* @param string $classname
Expand All @@ -52,7 +62,7 @@ public function getClassMetadata($classname) {
if ($annotation instanceof Inject) {
// Enrich @Inject annotation with @var content
if ($annotation->name == null) {
$propertyType = $this->getPropertyType($property);
$propertyType = $this->getPropertyType($reflectionClass, $property);
if ($propertyType == null) {
throw new AnnotationException("@Inject was found on $classname::"
. $property->getName() . " but no (or empty) @var annotation");
Expand Down Expand Up @@ -88,15 +98,69 @@ public function setAnnotationReader(Reader $annotationReader) {

/**
* Parse the docblock of the property to get the var annotation
* @param \ReflectionClass $class
* @param \ReflectionProperty $property
* @return string Type of the property (content of var annotation)
* @throws \DI\Annotations\AnnotationException
* @return string|null Type of the property (content of var annotation)
*/
private function getPropertyType(\ReflectionProperty $property) {
private function getPropertyType(\ReflectionClass $class, \ReflectionProperty $property) {
// Get the content of the @var annotation
if (preg_match('/@var\s+([^\s]+)/', $property->getDocComment(), $matches)) {
list(, $type) = $matches;
return $type;
} else {
return null;
}

// If the class name is not fully qualified (FQN must start with a \)
if ($type[0] !== '\\') {
$alias = (false === $pos = strpos($type, '\\')) ? $type : substr($type, 0, $pos);
$loweredAlias = strtolower($alias);

// Retrieve "use" statements
$uses = $this->phpParser->parseClass($property->getDeclaringClass());

$found = false;

if ($this->classExists($class->getNamespaceName() . '\\' . $type)) {
$type = $class->getNamespaceName() . '\\' . $type;
$found = true;
} elseif (isset($uses[$loweredAlias])) {
// Imported classes
if (false !== $pos) {
$type = $uses[$loweredAlias] . substr($type, $pos);
} else {
$type = $uses[$loweredAlias];
}
$found = true;
} elseif (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) {
// Class namespace
$type = $uses['__NAMESPACE__'] . '\\' . $type;
$found = true;
} elseif ($this->classExists($type)) {
// No namespace
$found = true;
}

if (!$found) {
throw new AnnotationException("The @var annotation on {$class->name}::" . $property->getName()
. " contains a non existent class. Did you maybe forget to add a 'use' statement for this annotation?");
}
}

if (!$this->classExists($type)) {
throw new AnnotationException("The @var annotation on {$class->name}::" . $property->getName()
. " contains a non existent class");
}
return null;

return $type;
}

/**
* @param string $class
* @return bool
*/
private function classExists($class) {
return class_exists($class) || interface_exists($class);
}

}
41 changes: 33 additions & 8 deletions tests/IntegrationTests/DI/BeanInjectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,16 +126,41 @@ public function testFactoryCreatesSingletons() {
$this->assertSame($class2_1, $class2_2);
}

/**
* Check that if a dependency is already set, the container
* will not overwrite it
*/
public function testIssue14() {
$object = new \IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue14();
$class2 = new Class2();
$object->setClass2($class2);
Container::getInstance()->injectAll($object);
$this->assertSame($class2, $object->getClass2());
}

/**
* Check that @ var annotation takes "use" statements into account
*/
public function testIssue1() {
/** @var $object \IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1 */
$object = Container::getInstance()->get('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1');
$this->assertInstanceOf('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2', $object->class2);
$this->assertInstanceOf('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2', $object->alias);
$this->assertInstanceOf('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2', $object->namespaceAlias);

/** @var $object \IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1\AnotherIssue1 */
$object = Container::getInstance()->get('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1\AnotherIssue1');
$this->assertInstanceOf('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2', $object->dependency);
$this->assertInstanceOf('\IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1\Dependency', $object->sameNamespaceDependency);
}

/**
* Check that if a dependency is already set, the container
* will not overwrite it
* Check error cases
* @expectedException \DI\Annotations\AnnotationException
*/
public function testIssue14() {
$object = new \IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue14();
$class2 = new Class2();
$object->setClass2($class2);
Container::getInstance()->injectAll($object);
$this->assertSame($class2, $object->getClass2());
public function testNotFoundVarAnnotation() {
/** @var $object \IntegrationTests\DI\Fixtures\BeanInjectionTest\NotFoundVarAnnotation */
Container::getInstance()->get('\IntegrationTests\DI\Fixtures\BeanInjectionTest\NotFoundVarAnnotation');
}

}
40 changes: 40 additions & 0 deletions tests/IntegrationTests/DI/Fixtures/BeanInjectionTest/Issue1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* PHP-DI
*
* @link http://mnapoli.github.com/PHP-DI/
* @copyright 2012 Matthieu Napoli (http://mnapoli.fr/)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/

namespace IntegrationTests\DI\Fixtures\BeanInjectionTest;

use \DI\Annotations\Inject;
use \IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2;
use \IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2 as Alias;
use \IntegrationTests\DI\Fixtures\BeanInjectionTest as NamespaceAlias;

/**
* Fixture class
*/
class Issue1 {

/**
* @Inject
* @var Class2
*/
public $class2;

/**
* @Inject
* @var Alias
*/
public $alias;

/**
* @Inject
* @var NamespaceAlias\Class2
*/
public $namespaceAlias;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
/**
* PHP-DI
*
* @link http://mnapoli.github.com/PHP-DI/
* @copyright 2012 Matthieu Napoli (http://mnapoli.fr/)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/

namespace IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1;

use \DI\Annotations\Inject;
use \IntegrationTests\DI\Fixtures\BeanInjectionTest\Class2;

/**
* Fixture class
*/
class AnotherIssue1 {

/**
* @Inject
* @var Class2
*/
public $dependency;

/**
* @Inject
* @var Dependency
*/
public $sameNamespaceDependency;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
/**
* PHP-DI
*
* @link http://mnapoli.github.com/PHP-DI/
* @copyright 2012 Matthieu Napoli (http://mnapoli.fr/)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/

namespace IntegrationTests\DI\Fixtures\BeanInjectionTest\Issue1;

/**
* Fixture class
*/
class Dependency {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php
/**
* PHP-DI
*
* @link http://mnapoli.github.com/PHP-DI/
* @copyright 2012 Matthieu Napoli (http://mnapoli.fr/)
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the LICENSE file)
*/

namespace IntegrationTests\DI\Fixtures\BeanInjectionTest;

use \DI\Annotations\Inject;

/**
* Fixture class
*/
class NotFoundVarAnnotation {

/**
* @Inject
* @var this_is_a_non_existent_class
*/
public $class2;

}

0 comments on commit 5fde3ac

Please sign in to comment.