Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Enumeration support #167

Merged
merged 5 commits into from

3 participants

@FabioBatSilva

This patch adds support for @Enum.

I think that is very useful to avoid wrong data input in annotations like @InheritanceType or @ManyToMany#fetch

Cheers..

@guilhermeblanco guilhermeblanco commented on the diff
lib/Doctrine/Common/Annotations/Annotation/Enum.php
((15 lines not shown))
+ *
+ * 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
@guilhermeblanco Owner

Missing ({, no?

@FabioBatSilva Owner

Fixed,

Thanks @guilhermeblanco.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@beberlei beberlei merged commit 30b8cea into from
@FabioBatSilva FabioBatSilva referenced this pull request in doctrine/doctrine2
Merged

Enumeration support #513

@FabioBatSilva FabioBatSilva deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 12, 2012
  1. @FabioBatSilva

    added support for @Enum

    FabioBatSilva authored
  2. @FabioBatSilva

    tests for @Enum support

    FabioBatSilva authored
  3. @FabioBatSilva

    funcional test

    FabioBatSilva authored
  4. @FabioBatSilva

    add missing @Attributes

    FabioBatSilva authored
  5. @FabioBatSilva

    remove extra comma

    FabioBatSilva authored
This page is out of date. Refresh to see the latest.
View
85 lib/Doctrine/Common/Annotations/Annotation/Enum.php
@@ -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
@guilhermeblanco Owner

Missing ({, no?

@FabioBatSilva Owner

Fixed,

Thanks @guilhermeblanco.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ * @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
23 lib/Doctrine/Common/Annotations/AnnotationException.php
@@ -124,4 +124,27 @@ public static function requiredError($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
+ ));
+ }
}
View
60 lib/Doctrine/Common/Annotations/DocParser.php
@@ -21,6 +21,7 @@
use Closure;
use ReflectionClass;
+use Doctrine\Common\Annotations\Annotation\Enum;
use Doctrine\Common\Annotations\Annotation\Target;
use Doctrine\Common\Annotations\Annotation\Attribute;
use Doctrine\Common\Annotations\Annotation\Attributes;
@@ -187,6 +188,26 @@
)
),
),
+ 'Doctrine\Common\Annotations\Annotation\Enum' => array(
+ 'is_annotation' => true,
+ 'has_constructor' => true,
+ 'targets_literal' => 'ANNOTATION_PROPERTY',
+ 'targets' => Target::TARGET_PROPERTY,
+ 'default_property' => 'value',
+ 'properties' => array(
+ 'value' => 'value'
+ ),
+ 'attribute_types' => array(
+ 'value' => array(
+ 'type' => 'array',
+ 'required' => true,
+ ),
+ 'literal' => array(
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ ),
+ ),
);
/**
@@ -393,14 +414,15 @@ private function collectAnnotationMetadata($name)
{
if (self::$metadataParser == null){
self::$metadataParser = new self();
- self::$metadataParser->setTarget(Target::TARGET_CLASS);
self::$metadataParser->setIgnoreNotImportedAnnotations(true);
self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
self::$metadataParser->setImports(array(
+ 'enum' => 'Doctrine\Common\Annotations\Annotation\Enum',
'target' => 'Doctrine\Common\Annotations\Annotation\Target',
'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute',
'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes'
));
+ AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php');
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php');
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php');
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php');
@@ -423,6 +445,9 @@ private function collectAnnotationMetadata($name)
// verify that the class is really meant to be an annotation
if ($metadata['is_annotation']) {
+
+ self::$metadataParser->setTarget(Target::TARGET_CLASS);
+
foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) {
if ($annotation instanceof Target) {
$metadata['targets'] = $annotation->targets;
@@ -460,10 +485,13 @@ private function collectAnnotationMetadata($name)
// collect all public properties
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
$metadata['properties'][$property->name] = $property->name;
+
+ if(false === ($propertyComment = $property->getDocComment())) {
+ continue;
+ }
// checks if the property has @var annotation
- if ((false !== $propertyComment = $property->getDocComment())
- && false !== strpos($propertyComment, '@var')
+ if (false !== strpos($propertyComment, '@var')
&& preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) {
// literal type declaration
$value = $matches[1];
@@ -490,6 +518,20 @@ private function collectAnnotationMetadata($name)
$metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required');
}
}
+
+ // checks if the property has @Enum
+ if (false !== strpos($propertyComment, '@Enum')){
+
+ $context = 'property ' . $class->name . "::\$" . $property->name;
+ self::$metadataParser->setTarget(Target::TARGET_PROPERTY);
+
+ foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
+ if($annotation instanceof Enum) {
+ $metadata['enum'][$property->name]['value'] = $annotation->value;
+ $metadata['enum'][$property->name]['literal'] = ! empty($annotation->literal) ? $annotation->literal : $annotation->value;
+ }
+ }
+ }
}
// choose the first property as default property
@@ -642,6 +684,16 @@ private function Annotation()
$this->match(DocLexer::T_CLOSE_PARENTHESIS);
}
+ if (isset(self::$annotationMetadata[$name]['enum'])) {
+ // checks all declared attributes
+ foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
+ // checks if the attribute is a valid enumerator
+ if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) {
+ throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]);
+ }
+ }
+ }
+
// checks all declared attributes
foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
if ($property === self::$annotationMetadata[$name]['default_property']
@@ -660,7 +712,7 @@ private function Annotation()
if ($type['type'] === 'array') {
// handle the case of a single value
- if (!is_array($values[$property])) {
+ if ( ! is_array($values[$property])) {
$values[$property] = array($values[$property]);
}
View
29 tests/Doctrine/Tests/Common/Annotations/AbstractReaderTest.php
@@ -350,6 +350,35 @@ public function testInvalidAnnotationButIgnored()
$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());
+ }
+ }
+
+ /**
+ * @return AnnotationReader
+ */
abstract protected function getReader();
}
View
56 tests/Doctrine/Tests/Common/Annotations/DocParserTest.php
@@ -665,6 +665,62 @@ public function testAnnotationWithRequiredAttributesWithoutContructor()
}
+ /**
+ * @expectedException Doctrine\Common\Annotations\AnnotationException
+ * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum declared on property SomeClassName::invalidProperty. accept only [ONE, TWO, THREE], but got FOUR.
+ */
+ public function testAnnotationEnumeratorException()
+ {
+ $parser = $this->createTestParser();
+ $context = 'property SomeClassName::invalidProperty.';
+ $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnum("FOUR")';
+
+ $parser->setIgnoreNotImportedAnnotations(false);
+ $parser->setTarget(Target::TARGET_PROPERTY);
+ $parser->parse($docblock, $context);
+ }
+
+ /**
+ * @expectedException Doctrine\Common\Annotations\AnnotationException
+ * @expectedExceptionMessage Attribute "value" of @Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral declared on property SomeClassName::invalidProperty. accept only [AnnotationEnumLiteral::ONE, AnnotationEnumLiteral::TWO, AnnotationEnumLiteral::THREE], but got 4.
+ */
+ public function testAnnotationEnumeratorLiteralException()
+ {
+ $parser = $this->createTestParser();
+ $context = 'property SomeClassName::invalidProperty.';
+ $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteral(4)';
+
+ $parser->setIgnoreNotImportedAnnotations(false);
+ $parser->setTarget(Target::TARGET_PROPERTY);
+ $parser->parse($docblock, $context);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage @Enum supports only scalar values "array" given.
+ */
+ public function testAnnotationEnumInvalidTypeDeclarationException()
+ {
+ $parser = $this->createTestParser();
+ $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumInvalid("foo")';
+
+ $parser->setIgnoreNotImportedAnnotations(false);
+ $parser->parse($docblock);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Undefined enumerator value "3" for literal "AnnotationEnumLiteral::THREE".
+ */
+ public function testAnnotationEnumInvalidLiteralDeclarationException()
+ {
+ $parser = $this->createTestParser();
+ $docblock = '@Doctrine\Tests\Common\Annotations\Fixtures\AnnotationEnumLiteralInvalid("foo")';
+
+ $parser->setIgnoreNotImportedAnnotations(false);
+ $parser->parse($docblock);
+ }
+
public function getConstantsProvider()
{
$provider[] = array(
View
21 tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnum.php
@@ -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
17 tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumInvalid.php
@@ -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
34 tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteral.php
@@ -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
31 tests/Doctrine/Tests/Common/Annotations/Fixtures/AnnotationEnumLiteralInvalid.php
@@ -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
29 tests/Doctrine/Tests/Common/Annotations/Fixtures/ClassWithAnnotationEnum.php
@@ -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(){}
+}
Something went wrong with that request. Please try again.