the whole shebang

This commit is contained in:
2014-11-25 16:42:40 +01:00
parent 7f74c0613e
commit ab1334c0cf
3686 changed files with 496409 additions and 1 deletions

View File

@@ -0,0 +1,3 @@
vendor/
composer.lock
composer.phar

View File

@@ -0,0 +1,9 @@
language: php
php:
- 5.3
- 5.4
before_script:
- composer --prefer-source --dev install
- phpunit

11
vendor/doctrine/annotations/README.md vendored Normal file
View File

@@ -0,0 +1,11 @@
# Doctrine Annotations
[![Build Status](https://travis-ci.org/doctrine/annotations.png?branch=master)](https://travis-ci.org/doctrine/annotations)
Docblock Annotations Parser library (extracted from Doctrine Common).
## Changelog
### v1.1
* Add Exception when ZendOptimizer+ or Opcache is configured to drop comments

View File

@@ -0,0 +1,30 @@
{
"name": "doctrine/annotations",
"type": "library",
"description": "Docblock Annotations Parser",
"keywords": ["annotations", "docblock", "parser"],
"homepage": "http://www.doctrine-project.org",
"license": "MIT",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
"php": ">=5.3.2",
"doctrine/lexer": "1.*"
},
"require-dev": {
"doctrine/cache": "1.*"
},
"autoload": {
"psr-0": { "Doctrine\\Common\\Annotations\\": "lib/" }
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* Annotations class
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class Annotation
{
/**
* Value property. Common among all derived classes.
*
* @var string
*/
public $value;
/**
* Constructor
*
* @param array $data Key-value for properties to be defined in this class
*/
public final function __construct(array $data)
{
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
/**
* Error handler for unknown property accessor in Annotation class.
*
* @param string $name Unknown property name
*
* @throws \BadMethodCallException
*/
public function __get($name)
{
throw new \BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
);
}
/**
* Error handler for unknown property mutator in Annotation class.
*
* @param string $name Unkown property name
* @param mixed $value Property value
*
* @throws \BadMethodCallException
*/
public function __set($name, $value)
{
throw new \BadMethodCallException(
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
);
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser
* to check the attribute type during the parsing process.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*
* @Annotation
*/
final class Attribute
{
/**
* @var string
*/
public $name;
/**
* @var string
*/
public $type;
/**
* @var boolean
*/
public $required = false;
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser
* to check the types of all declared attributes during the parsing process.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*
* @Annotation
*/
final class Attributes
{
/**
* @var array<Doctrine\Common\Annotations\Annotation\Attribute>
*/
public $value;
}

View File

@@ -0,0 +1,85 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser
* to check the available values during the parsing process.
*
* @since 2.4
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*
* @Annotation
* @Attributes({
* @Attribute("value", required = true, type = "array"),
* @Attribute("literal", required = false, type = "array")
* })
*/
final class Enum
{
/**
* @var array
*/
public $value;
/**
* Literal target declaration.
*
* @var array
*/
public $literal;
/**
* Annotation construct
*
* @param array $values
*
* @throws \InvalidArgumentException
*/
public function __construct(array $values)
{
if ( ! isset($values['literal'])) {
$values['literal'] = array();
}
foreach ($values['value'] as $var) {
if( ! is_scalar($var)) {
throw new \InvalidArgumentException(sprintf(
'@Enum supports only scalar values "%s" given.',
is_object($var) ? get_class($var) : gettype($var)
));
}
}
foreach ($values['literal'] as $key => $var) {
if( ! in_array($key, $values['value'])) {
throw new \InvalidArgumentException(sprintf(
'Undefined enumerator value "%s" for literal "%s".',
$key , $var
));
}
}
$this->value = $values['value'];
$this->literal = $values['literal'];
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser to ignore specific
* annotations during the parsing process.
*
* @Annotation
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class IgnoreAnnotation
{
/**
* @var array
*/
public $names;
/**
* Constructor
*
* @param array $values
*
* @throws \RuntimeException
*/
public function __construct(array $values)
{
if (is_string($values['value'])) {
$values['value'] = array($values['value']);
}
if (!is_array($values['value'])) {
throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value'])));
}
$this->names = $values['value'];
}
}

View File

@@ -0,0 +1,33 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser
* to check if that attribute is required during the parsing process.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*
* @Annotation
*/
final class Required
{
}

View File

@@ -0,0 +1,107 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations\Annotation;
/**
* Annotation that can be used to signal to the parser
* to check the annotation target during the parsing process.
*
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*
* @Annotation
*/
final class Target
{
const TARGET_CLASS = 1;
const TARGET_METHOD = 2;
const TARGET_PROPERTY = 4;
const TARGET_ANNOTATION = 8;
const TARGET_ALL = 15;
/**
* @var array
*/
private static $map = array(
'ALL' => self::TARGET_ALL,
'CLASS' => self::TARGET_CLASS,
'METHOD' => self::TARGET_METHOD,
'PROPERTY' => self::TARGET_PROPERTY,
'ANNOTATION' => self::TARGET_ANNOTATION,
);
/**
* @var array
*/
public $value;
/**
* Targets as bitmask.
*
* @var integer
*/
public $targets;
/**
* Literal target declaration.
*
* @var integer
*/
public $literal;
/**
* Annotation construct
*
* @param array $values
*
* @throws \InvalidArgumentException
*/
public function __construct(array $values)
{
if (!isset($values['value'])){
$values['value'] = null;
}
if (is_string($values['value'])){
$values['value'] = array($values['value']);
}
if (!is_array($values['value'])){
throw new \InvalidArgumentException(
sprintf('@Target expects either a string value, or an array of strings, "%s" given.',
is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
)
);
}
$bitmask = 0;
foreach ($values['value'] as $literal) {
if(!isset(self::$map[$literal])){
throw new \InvalidArgumentException(
sprintf('Invalid Target "%s". Available targets: [%s]',
$literal, implode(', ', array_keys(self::$map)))
);
}
$bitmask += self::$map[$literal];
}
$this->targets = $bitmask;
$this->value = $values['value'];
$this->literal = implode(', ', $this->value);
}
}

View File

@@ -0,0 +1,158 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* Description of AnnotationException
*
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
*/
class AnnotationException extends \Exception
{
/**
* Creates a new AnnotationException describing a Syntax error.
*
* @param string $message Exception message
* @return AnnotationException
*/
public static function syntaxError($message)
{
return new self('[Syntax Error] ' . $message);
}
/**
* Creates a new AnnotationException describing a Semantical error.
*
* @param string $message Exception message
* @return AnnotationException
*/
public static function semanticalError($message)
{
return new self('[Semantical Error] ' . $message);
}
/**
* Creates a new AnnotationException describing a constant semantical error.
*
* @since 2.3
* @param string $identifier
* @param string $context
* @return AnnotationException
*/
public static function semanticalErrorConstants($identifier, $context = null)
{
return self::semanticalError(sprintf(
"Couldn't find constant %s%s", $identifier,
$context ? ", $context." : "."
));
}
/**
* Creates a new AnnotationException describing an error which occurred during
* the creation of the annotation.
*
* @since 2.2
* @param string $message
* @return AnnotationException
*/
public static function creationError($message)
{
return new self('[Creation Error] ' . $message);
}
/**
* Creates a new AnnotationException describing an type error of an attribute.
*
* @since 2.2
* @param string $attributeName
* @param string $annotationName
* @param string $context
* @param string $expected
* @param mixed $actual
* @return AnnotationException
*/
public static function typeError($attributeName, $annotationName, $context, $expected, $actual)
{
return new self(sprintf(
'[Type Error] Attribute "%s" of @%s declared on %s expects %s, but got %s.',
$attributeName,
$annotationName,
$context,
$expected,
is_object($actual) ? 'an instance of '.get_class($actual) : gettype($actual)
));
}
/**
* Creates a new AnnotationException describing an required error of an attribute.
*
* @since 2.2
* @param string $attributeName
* @param string $annotationName
* @param string $context
* @param string $expected
* @return AnnotationException
*/
public static function requiredError($attributeName, $annotationName, $context, $expected)
{
return new self(sprintf(
'[Type Error] Attribute "%s" of @%s declared on %s expects %s. This value should not be null.',
$attributeName,
$annotationName,
$context,
$expected
));
}
/**
* Creates a new AnnotationException describing a invalid enummerator.
*
* @since 2.4
* @param string $attributeName
* @param string $annotationName
* @param string $context
* @param array $available
* @param mixed $given
* @return AnnotationException
*/
public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
{
throw new self(sprintf(
'[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.',
$attributeName,
$annotationName,
$context,
implode(', ', $available),
is_object($given) ? get_class($given) : $given
));
}
/**
* @return AnnotationException
*/
public static function optimizerPlusSaveComments()
{
throw new self("You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.");
}
}

View File

@@ -0,0 +1,318 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
use Doctrine\Common\Annotations\Annotation\Target;
use Closure;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
/**
* A reader for docblock annotations.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class AnnotationReader implements Reader
{
/**
* Global map for imports.
*
* @var array
*/
private static $globalImports = array(
'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation',
);
/**
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
*
* The names are case sensitive.
*
* @var array
*/
private static $globalIgnoredNames = array(
'access'=> true, 'author'=> true, 'copyright'=> true, 'deprecated'=> true,
'example'=> true, 'ignore'=> true, 'internal'=> true, 'link'=> true, 'see'=> true,
'since'=> true, 'tutorial'=> true, 'version'=> true, 'package'=> true,
'subpackage'=> true, 'name'=> true, 'global'=> true, 'param'=> true,
'return'=> true, 'staticvar'=> true, 'category'=> true, 'staticVar'=> true,
'static'=> true, 'var'=> true, 'throws'=> true, 'inheritdoc'=> true,
'inheritDoc'=> true, 'license'=> true, 'todo'=> true, 'TODO'=> true,
'deprec'=> true, 'property' => true, 'method' => true,
'abstract'=> true, 'exception'=> true, 'magic' => true, 'api' => true,
'final'=> true, 'filesource'=> true, 'throw' => true, 'uses' => true,
'usedby'=> true, 'private' => true, 'Annotation' => true, 'override' => true,
'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true,
'Required' => true, 'Attribute' => true, 'Attributes' => true,
'Target' => true, 'SuppressWarnings' => true,
'ingroup' => true, 'code' => true, 'endcode' => true,
'package_version' => true, 'fixme' => true
);
/**
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
*
* @param string $name
*/
static public function addGlobalIgnoredName($name)
{
self::$globalIgnoredNames[$name] = true;
}
/**
* Annotations Parser
*
* @var \Doctrine\Common\Annotations\DocParser
*/
private $parser;
/**
* Annotations Parser used to collect parsing metadata
*
* @var \Doctrine\Common\Annotations\DocParser
*/
private $preParser;
/**
* PHP Parser used to collect imports.
*
* @var \Doctrine\Common\Annotations\PhpParser
*/
private $phpParser;
/**
* In-memory cache mechanism to store imported annotations per class.
*
* @var array
*/
private $imports = array();
/**
* In-memory cache mechanism to store ignored annotations per class.
*
* @var array
*/
private $ignoredAnnotationNames = array();
/**
* Constructor.
*
* Initializes a new AnnotationReader.
*/
public function __construct()
{
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) {
throw AnnotationException::optimizerPlusSaveComments();
}
if (extension_loaded('opcache') && ini_get('opcache.save_comments') == 0) {
throw AnnotationException::optimizerPlusSaveComments();
}
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php');
$this->parser = new DocParser;
$this->preParser = new DocParser;
$this->preParser->setImports(self::$globalImports);
$this->preParser->setIgnoreNotImportedAnnotations(true);
$this->phpParser = new PhpParser;
}
/**
* Gets the annotations applied to a class.
*
* @param ReflectionClass $class The ReflectionClass of the class from which
* the class annotations should be read.
* @return array An array of Annotations.
*/
public function getClassAnnotations(ReflectionClass $class)
{
$this->parser->setTarget(Target::TARGET_CLASS);
$this->parser->setImports($this->getImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName());
}
/**
* Gets a class annotation.
*
* @param ReflectionClass $class The ReflectionClass of the class from which
* the class annotations should be read.
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getClassAnnotation(ReflectionClass $class, $annotationName)
{
$annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Gets the annotations applied to a property.
*
* @param ReflectionProperty $property The ReflectionProperty of the property
* from which the annotations should be read.
* @return array An array of Annotations.
*/
public function getPropertyAnnotations(ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$context = 'property ' . $class->getName() . "::\$" . $property->getName();
$this->parser->setTarget(Target::TARGET_PROPERTY);
$this->parser->setImports($this->getImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
return $this->parser->parse($property->getDocComment(), $context);
}
/**
* Gets a property annotation.
*
* @param ReflectionProperty $property
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
{
$annotations = $this->getPropertyAnnotations($property);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Gets the annotations applied to a method.
*
* @param \ReflectionMethod $method The ReflectionMethod of the method from which
* the annotations should be read.
*
* @return array An array of Annotations.
*/
public function getMethodAnnotations(ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$context = 'method ' . $class->getName() . '::' . $method->getName() . '()';
$this->parser->setTarget(Target::TARGET_METHOD);
$this->parser->setImports($this->getImports($class));
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
return $this->parser->parse($method->getDocComment(), $context);
}
/**
* Gets a method annotation.
*
* @param ReflectionMethod $method
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
{
$annotations = $this->getMethodAnnotations($method);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Returns the ignored annotations for the given class.
*
* @param ReflectionClass $class
* @return array
*/
private function getIgnoredAnnotationNames(ReflectionClass $class)
{
if (isset($this->ignoredAnnotationNames[$name = $class->getName()])) {
return $this->ignoredAnnotationNames[$name];
}
$this->collectParsingMetadata($class);
return $this->ignoredAnnotationNames[$name];
}
/**
* Retrieve imports
*
* @param \ReflectionClass $class
* @return array
*/
private function getImports(ReflectionClass $class)
{
if (isset($this->imports[$name = $class->getName()])) {
return $this->imports[$name];
}
$this->collectParsingMetadata($class);
return $this->imports[$name];
}
/**
* Collects parsing metadata for a given class
*
* @param ReflectionClass $class
*/
private function collectParsingMetadata(ReflectionClass $class)
{
$ignoredAnnotationNames = self::$globalIgnoredNames;
$annotations = $this->preParser->parse($class->getDocComment(), 'class '.$class->name);
foreach ($annotations as $annotation) {
if ($annotation instanceof IgnoreAnnotation) {
foreach ($annotation->names AS $annot) {
$ignoredAnnotationNames[$annot] = true;
}
}
}
$name = $class->getName();
$this->imports[$name] = array_merge(
self::$globalImports,
$this->phpParser->parseClass($class),
array('__NAMESPACE__' => $class->getNamespaceName())
);
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* AnnotationRegistry
*/
final class AnnotationRegistry
{
/**
* A map of namespaces to use for autoloading purposes based on a PSR-0 convention.
*
* Contains the namespace as key and an array of directories as value. If the value is NULL
* the include path is used for checking for the corresponding file.
*
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
*
* @var array
*/
static private $autoloadNamespaces = array();
/**
* A map of autoloader callables.
*
* @var array
*/
static private $loaders = array();
static public function reset()
{
self::$autoloadNamespaces = array();
self::$loaders = array();
}
/**
* Register file
*
* @param string $file
*/
static public function registerFile($file)
{
require_once $file;
}
/**
* Add a namespace with one or many directories to look for files or null for the include path.
*
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
*
* @param string $namespace
* @param string|array|null $dirs
*/
static public function registerAutoloadNamespace($namespace, $dirs = null)
{
self::$autoloadNamespaces[$namespace] = $dirs;
}
/**
* Register multiple namespaces
*
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
*
* @param array $namespaces
*/
static public function registerAutoloadNamespaces(array $namespaces)
{
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
}
/**
* Register an autoloading callable for annotations, much like spl_autoload_register().
*
* NOTE: These class loaders HAVE to be silent when a class was not found!
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
*
* @param callable $callable
*
* @throws \InvalidArgumentException
*/
static public function registerLoader($callable)
{
if (!is_callable($callable)) {
throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
}
self::$loaders[] = $callable;
}
/**
* Autoload an annotation class silently.
*
* @param string $class
* @return boolean
*/
static public function loadAnnotationClass($class)
{
foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
if (strpos($class, $namespace) === 0) {
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
if ($dirs === null) {
if ($path = stream_resolve_include_path($file)) {
require $path;
return true;
}
} else {
foreach((array)$dirs AS $dir) {
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
require $dir . DIRECTORY_SEPARATOR . $file;
return true;
}
}
}
}
}
foreach (self::$loaders AS $loader) {
if (call_user_func($loader, $class) === true) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,250 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Cache\Cache;
/**
* A cache aware annotation reader.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
final class CachedReader implements Reader
{
/**
* @var string
*/
private static $CACHE_SALT = '@[Annot]';
/**
* @var Reader
*/
private $delegate;
/**
* @var Cache
*/
private $cache;
/**
* @var boolean
*/
private $debug;
/**
* @var array
*/
private $loadedAnnotations;
/**
* Constructor
*
* @param Reader $reader
* @param Cache $cache
* @param bool $debug
*/
public function __construct(Reader $reader, Cache $cache, $debug = false)
{
$this->delegate = $reader;
$this->cache = $cache;
$this->debug = (Boolean) $debug;
}
/**
* Get annotations for class
*
* @param \ReflectionClass $class
* @return array
*/
public function getClassAnnotations(\ReflectionClass $class)
{
$cacheKey = $class->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
$annots = $this->delegate->getClassAnnotations($class);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
/**
* Get selected annotation for class
*
* @param \ReflectionClass $class
* @param string $annotationName
* @return null
*/
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
{
foreach ($this->getClassAnnotations($class) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
/**
* Get annotations for property
*
* @param \ReflectionProperty $property
* @return array
*/
public function getPropertyAnnotations(\ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
$cacheKey = $class->getName().'$'.$property->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
$annots = $this->delegate->getPropertyAnnotations($property);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
/**
* Get selected annotation for property
*
* @param \ReflectionProperty $property
* @param string $annotationName
* @return null
*/
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
{
foreach ($this->getPropertyAnnotations($property) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
/**
* Get method annotations
*
* @param \ReflectionMethod $method
* @return array
*/
public function getMethodAnnotations(\ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
$cacheKey = $class->getName().'#'.$method->getName();
if (isset($this->loadedAnnotations[$cacheKey])) {
return $this->loadedAnnotations[$cacheKey];
}
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
$annots = $this->delegate->getMethodAnnotations($method);
$this->saveToCache($cacheKey, $annots);
}
return $this->loadedAnnotations[$cacheKey] = $annots;
}
/**
* Get selected method annotation
*
* @param \ReflectionMethod $method
* @param string $annotationName
* @return null
*/
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
{
foreach ($this->getMethodAnnotations($method) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
/**
* Clear loaded annotations
*/
public function clearLoadedAnnotations()
{
$this->loadedAnnotations = array();
}
/**
* Fetches a value from the cache.
*
* @param string $rawCacheKey The cache key.
* @param \ReflectionClass $class The related class.
* @return mixed|boolean The cached value or false when the value is not in cache.
*/
private function fetchFromCache($rawCacheKey, \ReflectionClass $class)
{
$cacheKey = $rawCacheKey . self::$CACHE_SALT;
if (($data = $this->cache->fetch($cacheKey)) !== false) {
if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
return $data;
}
}
return false;
}
/**
* Saves a value to the cache
*
* @param string $rawCacheKey The cache key.
* @param mixed $value The value.
*/
private function saveToCache($rawCacheKey, $value)
{
$cacheKey = $rawCacheKey . self::$CACHE_SALT;
$this->cache->save($cacheKey, $value);
if ($this->debug) {
$this->cache->save('[C]'.$cacheKey, time());
}
}
/**
* Check if cache is fresh
*
* @param string $cacheKey
* @param \ReflectionClass $class
* @return bool
*/
private function isCacheFresh($cacheKey, \ReflectionClass $class)
{
if (false === $filename = $class->getFilename()) {
return true;
}
return $this->cache->fetch('[C]'.$cacheKey) >= filemtime($filename);
}
}

View File

@@ -0,0 +1,132 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Lexer\AbstractLexer;
/**
* Simple lexer for docblock annotations.
*
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
final class DocLexer extends AbstractLexer
{
const T_NONE = 1;
const T_INTEGER = 2;
const T_STRING = 3;
const T_FLOAT = 4;
// All tokens that are also identifiers should be >= 100
const T_IDENTIFIER = 100;
const T_AT = 101;
const T_CLOSE_CURLY_BRACES = 102;
const T_CLOSE_PARENTHESIS = 103;
const T_COMMA = 104;
const T_EQUALS = 105;
const T_FALSE = 106;
const T_NAMESPACE_SEPARATOR = 107;
const T_OPEN_CURLY_BRACES = 108;
const T_OPEN_PARENTHESIS = 109;
const T_TRUE = 110;
const T_NULL = 111;
const T_COLON = 112;
protected $noCase = array(
'@' => self::T_AT,
',' => self::T_COMMA,
'(' => self::T_OPEN_PARENTHESIS,
')' => self::T_CLOSE_PARENTHESIS,
'{' => self::T_OPEN_CURLY_BRACES,
'}' => self::T_CLOSE_CURLY_BRACES,
'=' => self::T_EQUALS,
':' => self::T_COLON,
'\\' => self::T_NAMESPACE_SEPARATOR
);
protected $withCase = array(
'true' => self::T_TRUE,
'false' => self::T_FALSE,
'null' => self::T_NULL
);
/**
* {@inheritdoc}
*/
protected function getCatchablePatterns()
{
return array(
'[a-z_\\\][a-z0-9_\:\\\]*[a-z]{1}',
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
'"(?:[^"]|"")*"',
);
}
/**
* {@inheritdoc}
*/
protected function getNonCatchablePatterns()
{
return array('\s+', '\*+', '(.)');
}
/**
* {@inheritdoc}
*
* @param string $value
*
* @return int
*/
protected function getType(&$value)
{
$type = self::T_NONE;
if ($value[0] === '"') {
$value = str_replace('""', '"', substr($value, 1, strlen($value) - 2));
return self::T_STRING;
}
if (isset($this->noCase[$value])) {
return $this->noCase[$value];
}
if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) {
return self::T_IDENTIFIER;
}
$lowerValue = strtolower($value);
if (isset($this->withCase[$lowerValue])) {
return $this->withCase[$lowerValue];
}
// Checking numeric value
if (is_numeric($value)) {
return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
? self::T_FLOAT : self::T_INTEGER;
}
return $type;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,269 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* File cache reader for annotations.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Benjamin Eberlei <kontakt@beberlei.de>
*/
class FileCacheReader implements Reader
{
/**
* @var Reader
*/
private $reader;
/**
* @var string
*/
private $dir;
/**
* @var bool
*/
private $debug;
/**
* @var array
*/
private $loadedAnnotations = array();
private $classNameHashes = array();
/**
* Constructor
*
* @param Reader $reader
* @param string $cacheDir
* @param bool $debug
*
* @throws \InvalidArgumentException
*/
public function __construct(Reader $reader, $cacheDir, $debug = false)
{
$this->reader = $reader;
if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
}
if (!is_writable($cacheDir)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $cacheDir));
}
$this->dir = rtrim($cacheDir, '\\/');
$this->debug = $debug;
}
/**
* Retrieve annotations for class
*
* @param \ReflectionClass $class
* @return array
*/
public function getClassAnnotations(\ReflectionClass $class)
{
if ( ! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name];
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
if (!is_file($path)) {
$annot = $this->reader->getClassAnnotations($class);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
if ($this->debug
&& (false !== $filename = $class->getFilename())
&& filemtime($path) < filemtime($filename)) {
@unlink($path);
$annot = $this->reader->getClassAnnotations($class);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
/**
* Get annotations for property
*
* @param \ReflectionProperty $property
* @return array
*/
public function getPropertyAnnotations(\ReflectionProperty $property)
{
$class = $property->getDeclaringClass();
if ( ! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name].'$'.$property->getName();
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
if (!is_file($path)) {
$annot = $this->reader->getPropertyAnnotations($property);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
if ($this->debug
&& (false !== $filename = $class->getFilename())
&& filemtime($path) < filemtime($filename)) {
@unlink($path);
$annot = $this->reader->getPropertyAnnotations($property);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
/**
* Retrieve annotations for method
*
* @param \ReflectionMethod $method
* @return array
*/
public function getMethodAnnotations(\ReflectionMethod $method)
{
$class = $method->getDeclaringClass();
if ( ! isset($this->classNameHashes[$class->name])) {
$this->classNameHashes[$class->name] = sha1($class->name);
}
$key = $this->classNameHashes[$class->name].'#'.$method->getName();
if (isset($this->loadedAnnotations[$key])) {
return $this->loadedAnnotations[$key];
}
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
if (!is_file($path)) {
$annot = $this->reader->getMethodAnnotations($method);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
if ($this->debug
&& (false !== $filename = $class->getFilename())
&& filemtime($path) < filemtime($filename)) {
@unlink($path);
$annot = $this->reader->getMethodAnnotations($method);
$this->saveCacheFile($path, $annot);
return $this->loadedAnnotations[$key] = $annot;
}
return $this->loadedAnnotations[$key] = include $path;
}
/**
* Save cache file
*
* @param string $path
* @param mixed $data
*/
private function saveCacheFile($path, $data)
{
file_put_contents($path, '<?php return unserialize('.var_export(serialize($data), true).');');
}
/**
* Gets a class annotation.
*
* @param \ReflectionClass $class The ReflectionClass of the class from which
* the class annotations should be read.
* @param string $annotationName The name of the annotation.
*
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
{
$annotations = $this->getClassAnnotations($class);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Gets a method annotation.
*
* @param \ReflectionMethod $method
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
{
$annotations = $this->getMethodAnnotations($method);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Gets a property annotation.
*
* @param \ReflectionProperty $property
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
{
$annotations = $this->getPropertyAnnotations($property);
foreach ($annotations as $annotation) {
if ($annotation instanceof $annotationName) {
return $annotation;
}
}
return null;
}
/**
* Clear stores annotations
*/
public function clearLoadedAnnotations()
{
$this->loadedAnnotations = array();
}
}

View File

@@ -0,0 +1,141 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Reader;
/**
* Allows the reader to be used in-place of Doctrine's reader.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class IndexedReader implements Reader
{
/**
* @var Reader
*/
private $delegate;
/**
* Constructor
*
* @param Reader $reader
*/
public function __construct(Reader $reader)
{
$this->delegate = $reader;
}
/**
* Get Annotations for class
*
* @param \ReflectionClass $class
* @return array
*/
public function getClassAnnotations(\ReflectionClass $class)
{
$annotations = array();
foreach ($this->delegate->getClassAnnotations($class) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
/**
* Get selected annotation for class
*
* @param \ReflectionClass $class
* @param string $annotation
* @return mixed
*/
public function getClassAnnotation(\ReflectionClass $class, $annotation)
{
return $this->delegate->getClassAnnotation($class, $annotation);
}
/**
* Get Annotations for method
*
* @param \ReflectionMethod $method
* @return array
*/
public function getMethodAnnotations(\ReflectionMethod $method)
{
$annotations = array();
foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
/**
* Get selected annotation for method
*
* @param \ReflectionMethod $method
* @param string $annotation
* @return mixed
*/
public function getMethodAnnotation(\ReflectionMethod $method, $annotation)
{
return $this->delegate->getMethodAnnotation($method, $annotation);
}
/**
* Get annotations for property
*
* @param \ReflectionProperty $property
* @return array
*/
public function getPropertyAnnotations(\ReflectionProperty $property)
{
$annotations = array();
foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
$annotations[get_class($annot)] = $annot;
}
return $annotations;
}
/**
* Get selected annotation for property
*
* @param \ReflectionProperty $property
* @param string $annotation
* @return mixed
*/
public function getPropertyAnnotation(\ReflectionProperty $property, $annotation)
{
return $this->delegate->getPropertyAnnotation($property, $annotation);
}
/**
* Proxy all methods to the delegate.
*
* @param string $method
* @param array $args
* @return mixed
*/
public function __call($method, $args)
{
return call_user_func_array(array($this->delegate, $method), $args);
}
}

View File

@@ -0,0 +1,89 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use SplFileObject;
/**
* Parses a file for namespaces/use/class declarations.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Christian Kaps <christian.kaps@mohiva.com>
*/
final class PhpParser
{
/**
* Parses a class.
*
* @param \ReflectionClass $class A <code>ReflectionClass</code> object.
* @return array A list with use statements in the form (Alias => FQN).
*/
public function parseClass(\ReflectionClass $class)
{
if (method_exists($class, 'getUseStatements')) {
return $class->getUseStatements();
}
if (false === $filename = $class->getFilename()) {
return array();
}
$content = $this->getFileContent($filename, $class->getStartLine());
if (null === $content) {
return array();
}
$namespace = preg_quote($class->getNamespaceName());
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
$tokenizer = new TokenParser('<?php ' . $content);
$statements = $tokenizer->parseUseStatements($class->getNamespaceName());
return $statements;
}
/**
* Get the content of the file right up to the given line number.
*
* @param string $filename The name of the file to load.
* @param int $lineNumber The number of lines to read from file.
* @return string The content of the file.
*/
private function getFileContent($filename, $lineNumber)
{
if ( ! is_file($filename)) {
return null;
}
$content = '';
$lineCnt = 0;
$file = new SplFileObject($filename);
while (!$file->eof()) {
if ($lineCnt++ == $lineNumber) {
break;
}
$content .= $file->fgets();
}
return $content;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* Interface for annotation readers.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
interface Reader
{
/**
* @param \ReflectionClass $class
* @return mixed
*/
function getClassAnnotations(\ReflectionClass $class);
/**
* @param \ReflectionClass $class
* @param string $annotationName
* @return mixed
*/
function getClassAnnotation(\ReflectionClass $class, $annotationName);
/**
* @param \ReflectionMethod $method
* @return mixed
*/
function getMethodAnnotations(\ReflectionMethod $method);
/**
* @param \ReflectionMethod $method
* @param string $annotationName
* @return mixed
*/
function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
/**
* @param \ReflectionProperty $property
* @return mixed
*/
function getPropertyAnnotations(\ReflectionProperty $property);
/**
* @param \ReflectionProperty $property
* @param string $annotationName
* @return mixed
*/
function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
}

View File

@@ -0,0 +1,157 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Simple Annotation Reader.
*
* This annotation reader is intended to be used in projects where you have
* full-control over all annotations that are available.
*
* @since 2.2
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class SimpleAnnotationReader implements Reader
{
/**
* @var DocParser
*/
private $parser;
/**
* Constructor.
*
* Initializes a new SimpleAnnotationReader.
*/
public function __construct()
{
$this->parser = new DocParser();
$this->parser->setIgnoreNotImportedAnnotations(true);
}
/**
* Adds a namespace in which we will look for annotations.
*
* @param string $namespace
*/
public function addNamespace($namespace)
{
$this->parser->addNamespace($namespace);
}
/**
* Gets the annotations applied to a class.
*
* @param \ReflectionClass $class The ReflectionClass of the class from which
* the class annotations should be read.
*
* @return array An array of Annotations.
*/
public function getClassAnnotations(\ReflectionClass $class)
{
return $this->parser->parse($class->getDocComment(), 'class '.$class->getName());
}
/**
* Gets the annotations applied to a method.
*
* @param \ReflectionMethod $method The ReflectionMethod of the method from which
* the annotations should be read.
*
* @return array An array of Annotations.
*/
public function getMethodAnnotations(\ReflectionMethod $method)
{
return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()');
}
/**
* Gets the annotations applied to a property.
*
* @param \ReflectionProperty $property The ReflectionProperty of the property
* from which the annotations should be read.
*
* @return array An array of Annotations.
*/
public function getPropertyAnnotations(\ReflectionProperty $property)
{
return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName());
}
/**
* Gets a class annotation.
*
* @param \ReflectionClass $class The ReflectionClass of the class from which
* the class annotations should be read.
* @param string $annotationName The name of the annotation.
*
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
{
foreach ($this->getClassAnnotations($class) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
/**
* Gets a method annotation.
*
* @param \ReflectionMethod $method
* @param string $annotationName The name of the annotation.
*
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
{
foreach ($this->getMethodAnnotations($method) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
/**
* Gets a property annotation.
*
* @param \ReflectionProperty $property
* @param string $annotationName The name of the annotation.
* @return mixed The Annotation or NULL, if the requested annotation does not exist.
*/
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
{
foreach ($this->getPropertyAnnotations($property) as $annot) {
if ($annot instanceof $annotationName) {
return $annot;
}
}
return null;
}
}

View File

@@ -0,0 +1,175 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Annotations;
/**
* Parses a file for namespaces/use/class declarations.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Christian Kaps <christian.kaps@mohiva.com>
*/
class TokenParser
{
/**
* The token list.
*
* @var array
*/
private $tokens;
/**
* The number of tokens.
*
* @var int
*/
private $numTokens = 0;
/**
* The current array pointer.
*
* @var int
*/
private $pointer = 0;
public function __construct($contents)
{
$this->tokens = token_get_all($contents);
$this->numTokens = count($this->tokens);
$this->pointer = 0;
}
/**
* Gets the next non whitespace and non comment token.
*
* @param $docCommentIsComment
* If TRUE then a doc comment is considered a comment and skipped.
* If FALSE then only whitespace and normal comments are skipped.
*
* @return array The token if exists, null otherwise.
*/
public function next($docCommentIsComment = TRUE)
{
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
$this->pointer++;
if ($this->tokens[$i][0] === T_WHITESPACE ||
$this->tokens[$i][0] === T_COMMENT ||
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
continue;
}
return $this->tokens[$i];
}
return null;
}
/**
* Parse a single use statement.
*
* @return array A list with all found class names for a use statement.
*/
public function parseUseStatement()
{
$class = '';
$alias = '';
$statements = array();
$explicitAlias = false;
while (($token = $this->next())) {
$isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
if (!$explicitAlias && $isNameToken) {
$class .= $token[1];
$alias = $token[1];
} else if ($explicitAlias && $isNameToken) {
$alias .= $token[1];
} else if ($token[0] === T_AS) {
$explicitAlias = true;
$alias = '';
} else if ($token === ',') {
$statements[strtolower($alias)] = $class;
$class = '';
$alias = '';
$explicitAlias = false;
} else if ($token === ';') {
$statements[strtolower($alias)] = $class;
break;
} else {
break;
}
}
return $statements;
}
/**
* Get all use statements.
*
* @param string $namespaceName The namespace name of the reflected class.
* @return array A list with all found use statements.
*/
public function parseUseStatements($namespaceName)
{
$statements = array();
while (($token = $this->next())) {
if ($token[0] === T_USE) {
$statements = array_merge($statements, $this->parseUseStatement());
continue;
}
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
continue;
}
// Get fresh array for new namespace. This is to prevent the parser to collect the use statements
// for a previous namespace with the same name. This is the case if a namespace is defined twice
// or if a namespace with the same name is commented out.
$statements = array();
}
return $statements;
}
/**
* Get the namespace.
*
* @return string The found namespace.
*/
public function parseNamespace()
{
$name = '';
while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
$name .= $token[1];
}
return $name;
}
/**
* Get the class name.
*
* @return string The foundclass name.
*/
public function parseClass()
{
// Namespaces and class names are tokenized the same: T_STRINGs
// separated by T_NS_SEPARATOR so we can use one function to provide
// both.
return $this->parseNamespace();
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/Doctrine/Tests/TestInit.php"
>
<testsuites>
<testsuite name="Doctrine Annotations Test Suite">
<directory>./tests/Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./lib/Doctrine/</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
</exclude>
</groups>
</phpunit>

View File

@@ -0,0 +1,571 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\DoctrineReader;
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
use Doctrine\Common\Annotations\Annotation\IgnorePhpDoc;
use ReflectionClass, Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Tests\Common\Annotations\DummyAnnotation;
use Doctrine\Tests\Common\Annotations\Name;
use Doctrine\Tests\Common\Annotations\DummyId;
use Doctrine\Tests\Common\Annotations\DummyJoinTable;
use Doctrine\Tests\Common\Annotations\DummyJoinColumn;
use Doctrine\Tests\Common\Annotations\DummyColumn;
use Doctrine\Tests\Common\Annotations\DummyGeneratedValue;
require_once __DIR__ . '/TopLevelAnnotation.php';
abstract class AbstractReaderTest extends \PHPUnit_Framework_TestCase
{
public function getReflectionClass()
{
$className = 'Doctrine\Tests\Common\Annotations\DummyClass';
return new ReflectionClass($className);
}
public function testAnnotations()
{
$class = $this->getReflectionClass();
$reader = $this->getReader();
$this->assertEquals(1, count($reader->getClassAnnotations($class)));
$this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyAnnotation', $annot = $reader->getClassAnnotation($class, $annotName));
$this->assertEquals("hello", $annot->dummyValue);
$field1Prop = $class->getProperty('field1');
$propAnnots = $reader->getPropertyAnnotations($field1Prop);
$this->assertEquals(1, count($propAnnots));
$this->assertInstanceOf($annotName, $annot = $reader->getPropertyAnnotation($field1Prop, $annotName));
$this->assertEquals("fieldHello", $annot->dummyValue);
$getField1Method = $class->getMethod('getField1');
$methodAnnots = $reader->getMethodAnnotations($getField1Method);
$this->assertEquals(1, count($methodAnnots));
$this->assertInstanceOf($annotName, $annot = $reader->getMethodAnnotation($getField1Method, $annotName));
$this->assertEquals(array(1, 2, "three"), $annot->value);
$field2Prop = $class->getProperty('field2');
$propAnnots = $reader->getPropertyAnnotations($field2Prop);
$this->assertEquals(1, count($propAnnots));
$this->assertInstanceOf($annotName = 'Doctrine\Tests\Common\Annotations\DummyJoinTable', $joinTableAnnot = $reader->getPropertyAnnotation($field2Prop, $annotName));
$this->assertEquals(1, count($joinTableAnnot->joinColumns));
$this->assertEquals(1, count($joinTableAnnot->inverseJoinColumns));
$this->assertTrue($joinTableAnnot->joinColumns[0] instanceof DummyJoinColumn);
$this->assertTrue($joinTableAnnot->inverseJoinColumns[0] instanceof DummyJoinColumn);
$this->assertEquals('col1', $joinTableAnnot->joinColumns[0]->name);
$this->assertEquals('col2', $joinTableAnnot->joinColumns[0]->referencedColumnName);
$this->assertEquals('col3', $joinTableAnnot->inverseJoinColumns[0]->name);
$this->assertEquals('col4', $joinTableAnnot->inverseJoinColumns[0]->referencedColumnName);
$dummyAnnot = $reader->getMethodAnnotation($class->getMethod('getField1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
$this->assertEquals('', $dummyAnnot->dummyValue);
$this->assertEquals(array(1, 2, 'three'), $dummyAnnot->value);
$dummyAnnot = $reader->getPropertyAnnotation($class->getProperty('field1'), 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
$this->assertEquals('fieldHello', $dummyAnnot->dummyValue);
$classAnnot = $reader->getClassAnnotation($class, 'Doctrine\Tests\Common\Annotations\DummyAnnotation');
$this->assertEquals('hello', $classAnnot->dummyValue);
}
public function testAnnotationsWithValidTargets()
{
$reader = $this->getReader();
$class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithValidAnnotationTarget');
$this->assertEquals(1,count($reader->getClassAnnotations($class)));
$this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('foo'))));
$this->assertEquals(1,count($reader->getMethodAnnotations($class->getMethod('someFunction'))));
$this->assertEquals(1,count($reader->getPropertyAnnotations($class->getProperty('nested'))));
}
public function testAnnotationsWithVarType()
{
$reader = $this->getReader();
$class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
$this->assertEquals(1,count($fooAnnot = $reader->getPropertyAnnotations($class->getProperty('foo'))));
$this->assertEquals(1,count($barAnnot = $reader->getMethodAnnotations($class->getMethod('bar'))));
$this->assertInternalType('string', $fooAnnot[0]->string);
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll', $barAnnot[0]->annotation);
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetPropertyMethod is not allowed to be declared on class Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass. You may only use this annotation on these code elements: METHOD, PROPERTY
*/
public function testClassWithInvalidAnnotationTargetAtClassDocBlock()
{
$reader = $this->getReader();
$reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtClass'));
}
public function testClassWithWithInclude()
{
$reader = $this->getReader();
$annots = $reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithRequire'));
$this->assertCount(1, $annots);
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$foo. You may only use this annotation on these code elements: CLASS
*/
public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'foo'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetAnnotation is not allowed to be declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty::$bar. You may only use this annotation on these code elements: ANNOTATION
*/
public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtProperty', 'bar'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Semantical Error] Annotation @AnnotationTargetClass is not allowed to be declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod::functionName(). You may only use this annotation on these code elements: CLASS
*/
public function testClassWithInvalidAnnotationTargetAtMethodDocBlock()
{
$reader = $this->getReader();
$reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithInvalidAnnotationTargetAtMethod', 'functionName'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
*/
public function testClassWithAnnotationWithTargetSyntaxErrorAtClassDocBlock()
{
$reader = $this->getReader();
$reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
*/
public function testClassWithAnnotationWithTargetSyntaxErrorAtPropertyDocBlock()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','foo'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 24 in class @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError.
*/
public function testClassWithAnnotationWithTargetSyntaxErrorAtMethodDocBlock()
{
$reader = $this->getReader();
$reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithTargetSyntaxError','bar'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Type Error] Attribute "string" of @AnnotationWithVarType declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::$invalidProperty expects a(n) string, but got integer.
*/
public function testClassWithPropertyInvalidVarTypeError()
{
$reader = $this->getReader();
$class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
$reader->getPropertyAnnotations($class->getProperty('invalidProperty'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Type Error] Attribute "annotation" of @AnnotationWithVarType declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType::invalidMethod() expects a(n) Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll, but got an instance of Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation.
*/
public function testClassWithMethodInvalidVarTypeError()
{
$reader = $this->getReader();
$class = new ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationWithVarType');
$reader->getMethodAnnotations($class->getMethod('invalidMethod'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in class Doctrine\Tests\Common\Annotations\DummyClassSyntaxError.
*/
public function testClassSyntaxErrorContext()
{
$reader = $this->getReader();
$reader->getClassAnnotations(new \ReflectionClass('Doctrine\Tests\Common\Annotations\DummyClassSyntaxError'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in method Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError::foo().
*/
public function testMethodSyntaxErrorContext()
{
$reader = $this->getReader();
$reader->getMethodAnnotations(new \ReflectionMethod('Doctrine\Tests\Common\Annotations\DummyClassMethodSyntaxError', 'foo'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage Expected namespace separator or identifier, got ')' at position 18 in property Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError::$foo.
*/
public function testPropertySyntaxErrorContext()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassPropertySyntaxError', 'foo'));
}
/**
* @group regression
*/
public function testMultipleAnnotationsOnSameLine()
{
$reader = $this->getReader();
$annots = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClass2', 'id'));
$this->assertEquals(3, count($annots));
}
public function testNonAnnotationProblem()
{
$reader = $this->getReader();
$this->assertNotNull($annot = $reader->getPropertyAnnotation(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\DummyClassNonAnnotationProblem', 'foo'), $name = 'Doctrine\Tests\Common\Annotations\DummyAnnotation'));
$this->assertInstanceOf($name, $annot);
}
public function testImportWithConcreteAnnotation()
{
$reader = $this->getReader();
$property = new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestImportWithConcreteAnnotation', 'field');
$annotations = $reader->getPropertyAnnotations($property);
$this->assertEquals(1, count($annotations));
$this->assertNotNull($reader->getPropertyAnnotation($property, 'Doctrine\Tests\Common\Annotations\DummyAnnotation'));
}
public function testImportWithInheritance()
{
$reader = $this->getReader();
$class = new TestParentClass();
$ref = new \ReflectionClass($class);
$childAnnotations = $reader->getPropertyAnnotations($ref->getProperty('child'));
$this->assertEquals(1, count($childAnnotations));
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Foo\Name', reset($childAnnotations));
$parentAnnotations = $reader->getPropertyAnnotations($ref->getProperty('parent'));
$this->assertEquals(1, count($parentAnnotations));
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Bar\Name', reset($parentAnnotations));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage The annotation "@NameFoo" in property Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass::$field was never imported.
*/
public function testImportDetectsNotImportedAnnotation()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestAnnotationNotImportedClass', 'field'));
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage The annotation "@Foo\Bar\Name" in property Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass::$field was never imported.
*/
public function testImportDetectsNonExistentAnnotation()
{
$reader = $this->getReader();
$reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestNonExistentAnnotationClass', 'field'));
}
public function testTopLevelAnnotation()
{
$reader = $this->getReader();
$annotations = $reader->getPropertyAnnotations(new \ReflectionProperty('Doctrine\Tests\Common\Annotations\TestTopLevelAnnotationClass', 'field'));
$this->assertEquals(1, count($annotations));
$this->assertInstanceOf('\TopLevelAnnotation', reset($annotations));
}
public function testIgnoresAnnotationsNotPrefixedWithWhitespace()
{
$reader = $this->getReader();
$annotation = $reader->getClassAnnotation(new \ReflectionClass(new TestIgnoresNonAnnotationsClass()), 'Doctrine\Tests\Common\Annotations\Name');
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Name', $annotation);
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage The class "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Doctrine\Tests\Common\Annotations\Fixtures\NoAnnotation". If it is indeed no annotation, then you need to add @IgnoreAnnotation("NoAnnotation") to the _class_ doc comment of class Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass.
*/
public function testErrorWhenInvalidAnnotationIsUsed()
{
$reader = $this->getReader();
$ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageClass');
$reader->getClassAnnotations($ref);
}
public function testInvalidAnnotationUsageButIgnoredClass()
{
$reader = $this->getReader();
$ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\InvalidAnnotationUsageButIgnoredClass');
$annots = $reader->getClassAnnotations($ref);
$this->assertEquals(2, count($annots));
}
/**
* @group DDC-1660
* @group regression
*/
public function testInvalidAnnotationButIgnored()
{
$reader = $this->getReader();
$class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660');
$this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version'));
$this->assertCount(0, $reader->getClassAnnotations($class));
$this->assertCount(0, $reader->getMethodAnnotations($class->getMethod('bar')));
$this->assertCount(0, $reader->getPropertyAnnotations($class->getProperty('foo')));
}
public function testAnnotationEnumeratorException()
{
$reader = $this->getReader();
$class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum');
$this->assertCount(1, $bar = $reader->getMethodAnnotations($class->getMethod('bar')));
$this->assertCount(1, $foo = $reader->getPropertyAnnotations($class->getProperty('foo')));
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $bar[0]);
$this->assertInstanceOf('Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum', $foo[0]);
try {
$reader->getPropertyAnnotations($class->getProperty('invalidProperty'));
$this->fail();
} catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
$this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::$invalidProperty accept only [ONE, TWO, THREE], but got FOUR.', $exc->getMessage());
}
try {
$reader->getMethodAnnotations($class->getMethod('invalidMethod'));
$this->fail();
} catch (\Doctrine\Common\Annotations\AnnotationException $exc) {
$this->assertEquals('[Enum Error] Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on method Doctrine\Tests\Common\Annotations\Fixtures\ClassWithAnnotationEnum::invalidMethod() accept only [ONE, TWO, THREE], but got 5.', $exc->getMessage());
}
}
/**
* @group DCOM-106
*/
public function testIgnoreFixMeAndUpperCaseToDo()
{
$reader = $this->getReader();
$ref = new \ReflectionClass('Doctrine\Tests\Common\Annotations\DCOM106');
$reader->getClassAnnotations($ref);
}
/**
* @return AnnotationReader
*/
abstract protected function getReader();
}
/**
* @parseAnnotation("var")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*
*/
class TestParseAnnotationClass
{
/**
* @var
*/
private $field;
}
/**
* @Name
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class TestIgnoresNonAnnotationsClass
{
}
class TestTopLevelAnnotationClass
{
/**
* @\TopLevelAnnotation
*/
private $field;
}
class TestNonExistentAnnotationClass
{
/**
* @Foo\Bar\Name
*/
private $field;
}
class TestAnnotationNotImportedClass
{
/**
* @NameFoo
*/
private $field;
}
class TestChildClass
{
/**
* @\Doctrine\Tests\Common\Annotations\Foo\Name(name = "foo")
*/
protected $child;
}
class TestParentClass extends TestChildClass
{
/**
* @\Doctrine\Tests\Common\Annotations\Bar\Name(name = "bar")
*/
private $parent;
}
class TestImportWithConcreteAnnotation
{
/**
* @DummyAnnotation(dummyValue = "bar")
*/
private $field;
}
/**
* @ignoreAnnotation("var")
*/
class DummyClass2 {
/**
* @DummyId @DummyColumn(type="integer") @DummyGeneratedValue
* @var integer
*/
private $id;
}
/** @Annotation */
class DummyId extends \Doctrine\Common\Annotations\Annotation {}
/** @Annotation */
class DummyColumn extends \Doctrine\Common\Annotations\Annotation {
public $type;
}
/** @Annotation */
class DummyGeneratedValue extends \Doctrine\Common\Annotations\Annotation {}
/** @Annotation */
class DummyAnnotation extends \Doctrine\Common\Annotations\Annotation {
public $dummyValue;
}
/**
* @api
* @Annotation
*/
class DummyAnnotationWithIgnoredAnnotation extends \Doctrine\Common\Annotations\Annotation {
public $dummyValue;
}
/** @Annotation */
class DummyJoinColumn extends \Doctrine\Common\Annotations\Annotation {
public $name;
public $referencedColumnName;
}
/** @Annotation */
class DummyJoinTable extends \Doctrine\Common\Annotations\Annotation {
public $name;
public $joinColumns;
public $inverseJoinColumns;
}
/**
* @DummyAnnotation(@)
*/
class DummyClassSyntaxError
{
}
class DummyClassMethodSyntaxError
{
/**
* @DummyAnnotation(@)
*/
public function foo()
{
}
}
class DummyClassPropertySyntaxError
{
/**
* @DummyAnnotation(@)
*/
public $foo;
}
/**
* @ignoreAnnotation({"since", "var"})
*/
class DummyClassNonAnnotationProblem
{
/**
* @DummyAnnotation
*
* @var \Test
* @since 0.1
*/
public $foo;
}
/**
* @DummyAnnotation Foo bar <foobar@1domain.com>
*/
class DummyClassWithEmail
{
}
/**
* @fixme public
* @TODO
*/
class DCOM106
{
}
namespace Doctrine\Tests\Common\Annotations\Foo;
/** @Annotation */
class Name extends \Doctrine\Common\Annotations\Annotation
{
public $name;
}
namespace Doctrine\Tests\Common\Annotations\Bar;
/** @Annotation */
class Name extends \Doctrine\Common\Annotations\Annotation
{
public $name;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\AnnotationReader;
class AnnotationReaderTest extends AbstractReaderTest
{
protected function getReader()
{
return new AnnotationReader();
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Cache\ArrayCache;
class CachedReaderTest extends AbstractReaderTest
{
private $cache;
public function testIgnoresStaleCache()
{
$file = __DIR__.'/Fixtures/Controller.php';
touch($file);
$name = 'Doctrine\Tests\Common\Annotations\Fixtures\Controller';
$cacheKey = $name.'@[Annot]';
$cache = $this->getMock('Doctrine\Common\Cache\Cache');
$cache
->expects($this->at(0))
->method('fetch')
->with($this->equalTo($cacheKey))
->will($this->returnValue(array()))
;
$cache
->expects($this->at(1))
->method('fetch')
->with($this->equalTo('[C]'.$cacheKey))
->will($this->returnValue(time() - 10))
;
$cache
->expects($this->at(2))
->method('save')
->with($this->equalTo($cacheKey))
;
$cache
->expects($this->at(3))
->method('save')
->with($this->equalTo('[C]'.$cacheKey))
;
$reader = new CachedReader(new AnnotationReader(), $cache, true);
$route = new Route();
$route->pattern = '/someprefix';
$this->assertEquals(array($route), $reader->getClassAnnotations(new \ReflectionClass($name)));
}
protected function getReader()
{
$this->cache = new ArrayCache();
return new CachedReader(new AnnotationReader(), $this->cache);
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\DocLexer;
class DocLexerTest extends \PHPUnit_Framework_TestCase
{
public function testMarkerAnnotation()
{
$lexer = new DocLexer;
$lexer->setInput("@Name");
$this->assertNull($lexer->token);
$this->assertNull($lexer->lookahead);
$this->assertTrue($lexer->moveNext());
$this->assertNull($lexer->token);
$this->assertEquals('@', $lexer->lookahead['value']);
$this->assertTrue($lexer->moveNext());
$this->assertEquals('@', $lexer->token['value']);
$this->assertEquals('Name', $lexer->lookahead['value']);
$this->assertFalse($lexer->moveNext());
}
public function testScannerTokenizesDocBlockWhitConstants()
{
$lexer = new DocLexer();
$docblock = '@AnnotationWithConstants(PHP_EOL, ClassWithConstants::SOME_VALUE, \Doctrine\Tests\Common\Annotations\Fixtures\IntefaceWithConstants::SOME_VALUE)';
$tokens = array (
array(
'value' => '@',
'position' => 0,
'type' => DocLexer::T_AT,
),
array(
'value' => 'AnnotationWithConstants',
'position' => 1,
'type' => DocLexer::T_IDENTIFIER,
),
array(
'value' => '(',
'position' => 24,
'type' => DocLexer::T_OPEN_PARENTHESIS,
),
array(
'value' => 'PHP_EOL',
'position' => 25,
'type' => DocLexer::T_IDENTIFIER,
),
array(
'value' => ',',
'position' => 32,
'type' => DocLexer::T_COMMA,
),
array(
'value' => 'ClassWithConstants::SOME_VALUE',
'position' => 34,
'type' => DocLexer::T_IDENTIFIER,
),
array(
'value' => ',',
'position' => 64,
'type' => DocLexer::T_COMMA,
),
array(
'value' => '\\Doctrine\\Tests\\Common\\Annotations\\Fixtures\\IntefaceWithConstants::SOME_VALUE',
'position' => 66,
'type' => DocLexer::T_IDENTIFIER,
),
array(
'value' => ')',
'position' => 143,
'type' => DocLexer::T_CLOSE_PARENTHESIS,
)
);
$lexer->setInput($docblock);
foreach ($tokens as $expected) {
$lexer->moveNext();
$lookahead = $lexer->lookahead;
$this->assertEquals($expected['value'], $lookahead['value']);
$this->assertEquals($expected['type'], $lookahead['type']);
$this->assertEquals($expected['position'], $lookahead['position']);
}
$this->assertFalse($lexer->moveNext());
}
public function testScannerTokenizesDocBlockWhitInvalidIdentifier()
{
$lexer = new DocLexer();
$docblock = '@Foo\3.42';
$tokens = array (
array(
'value' => '@',
'position' => 0,
'type' => DocLexer::T_AT,
),
array(
'value' => 'Foo',
'position' => 1,
'type' => DocLexer::T_IDENTIFIER,
),
array(
'value' => '\\',
'position' => 4,
'type' => DocLexer::T_NAMESPACE_SEPARATOR,
),
array(
'value' => 3.42,
'position' => 5,
'type' => DocLexer::T_FLOAT,
)
);
$lexer->setInput($docblock);
foreach ($tokens as $expected) {
$lexer->moveNext();
$lookahead = $lexer->lookahead;
$this->assertEquals($expected['value'], $lookahead['value']);
$this->assertEquals($expected['type'], $lookahead['type']);
$this->assertEquals($expected['position'], $lookahead['position']);
}
$this->assertFalse($lexer->moveNext());
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Tests\Common\Annotations\DummyAnnotation;
use Doctrine\Tests\Common\Annotations\Name;
use Doctrine\Tests\Common\Annotations\DummyJoinTable;
use Doctrine\Tests\Common\Annotations\DummyJoinColumn;
/**
* A description of this class.
*
* Let's see if the parser recognizes that this @ is not really referring to an
* annotation. Also make sure that @var \ is not concated to "@var\is".
*
* @author robo
* @since 2.0
* @DummyAnnotation(dummyValue="hello")
*/
class DummyClass
{
/**
* A nice property.
*
* @var mixed
* @DummyAnnotation(dummyValue="fieldHello")
*/
private $field1;
/**
* @DummyJoinTable(name="join_table",
* joinColumns={@DummyJoinColumn(name="col1", referencedColumnName="col2")},
* inverseJoinColumns={
* @DummyJoinColumn(name="col3", referencedColumnName="col4")
* })
*/
private $field2;
/**
* Gets the value of field1.
*
* @return mixed
* @DummyAnnotation({1,2,"three"})
*/
public function getField1()
{
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\FileCacheReader;
class FileCacheReaderTest extends AbstractReaderTest
{
private $cacheDir;
protected function getReader()
{
$this->cacheDir = sys_get_temp_dir() . "/annotations_". uniqid();
@mkdir($this->cacheDir);
return new FileCacheReader(new AnnotationReader(), $this->cacheDir);
}
public function tearDown()
{
foreach (glob($this->cacheDir.'/*.php') AS $file) {
unlink($file);
}
rmdir($this->cacheDir);
}
/**
* @group DCOM-81
*/
public function testAttemptToCreateAnnotationCacheDir()
{
$this->cacheDir = sys_get_temp_dir() . "/not_existed_dir_". uniqid();
$this->assertFalse(is_dir($this->cacheDir));
$cache = new FileCacheReader(new AnnotationReader(), $this->cacheDir);
$this->assertTrue(is_dir($this->cacheDir));
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/** @Annotation */
class AnnotWithDefaultValue
{
/** @var string */
public $foo = 'bar';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/**
* @Annotation
*/
class Autoload
{
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/** @Annotation */
class Route
{
/** @var string @Required */
public $pattern;
public $name;
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/** @Annotation */
class Secure
{
private $roles;
public function __construct(array $values)
{
if (is_string($values['value'])) {
$values['value'] = array($values['value']);
}
$this->roles = $values['value'];
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/** @Annotation */
class Template
{
private $name;
public function __construct(array $values)
{
$this->name = isset($values['value']) ? $values['value'] : null;
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Annotation;
/**
* @Annotation
* @Target("PROPERTY")
*/
final class Version
{
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationEnum
{
const ONE = 'ONE';
const TWO = 'TWO';
const THREE = 'THREE';
/**
* @var mixed
*
* @Enum({"ONE","TWO","THREE"})
*/
public $value;
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationEnumInvalid
{
/**
* @var mixed
*
* @Enum({1, 2, "foo", "bar", {"foo":"bar"}})
*/
public $value;
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral as SelfEnum;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationEnumLiteral
{
const ONE = 1;
const TWO = 2;
const THREE = 3;
/**
* @var mixed
*
* @Enum(
* value = {
* 1,
* 2,
* 3,
* },
* literal = {
* 1 : "AnnotationEnumLiteral::ONE",
* 2 : "AnnotationEnumLiteral::TWO",
* 3 : "AnnotationEnumLiteral::THREE",
* }
* )
*/
public $value;
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationEnumLiteralInvalid
{
const ONE = 1;
const TWO = 2;
const THREE = 3;
/**
* @var mixed
*
* @Enum(
* value = {
* 1,
* 2
* },
* literal = {
* 1 : "AnnotationEnumLiteral::ONE",
* 2 : "AnnotationEnumLiteral::TWO",
* 3 : "AnnotationEnumLiteral::THREE"
* }
* )
*/
public $value;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
class AnnotationTargetAll
{
public $data;
public $name;
public $target;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target({ "ANNOTATION" })
*/
final class AnnotationTargetAnnotation
{
public $data;
public $name;
public $target;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("CLASS")
*/
final class AnnotationTargetClass
{
public $data;
public $name;
public $target;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("METHOD")
*/
final class AnnotationTargetMethod
{
public $data;
public $name;
public $target;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target({ "METHOD", "PROPERTY" })
*/
final class AnnotationTargetPropertyMethod
{
public $data;
public $name;
public $target;
}

View File

@@ -0,0 +1,119 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
* @Attributes({
@Attribute("mixed", type = "mixed"),
@Attribute("boolean", type = "boolean"),
@Attribute("bool", type = "bool"),
@Attribute("float", type = "float"),
@Attribute("string", type = "string"),
@Attribute("integer", type = "integer"),
@Attribute("array", type = "array"),
@Attribute("arrayOfIntegers", type = "array<integer>"),
@Attribute("annotation", type = "Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll"),
@Attribute("arrayOfAnnotations", type = "array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>"),
})
*/
final class AnnotationWithAttributes
{
public final function __construct(array $data)
{
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
private $mixed;
private $boolean;
private $bool;
private $float;
private $string;
private $integer;
private $array;
private $annotation;
private $arrayOfIntegers;
private $arrayOfAnnotations;
/**
* @return mixed
*/
public function getMixed()
{
return $this->mixed;
}
/**
* @return boolean
*/
public function getBoolean()
{
return $this->boolean;
}
/**
* @return bool
*/
public function getBool()
{
return $this->bool;
}
/**
* @return float
*/
public function getFloat()
{
return $this->float;
}
/**
* @return string
*/
public function getString()
{
return $this->string;
}
public function getInteger()
{
return $this->integer;
}
/**
* @return array
*/
public function getArray()
{
return $this->array;
}
/**
* @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll
*/
public function getAnnotation()
{
return $this->annotation;
}
/**
* @return array<integer>
*/
public function getArrayOfIntegers()
{
return $this->arrayOfIntegers;
}
/**
* @return array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>
*/
public function getArrayOfAnnotations()
{
return $this->arrayOfAnnotations;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationWithConstants
{
const INTEGER = 1;
const FLOAT = 1.2;
const STRING = '1.2.3';
/**
* @var mixed
*/
public $value;
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
* @Attributes({
@Attribute("value", required = true , type = "string"),
@Attribute("annot", required = true , type = "Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation"),
})
*/
final class AnnotationWithRequiredAttributes
{
public final function __construct(array $data)
{
foreach ($data as $key => $value) {
$this->$key = $value;
}
}
/**
* @var string
*/
private $value;
/**
*
* @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
*/
private $annot;
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @return Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
*/
public function getAnnot()
{
return $this->annot;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationWithRequiredAttributesWithoutContructor
{
/**
* @Required
* @var string
*/
public $value;
/**
* @Required
* @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation
*/
public $annot;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target(@)
*/
final class AnnotationWithTargetSyntaxError
{
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @Annotation
* @Target("ALL")
*/
final class AnnotationWithVarType
{
/**
* @var mixed
*/
public $mixed;
/**
* @var boolean
*/
public $boolean;
/**
* @var bool
*/
public $bool;
/**
* @var float
*/
public $float;
/**
* @var string
*/
public $string;
/**
* @var integer
*/
public $integer;
/**
* @var array
*/
public $array;
/**
* @var Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll
*/
public $annotation;
/**
* @var array<integer>
*/
public $arrayOfIntegers;
/**
* @var array<Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll>
*/
public $arrayOfAnnotations;
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* This class is not an annotation
* It's a class build to test ClassWithInclude
*/
class Api
{
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @since 2.0
* @version $Id: SomeEntityClass.php 509 2012-02-03 09:38:48Z mf $
*/
class ClassDDC1660
{
/**
* @var string
* @since 2.0
* @version 1
*/
public $foo;
/**
* @param string
* @return string
* @since 2.0
* @version 1
*/
public function bar($param)
{
return null;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum;
class ClassWithAnnotationEnum
{
/**
* @AnnotationEnum(AnnotationEnum::ONE)
*/
public $foo;
/**
* @AnnotationEnum("TWO")
*/
public function bar(){}
/**
* @AnnotationEnum("FOUR")
*/
public $invalidProperty;
/**
* @AnnotationEnum(5)
*/
public function invalidMethod(){}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithTargetSyntaxError;
/**
* @AnnotationWithTargetSyntaxError()
*/
class ClassWithAnnotationWithTargetSyntaxError
{
/**
* @AnnotationWithTargetSyntaxError()
*/
public $foo;
/**
* @AnnotationWithTargetSyntaxError()
*/
public function bar(){}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationWithVarType;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
class ClassWithAnnotationWithVarType
{
/**
* @AnnotationWithVarType(string = "String Value")
*/
public $foo;
/**
* @AnnotationWithVarType(annotation = @AnnotationTargetAll)
*/
public function bar(){}
/**
* @AnnotationWithVarType(string = 123)
*/
public $invalidProperty;
/**
* @AnnotationWithVarType(annotation = @AnnotationTargetAnnotation)
*/
public function invalidMethod(){}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
/**
* @AnnotationTargetAll("Foo")
*/
final class ClassWithClosure
{
/**
* @AnnotationTargetAll(@AnnotationTargetAnnotation)
* @var string
*/
public $value;
/**
* @AnnotationTargetAll(@AnnotationTargetAnnotation)
*
* @param \Closure $callback
* @return \Closure
*/
public function methodName(\Closure $callback)
{
$self = $this;
return function() use ($self, $callback) {
return $callback;
};
}
/**
* @param integer $year
* @param integer $month
* @param integer $day
* @return \Doctrine\Common\Collections\ArrayCollection
*/
public function getEventsForDate($year, $month, $day){
$extractEvents = null; // check if date of item is inside day given
$extractEvents = $this->events->filter(function ($item) use ($year, $month, $day) {
$leftDate = new \DateTime($year.'-'.$month.'-'.$day.' 00:00');
$rigthDate = new \DateTime($year.'-'.$month.'-'.$day.' +1 day 00:00');
return ( ( $leftDate <= $item->getDateStart() ) && ( $item->getDateStart() < $rigthDate ) );
}
);
return $extractEvents;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
class ClassWithConstants
{
const SOME_VALUE = 'ClassWithConstants.SOME_VALUE';
const SOME_KEY = 'ClassWithConstants.SOME_KEY';
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use
\Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure,
\Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route
;
use \Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
class ClassWithFullyQualifiedUseStatements {}

View File

@@ -0,0 +1,17 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetPropertyMethod;
/**
* @AnnotationTargetPropertyMethod("Some data")
*/
class ClassWithInvalidAnnotationTargetAtClass
{
/**
* @AnnotationTargetPropertyMethod("Bar")
*/
public $foo;
}

View File

@@ -0,0 +1,20 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
/**
* @AnnotationTargetClass("Some data")
*/
class ClassWithInvalidAnnotationTargetAtMethod
{
/**
* @AnnotationTargetClass("functionName")
*/
public function functionName($param)
{
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAnnotation;
/**
* @AnnotationTargetClass("Some data")
*/
class ClassWithInvalidAnnotationTargetAtProperty
{
/**
* @AnnotationTargetClass("Bar")
*/
public $foo;
/**
* @AnnotationTargetAnnotation("Foo")
*/
public $bar;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
// Include a class named Api
require_once(__DIR__ . '/Api.php');
use Doctrine\Tests\Common\Annotations\DummyAnnotationWithIgnoredAnnotation;
/**
* @DummyAnnotationWithIgnoredAnnotation(dummyValue="hello")
*/
class ClassWithRequire
{
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetClass;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetAll;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetPropertyMethod;
use Doctrine\Tests\Common\Annotations\Fixtures\AnnotationTargetNestedAnnotation;
/**
* @AnnotationTargetClass("Some data")
*/
class ClassWithValidAnnotationTarget
{
/**
* @AnnotationTargetPropertyMethod("Some data")
*/
public $foo;
/**
* @AnnotationTargetAll("Some data",name="Some name")
*/
public $name;
/**
* @AnnotationTargetPropertyMethod("Some data",name="Some name")
*/
public function someFunction()
{
}
/**
* @AnnotationTargetAll(@AnnotationTargetAnnotation)
*/
public $nested;
}

View File

@@ -0,0 +1,300 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
/**
* @Route("/someprefix")
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class Controller
{
/**
* @Route("/", name="_demo")
* @Template()
*/
public function indexAction()
{
return array();
}
/**
* @Route("/hello/{name}", name="_demo_hello")
* @Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
/**
* @Route("/contact", name="_demo_contact")
* @Template()
*/
public function contactAction()
{
$form = ContactForm::create($this->get('form.context'), 'contact');
$form->bind($this->container->get('request'), $form);
if ($form->isValid()) {
$form->send($this->get('mailer'));
$this->get('session')->setFlash('notice', 'Message sent!');
return new RedirectResponse($this->generateUrl('_demo'));
}
return array('form' => $form);
}
/**
* Creates the ACL for the passed object identity
*
* @param ObjectIdentityInterface $oid
* @return void
*/
private function createObjectIdentity(ObjectIdentityInterface $oid)
{
$classId = $this->createOrRetrieveClassId($oid->getType());
$this->connection->executeQuery($this->getInsertObjectIdentitySql($oid->getIdentifier(), $classId, true));
}
/**
* Returns the primary key for the passed class type.
*
* If the type does not yet exist in the database, it will be created.
*
* @param string $classType
* @return integer
*/
private function createOrRetrieveClassId($classType)
{
if (false !== $id = $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn()) {
return $id;
}
$this->connection->executeQuery($this->getInsertClassSql($classType));
return $this->connection->executeQuery($this->getSelectClassIdSql($classType))->fetchColumn();
}
/**
* Returns the primary key for the passed security identity.
*
* If the security identity does not yet exist in the database, it will be
* created.
*
* @param SecurityIdentityInterface $sid
* @return integer
*/
private function createOrRetrieveSecurityIdentityId(SecurityIdentityInterface $sid)
{
if (false !== $id = $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn()) {
return $id;
}
$this->connection->executeQuery($this->getInsertSecurityIdentitySql($sid));
return $this->connection->executeQuery($this->getSelectSecurityIdentityIdSql($sid))->fetchColumn();
}
/**
* Deletes all ACEs for the given object identity primary key.
*
* @param integer $oidPK
* @return void
*/
private function deleteAccessControlEntries($oidPK)
{
$this->connection->executeQuery($this->getDeleteAccessControlEntriesSql($oidPK));
}
/**
* Deletes the object identity from the database.
*
* @param integer $pk
* @return void
*/
private function deleteObjectIdentity($pk)
{
$this->connection->executeQuery($this->getDeleteObjectIdentitySql($pk));
}
/**
* Deletes all entries from the relations table from the database.
*
* @param integer $pk
* @return void
*/
private function deleteObjectIdentityRelations($pk)
{
$this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk));
}
/**
* This regenerates the ancestor table which is used for fast read access.
*
* @param AclInterface $acl
* @return void
*/
private function regenerateAncestorRelations(AclInterface $acl)
{
$pk = $acl->getId();
$this->connection->executeQuery($this->getDeleteObjectIdentityRelationsSql($pk));
$this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $pk));
$parentAcl = $acl->getParentAcl();
while (null !== $parentAcl) {
$this->connection->executeQuery($this->getInsertObjectIdentityRelationSql($pk, $parentAcl->getId()));
$parentAcl = $parentAcl->getParentAcl();
}
}
/**
* This processes changes on an ACE related property (classFieldAces, or objectFieldAces).
*
* @param string $name
* @param array $changes
* @return void
*/
private function updateFieldAceProperty($name, array $changes)
{
$sids = new \SplObjectStorage();
$classIds = new \SplObjectStorage();
$currentIds = array();
foreach ($changes[1] as $field => $new) {
for ($i=0,$c=count($new); $i<$c; $i++) {
$ace = $new[$i];
if (null === $ace->getId()) {
if ($sids->contains($ace->getSecurityIdentity())) {
$sid = $sids->offsetGet($ace->getSecurityIdentity());
} else {
$sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity());
}
$oid = $ace->getAcl()->getObjectIdentity();
if ($classIds->contains($oid)) {
$classId = $classIds->offsetGet($oid);
} else {
$classId = $this->createOrRetrieveClassId($oid->getType());
}
$objectIdentityId = $name === 'classFieldAces' ? null : $ace->getAcl()->getId();
$this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, $field, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure()));
$aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, $field, $i))->fetchColumn();
$this->loadedAces[$aceId] = $ace;
$aceIdProperty = new \ReflectionProperty('Symfony\Component\Security\Acl\Domain\Entry', 'id');
$aceIdProperty->setAccessible(true);
$aceIdProperty->setValue($ace, intval($aceId));
} else {
$currentIds[$ace->getId()] = true;
}
}
}
foreach ($changes[0] as $old) {
for ($i=0,$c=count($old); $i<$c; $i++) {
$ace = $old[$i];
if (!isset($currentIds[$ace->getId()])) {
$this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId()));
unset($this->loadedAces[$ace->getId()]);
}
}
}
}
/**
* This processes changes on an ACE related property (classAces, or objectAces).
*
* @param string $name
* @param array $changes
* @return void
*/
private function updateAceProperty($name, array $changes)
{
list($old, $new) = $changes;
$sids = new \SplObjectStorage();
$classIds = new \SplObjectStorage();
$currentIds = array();
for ($i=0,$c=count($new); $i<$c; $i++) {
$ace = $new[$i];
if (null === $ace->getId()) {
if ($sids->contains($ace->getSecurityIdentity())) {
$sid = $sids->offsetGet($ace->getSecurityIdentity());
} else {
$sid = $this->createOrRetrieveSecurityIdentityId($ace->getSecurityIdentity());
}
$oid = $ace->getAcl()->getObjectIdentity();
if ($classIds->contains($oid)) {
$classId = $classIds->offsetGet($oid);
} else {
$classId = $this->createOrRetrieveClassId($oid->getType());
}
$objectIdentityId = $name === 'classAces' ? null : $ace->getAcl()->getId();
$this->connection->executeQuery($this->getInsertAccessControlEntrySql($classId, $objectIdentityId, null, $i, $sid, $ace->getStrategy(), $ace->getMask(), $ace->isGranting(), $ace->isAuditSuccess(), $ace->isAuditFailure()));
$aceId = $this->connection->executeQuery($this->getSelectAccessControlEntryIdSql($classId, $objectIdentityId, null, $i))->fetchColumn();
$this->loadedAces[$aceId] = $ace;
$aceIdProperty = new \ReflectionProperty($ace, 'id');
$aceIdProperty->setAccessible(true);
$aceIdProperty->setValue($ace, intval($aceId));
} else {
$currentIds[$ace->getId()] = true;
}
}
for ($i=0,$c=count($old); $i<$c; $i++) {
$ace = $old[$i];
if (!isset($currentIds[$ace->getId()])) {
$this->connection->executeQuery($this->getDeleteAccessControlEntrySql($ace->getId()));
unset($this->loadedAces[$ace->getId()]);
}
}
}
/**
* Persists the changes which were made to ACEs to the database.
*
* @param \SplObjectStorage $aces
* @return void
*/
private function updateAces(\SplObjectStorage $aces)
{
foreach ($aces as $ace) {
$propertyChanges = $aces->offsetGet($ace);
$sets = array();
if (isset($propertyChanges['mask'])) {
$sets[] = sprintf('mask = %d', $propertyChanges['mask'][1]);
}
if (isset($propertyChanges['strategy'])) {
$sets[] = sprintf('granting_strategy = %s', $this->connection->quote($propertyChanges['strategy']));
}
if (isset($propertyChanges['aceOrder'])) {
$sets[] = sprintf('ace_order = %d', $propertyChanges['aceOrder'][1]);
}
if (isset($propertyChanges['auditSuccess'])) {
$sets[] = sprintf('audit_success = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditSuccess'][1]));
}
if (isset($propertyChanges['auditFailure'])) {
$sets[] = sprintf('audit_failure = %s', $this->connection->getDatabasePlatform()->convertBooleans($propertyChanges['auditFailure'][1]));
}
$this->connection->executeQuery($this->getUpdateAccessControlEntrySql($ace->getId(), $sets));
}
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
class DifferentNamespacesPerFileWithClassAsFirst {}
}
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
}
namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
}

View File

@@ -0,0 +1,15 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
}
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
}
namespace Doctrine\Tests\Common\Annotations\Fixtures {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
class DifferentNamespacesPerFileWithClassAsLast {}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
class EqualNamespacesPerFileWithClassAsFirst {}
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
class EqualNamespacesPerFileWithClassAsLast {}

View File

@@ -0,0 +1,12 @@
<?php
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
class GlobalNamespacesPerFileWithClassAsFirst {}
}
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
}
namespace {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
class GlobalNamespacesPerFileWithClassAsLast {}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
interface IntefaceWithConstants
{
const SOME_VALUE = 'IntefaceWithConstants.SOME_VALUE';
const SOME_KEY = 'IntefaceWithConstants.SOME_KEY';
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
/**
* @NoAnnotation
* @IgnoreAnnotation("NoAnnotation")
* @Route("foo")
*/
class InvalidAnnotationUsageButIgnoredClass
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
/**
* @NoAnnotation
*/
class InvalidAnnotationUsageClass
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
class AnotherClass { }
class MultipleClassesInFile { }

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use
Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route,
Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure
;
class MultipleImportsInUseStatement {}

View File

@@ -0,0 +1,20 @@
<?php
// namespace Doctrine\Tests\Common\Annotations\Fixtures;
namespace Doctrine\Tests\Common\Annotations\Fixtures\Foo {
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
// class NamespaceAndClassCommentedOut {}
}
namespace Doctrine\Tests\Common\Annotations\Fixtures {
// class NamespaceAndClassCommentedOut {}
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
// namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
class NamespaceAndClassCommentedOut {}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
$var = 1;
function () use ($var) {};
class NamespaceWithClosureDeclaration {}

View File

@@ -0,0 +1,5 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
class NoAnnotation {}

View File

@@ -0,0 +1,10 @@
<?php
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route;
/**
* @Route("foo")
* @Template
*/
class AnnotationsTestsFixturesNonNamespacedClass { }

View File

@@ -0,0 +1,13 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Fixtures;
use Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Secure;
interface TestInterface
{
/**
* @Secure
*/
function foo();
}

View File

@@ -0,0 +1,194 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\FileCacheReader;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\DocLexer;
use Doctrine\Common\Annotations\DocParser;
use Doctrine\Common\Annotations\PhpParser;
use Doctrine\Common\Annotations\AnnotationReader;
require_once __DIR__ . '/Fixtures/Annotation/Route.php';
require_once __DIR__ . '/Fixtures/Annotation/Template.php';
require_once __DIR__ . '/Fixtures/Annotation/Secure.php';
require_once __DIR__ . '/Fixtures/SingleClassLOC1000.php';
class PerformanceTest extends \PHPUnit_Framework_TestCase
{
/**
* @group performance
*/
public function testCachedReadPerformanceWithInMemory()
{
$reader = new CachedReader(new AnnotationReader(), new ArrayCache());
$method = $this->getMethod();
$time = microtime(true);
for ($i=0,$c=500; $i<$c; $i++) {
$reader->getMethodAnnotations($method);
}
$time = microtime(true) - $time;
$this->printResults('cached reader (in-memory)', $time, $c);
}
/**
* @group performance
*/
public function testCachedReadPerformanceWithFileCache()
{
$method = $this->getMethod();
// prime cache
$reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir());
$reader->getMethodAnnotations($method);
$time = microtime(true);
for ($i=0,$c=500; $i<$c; $i++) {
$reader = new FileCacheReader(new AnnotationReader(), sys_get_temp_dir());
$reader->getMethodAnnotations($method);
clearstatcache();
}
$time = microtime(true) - $time;
$this->printResults('cached reader (file)', $time, $c);
}
/**
* @group performance
*/
public function testReadPerformance()
{
$method = $this->getMethod();
$time = microtime(true);
for ($i=0,$c=150; $i<$c; $i++) {
$reader = new AnnotationReader();
$reader->getMethodAnnotations($method);
}
$time = microtime(true) - $time;
$this->printResults('reader', $time, $c);
}
/**
* @group performance
*/
public function testDocParsePerformance()
{
$imports = array(
'ignorephpdoc' => 'Annotations\Annotation\IgnorePhpDoc',
'ignoreannotation' => 'Annotations\Annotation\IgnoreAnnotation',
'route' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Route',
'template' => 'Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Template',
'__NAMESPACE__' => 'Doctrine\Tests\Common\Annotations\Fixtures',
);
$ignored = array(
'access', 'author', 'copyright', 'deprecated', 'example', 'ignore',
'internal', 'link', 'see', 'since', 'tutorial', 'version', 'package',
'subpackage', 'name', 'global', 'param', 'return', 'staticvar',
'static', 'var', 'throws', 'inheritdoc',
);
$method = $this->getMethod();
$methodComment = $method->getDocComment();
$classComment = $method->getDeclaringClass()->getDocComment();
$time = microtime(true);
for ($i=0,$c=200; $i<$c; $i++) {
$parser = new DocParser();
$parser->setImports($imports);
$parser->setIgnoredAnnotationNames($ignored);
$parser->setIgnoreNotImportedAnnotations(true);
$parser->parse($methodComment);
$parser->parse($classComment);
}
$time = microtime(true) - $time;
$this->printResults('doc-parser', $time, $c);
}
/**
* @group performance
*/
public function testDocLexerPerformance()
{
$method = $this->getMethod();
$methodComment = $method->getDocComment();
$classComment = $method->getDeclaringClass()->getDocComment();
$time = microtime(true);
for ($i=0,$c=500; $i<$c; $i++) {
$lexer = new DocLexer();
$lexer->setInput($methodComment);
$lexer->setInput($classComment);
}
$time = microtime(true) - $time;
$this->printResults('doc-lexer', $time, $c);
}
/**
* @group performance
*/
public function testPhpParserPerformanceWithShortCut()
{
$class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\NamespacedSingleClassLOC1000');
$time = microtime(true);
for ($i=0,$c=500; $i<$c; $i++) {
$parser = new PhpParser();
$parser->parseClass($class);
}
$time = microtime(true) - $time;
$this->printResults('doc-parser-with-short-cut', $time, $c);
}
/**
* @group performance
*/
public function testPhpParserPerformanceWithoutShortCut()
{
$class = new \ReflectionClass('SingleClassLOC1000');
$time = microtime(true);
for ($i=0,$c=500; $i<$c; $i++) {
$parser = new PhpParser();
$parser->parseClass($class);
}
$time = microtime(true) - $time;
$this->printResults('doc-parser-without-short-cut', $time, $c);
}
private function getMethod()
{
return new \ReflectionMethod('Doctrine\Tests\Common\Annotations\Fixtures\Controller', 'helloAction');
}
private function printResults($test, $time, $iterations)
{
if (0 == $iterations) {
throw new \InvalidArgumentException('$iterations cannot be zero.');
}
$title = $test." results:\n";
$iterationsText = sprintf("Iterations: %d\n", $iterations);
$totalTime = sprintf("Total Time: %.3f s\n", $time);
$iterationTime = sprintf("Time per iteration: %.3f ms\n", $time/$iterations * 1000);
$max = max(strlen($title), strlen($iterationTime)) - 1;
echo "\n".str_repeat('-', $max)."\n";
echo $title;
echo str_repeat('=', $max)."\n";
echo $iterationsText;
echo $totalTime;
echo $iterationTime;
echo str_repeat('-', $max)."\n";
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use ReflectionClass;
use Doctrine\Common\Annotations\PhpParser;
require_once __DIR__.'/Fixtures/NonNamespacedClass.php';
require_once __DIR__.'/Fixtures/GlobalNamespacesPerFileWithClassAsFirst.php';
require_once __DIR__.'/Fixtures/GlobalNamespacesPerFileWithClassAsLast.php';
class PhpParserTest extends \PHPUnit_Framework_TestCase
{
public function testParseClassWithMultipleClassesInFile()
{
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\MultipleClassesInFile');
$parser = new PhpParser();
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
), $parser->parseClass($class));
}
public function testParseClassWithMultipleImportsInUseStatement()
{
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\MultipleImportsInUseStatement');
$parser = new PhpParser();
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
), $parser->parseClass($class));
}
public function testParseClassWhenNotUserDefined()
{
$parser = new PhpParser();
$this->assertEquals(array(), $parser->parseClass(new \ReflectionClass('\stdClass')));
}
public function testClassFileDoesNotExist()
{
$class = $this->getMockBuilder('\ReflectionClass')
->disableOriginalConstructor()
->getMock();
$class->expects($this->once())
->method('getFilename')
->will($this->returnValue('/valid/class/Fake.php(35) : eval()d code'));
$parser = new PhpParser();
$this->assertEquals(array(), $parser->parseClass($class));
}
public function testParseClassWhenClassIsNotNamespaced()
{
$parser = new PhpParser();
$class = new ReflectionClass('\AnnotationsTestsFixturesNonNamespacedClass');
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testParseClassWhenClassIsInterface()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\TestInterface');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
), $parser->parseClass($class));
}
public function testClassWithFullyQualifiedUseStatements()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithFullyQualifiedUseStatements');
$this->assertEquals(array(
'secure' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => '\\' . __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testNamespaceAndClassCommentedOut()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceAndClassCommentedOut');
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testEqualNamespacesPerFileWithClassAsFirst()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsFirst');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
), $parser->parseClass($class));
}
public function testEqualNamespacesPerFileWithClassAsLast()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\EqualNamespacesPerFileWithClassAsLast');
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testDifferentNamespacesPerFileWithClassAsFirst()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsFirst');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
), $parser->parseClass($class));
}
public function testDifferentNamespacesPerFileWithClassAsLast()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\DifferentNamespacesPerFileWithClassAsLast');
$this->assertEquals(array(
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testGlobalNamespacesPerFileWithClassAsFirst()
{
$parser = new PhpParser();
$class = new \ReflectionClass('\GlobalNamespacesPerFileWithClassAsFirst');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
), $parser->parseClass($class));
}
public function testGlobalNamespacesPerFileWithClassAsLast()
{
$parser = new PhpParser();
$class = new ReflectionClass('\GlobalNamespacesPerFileWithClassAsLast');
$this->assertEquals(array(
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testNamespaceWithClosureDeclaration()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
public function testIfPointerResetsOnMultipleParsingTries()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\NamespaceWithClosureDeclaration');
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
$this->assertEquals(array(
'secure' => __NAMESPACE__ . '\Fixtures\Annotation\Secure',
'route' => __NAMESPACE__ . '\Fixtures\Annotation\Route',
'template' => __NAMESPACE__ . '\Fixtures\Annotation\Template',
), $parser->parseClass($class));
}
/**
* @group DCOM-97
* @group regression
*/
public function testClassWithClosure()
{
$parser = new PhpParser();
$class = new ReflectionClass(__NAMESPACE__ . '\Fixtures\ClassWithClosure');
$this->assertEquals(array(
'annotationtargetall' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAll',
'annotationtargetannotation' => __NAMESPACE__ . '\Fixtures\AnnotationTargetAnnotation',
), $parser->parseClass($class));
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Doctrine\Tests\Common\Annotations;
use Doctrine\Common\Annotations\SimpleAnnotationReader;
class SimpleAnnotationReaderTest extends AbstractReaderTest
{
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testImportDetectsNotImportedAnnotation()
{
parent::testImportDetectsNotImportedAnnotation();
}
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testImportDetectsNonExistentAnnotation()
{
parent::testImportDetectsNonExistentAnnotation();
}
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testClassWithInvalidAnnotationTargetAtClassDocBlock()
{
parent::testClassWithInvalidAnnotationTargetAtClassDocBlock();
}
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testClassWithInvalidAnnotationTargetAtPropertyDocBlock()
{
parent::testClassWithInvalidAnnotationTargetAtPropertyDocBlock();
}
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock()
{
parent::testClassWithInvalidNestedAnnotationTargetAtPropertyDocBlock();
}
/**
* Contrary to the behavior of the default annotation reader, we do just ignore
* these in the simple annotation reader (so, no expected exception here).
*/
public function testClassWithInvalidAnnotationTargetAtMethodDocBlock()
{
parent::testClassWithInvalidAnnotationTargetAtMethodDocBlock();
}
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
*/
public function testInvalidAnnotationUsageButIgnoredClass()
{
parent::testInvalidAnnotationUsageButIgnoredClass();
}
/**
* @group DDC-1660
* @group regression
*
* Contrary to the behavior of the default annotation reader, @version is not ignored
*/
public function testInvalidAnnotationButIgnored()
{
$reader = $this->getReader();
$class = new \ReflectionClass('Doctrine\Tests\Common\Annotations\Fixtures\ClassDDC1660');
$this->assertTrue(class_exists('Doctrine\Tests\Common\Annotations\Fixtures\Annotation\Version'));
$this->assertCount(1, $reader->getClassAnnotations($class));
$this->assertCount(1, $reader->getMethodAnnotations($class->getMethod('bar')));
$this->assertCount(1, $reader->getPropertyAnnotations($class->getProperty('foo')));
}
protected function getReader()
{
$reader = new SimpleAnnotationReader();
$reader->addNamespace(__NAMESPACE__);
$reader->addNamespace(__NAMESPACE__ . '\Fixtures');
$reader->addNamespace(__NAMESPACE__ . '\Fixtures\Annotation');
return $reader;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Ticket;
use Doctrine\Tests\Common\Annotations\Fixtures\Controller;
/**
* @group
*/
class DCOM55Test extends \PHPUnit_Framework_TestCase
{
/**
* @expectedException Doctrine\Common\Annotations\AnnotationException
* @expectedExceptionMessage [Semantical Error] The class "Doctrine\Tests\Common\Annotations\Fixtures\Controller" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Doctrine\Tests\Common\Annotations\Fixtures\Controller". If it is indeed no annotation, then you need to add @IgnoreAnnotation("Controller") to the _class_ doc comment of class Doctrine\Tests\Common\Annotations\Ticket\Dummy.
*/
public function testIssue()
{
$class = new \ReflectionClass(__NAMESPACE__ . '\\Dummy');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$reader->getClassAnnotations($class);
}
public function testAnnotation()
{
$class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Consumer');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$annots = $reader->getClassAnnotations($class);
$this->assertEquals(1, count($annots));
$this->assertInstanceOf(__NAMESPACE__.'\\DCOM55Annotation', $annots[0]);
}
public function testParseAnnotationDocblocks()
{
$class = new \ReflectionClass(__NAMESPACE__ . '\\DCOM55Annotation');
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$annots = $reader->getClassAnnotations($class);
$this->assertEquals(0, count($annots));
}
}
/**
* @Controller
*/
class Dummy
{
}
/**
* @Annotation
*/
class DCOM55Annotation
{
}
/**
* @DCOM55Annotation
*/
class DCOM55Consumer
{
}

View File

@@ -0,0 +1,8 @@
<?php
// Some class named Entity in the global namespace
/**
* @Annotation
*/
class Entity
{
}

View File

@@ -0,0 +1,112 @@
<?php
namespace Doctrine\Tests\Common\Annotations\Ticket;
//Some class named Entity in the global namespace
include __DIR__ .'/DCOM58Entity.php';
/**
* @group DCOM58
*/
class DCOM58Test extends \PHPUnit_Framework_TestCase
{
public function testIssue()
{
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
$result = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass"));
foreach ($result as $annot) {
$classAnnotations[get_class($annot)] = $annot;
}
$this->assertTrue(!isset($classAnnotations['']), 'Class "xxx" is not a valid entity or mapped super class.');
}
public function testIssueGlobalNamespace()
{
$docblock = "@Entity";
$parser = new \Doctrine\Common\Annotations\DocParser();
$parser->setImports(array(
"__NAMESPACE__" =>"Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping"
));
$annots = $parser->parse($docblock);
$this->assertEquals(1, count($annots));
$this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
}
public function testIssueNamespaces()
{
$docblock = "@Entity";
$parser = new \Doctrine\Common\Annotations\DocParser();
$parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM");
$annots = $parser->parse($docblock);
$this->assertEquals(1, count($annots));
$this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Entity", $annots[0]);
}
public function testIssueMultipleNamespaces()
{
$docblock = "@Entity";
$parser = new \Doctrine\Common\Annotations\DocParser();
$parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping");
$parser->addNamespace("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM");
$annots = $parser->parse($docblock);
$this->assertEquals(1, count($annots));
$this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
}
public function testIssueWithNamespacesOrImports()
{
$docblock = "@Entity";
$parser = new \Doctrine\Common\Annotations\DocParser();
$annots = $parser->parse($docblock);
$this->assertEquals(1, count($annots));
$this->assertInstanceOf("Entity", $annots[0]);
$this->assertEquals(1, count($annots));
}
public function testIssueSimpleAnnotationReader()
{
$reader = new \Doctrine\Common\Annotations\SimpleAnnotationReader();
$reader->addNamespace('Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping');
$annots = $reader->getClassAnnotations(new \ReflectionClass(__NAMESPACE__."\MappedClass"));
$this->assertEquals(1, count($annots));
$this->assertInstanceOf("Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping\Entity", $annots[0]);
}
}
/**
* @Entity
*/
class MappedClass
{
}
namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM\Mapping;
/**
* @Annotation
*/
class Entity
{
}
namespace Doctrine\Tests\Common\Annotations\Ticket\Doctrine\ORM;
/**
* @Annotation
*/
class Entity
{
}

View File

@@ -0,0 +1,8 @@
<?php
use Doctrine\Common\Annotations\Annotation;
/** @Annotation */
class TopLevelAnnotation extends Annotation
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests;
/**
* Base testcase class for all Doctrine testcases.
*/
abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase
{
}

View File

@@ -0,0 +1,26 @@
<?php
/*
* This file bootstraps the test environment.
*/
namespace Doctrine\Tests;
error_reporting(E_ALL | E_STRICT);
// register silently failing autoloader
spl_autoload_register(function($class)
{
if (0 === strpos($class, 'Doctrine\Tests\\')) {
$path = __DIR__.'/../../'.strtr($class, '\\', '/').'.php';
if (is_file($path) && is_readable($path)) {
require_once $path;
return true;
}
}
});
require_once __DIR__ . "/../../../vendor/autoload.php";
\Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace(
'Doctrine\Tests\Common\Annotations\Fixtures', __DIR__ . '/../../'
);