Skip to content

Instantly share code, notes, and snippets.

@Seldaek
Created May 26, 2011 13:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Seldaek/993170 to your computer and use it in GitHub Desktop.
Save Seldaek/993170 to your computer and use it in GitHub Desktop.
Annotations

Annotation Change RFC

Most of those changes are ideas taken from the php-annotations lib (see its wiki too) by Rasmus Schultz.

Goals

I am not the author of that lib but have been discussing with him for a while and I am convinced that this could benefit Symfony users and PHP as a whole on the long run, if we standardized the annotations a bit more than what we have right now.

For example, you have to define the data type with @ORM\Column, @var and @Assert\Type(). This is absurd, if we had a proper standard way of parsing type information, and all libraries used it, it would only require one annotation. It would also save much work on the library side since they don't have to each define their own way of parsing type information, with the inevitable gaps and inconsistencies that this introduces.

Loading & Resolving

  • Annotations without namespace prefix:
    • There would be a set of standardized "autoimported" annotations (to be defined, but you can look at sample ones), e.g. @var, @param, ...
    • If they don't exist, and are not specifically autoimported or ignored, an Exception is thrown.
  • Annotations with namespace prefix (NO CHANGE):
    • The prefix is resolved using use statements.
    • If the resulting class name does not exist, an Exception is thrown.

Syntax

  • Syntax is more PHP-like, if you use @foo() whatever sits inside the parenthesis is basically what could fit inside a php array(). The syntax is equivalent to php arrays, and anything that can go in there (php constants, new Foo()) is valid.
  • Parsing of "old-style" phpdocs like @var SomeType is delegated to the annotation class itself, so the annotation can parse that how it wants. Maybe this custom parsing could be possible for () too, allowing us to support the current syntax for a transitional period, but I don't think it's a particularly good idea.
  • Nesting of annotations is not possible anymore, since this is not a valid php structure, but it seems that it's not really necessary to have nesting if we allow multiple annotations of the same name, and if you can instantiate any object to pass it to an annotation using new Foo, which seems to be the only case left in Doctrine.

Multiple Annotations & Inheritance

  • Multiple annotations of the same name are supported, optionally, the annotation defines that in its @usage annotation.
  • Rules for inheritance are described towards the end of that page and they seem good so I'm not gonna repeat everything.

Addendum

Now don't forget RFC means Request For Comments, please comment away, but don't flame. It might hurt some feelings of Annotation library developers, but please think of the users.

<?php
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
*/
class AcmeUser
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*
* @var integer
*/
private $id;
/**
* @ORM\Column(type="string", nullable=false)
* @Assert\NotBlank
*
* @var string
*/
private $name;
/**
* @ORM\ManyToMany(targetEntity="Phonenumber")
* @ORM\JoinTable(name="users_phonenumbers",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
* )
*/
private $phonenumbers;
}
<?php
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
*/
class AcmeUser
{
/**
* @ORM\Id
* @ORM\GeneratedValue("strategy"=>"AUTO") // syntax change
* @ORM\Column("type"=>"integer") // syntax change
*
* @var integer // loads Annotation\Var
*/
private $id;
/**
* @ORM\Column("type"=>"string", "nullable"=>false) // syntax change
* @Assert\NotBlank
*
* @var string // loads Annotation\Var
*/
private $name;
/**
* @ORM\ManyToMany("targetEntity"=>"Phonenumber")
* @ORM\JoinTable("name"=>"users_phonenumbers",
* "joinColumns" => new ORM\JoinColumn("user_id", array("referencedColumnName"=>"id")),
* "inverseJoinColumns" => new ORM\JoinColumn("phonenumber_id", array("referencedColumnName"=>"id", "unique"=>true))
* )
*/
private $phonenumbers;
}
@stof
Copy link

stof commented May 26, 2011

I disagree about saying that @var and @ORM\Column does the same thing. The first one tells you what is the type of the object and nothing more. The Doctrine annotation controls the way it is persisted. So it needs many attributes to control it (optionnal but useful) and the same PHP object could be persisted in different ways (see date and datetime for DateTime objects).

And for nesting annotations, what about the way indexes are defined currently in Doctrine, by placing an array of indexes as one attribute of @ORM\Table ?
And I disagree about saying it is invalid in PHP. You can nest arrays and use objects as values of an array.

@mindplay-dk
Copy link

Passing "annotations" to an annotation as arguments is wrong to begin with.

What makes it an annotation in the first place, is not merely the fact that it was constructed using the @syntax, but the fact that it annotates a source-code element.

Your @Index arguments do not annotate anything; they are a convenient and lazy, but deceptive shortcut to quick construcion of objects to be used as arguments - but they are not technically annotations, and therefore, why would you want them to look like annotations? It's misleading and wrong.

@Seldaek
Copy link
Author

Seldaek commented May 26, 2011

@stof: The @var / @ORM\Column thing is just an example. But anyway to answer you, I think the Column thing should stay, to specify things like nullable and other, but the type would not need to be part of it, or be made optional if it can be inferred from @var.

As for the nesting, yes nested array are valid php of course, but annotations have no meaning in php itself, so if we assume that anything between () is "php-like", we should not allow @randomStuff in there. That's what I meant by invalid php syntax. See mindplay's answer about why you don't want that anyway.

@Seldaek
Copy link
Author

Seldaek commented May 26, 2011

BTW please everyone keep the comments on the ML thread from now on.

@lsmith77
Copy link

could you explain with a concrete example what the use case of @var string // loads Annotation\Var is?
From the text above I would have thought it means that the type could be omitted in the Column annotation @ORM\Column("nullable"=>false) // syntax change

@Seldaek
Copy link
Author

Seldaek commented May 26, 2011

@lsmith77: All this is saying is that @var string would be parsed by the standard "Var" annotation provided by the library, and made available to anyone. Which in effect would allow doctrine to drop the type from @ORM\Column yes. It's one of the goals of the change, but I didn't highlight it to avoid big WTFs of people. The before/after files above are just to show that the syntax changes are not that huge for most common annotations. Nested arrays would turn from {"foo"="bar"} to array('foo'=>'bar') for example.

@lsmith77
Copy link

well without it .. i had the a "why" WTF :)

@Seldaek
Copy link
Author

Seldaek commented May 26, 2011

Added a @ManyToMany example for the nesting.

@mindplay-dk
Copy link

Those inline comments, such as @var string // loads Annotation\Var - are those part of the syntax, or just there for the sake of discussion? (are inline comments inside doc-blocks understood and parsed by IDEs and documentation generators?)

@Seldaek
Copy link
Author

Seldaek commented May 27, 2011

This was just for discussion, inline comments in doc-blocks have no meaning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment