2015-08-18 00:00:26 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\Validator\Mapping ;
use Symfony\Component\Validator\Constraint ;
use Symfony\Component\Validator\Constraints\GroupSequence ;
use Symfony\Component\Validator\Constraints\Traverse ;
use Symfony\Component\Validator\Constraints\Valid ;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException ;
use Symfony\Component\Validator\Exception\GroupDefinitionException ;
use Symfony\Component\Validator\ValidationVisitorInterface ;
/**
* Default implementation of { @ link ClassMetadataInterface } .
*
* This class supports serialization and cloning .
*
* @ author Bernhard Schussek < bschussek @ gmail . com >
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class ClassMetadata extends ElementMetadata implements ClassMetadataInterface
{
/**
* @ var string
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getClassName ()} instead .
*/
public $name ;
/**
* @ var string
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getDefaultGroup ()} instead .
*/
public $defaultGroup ;
/**
* @ var MemberMetadata []
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getPropertyMetadata ()} instead .
*/
public $members = array ();
/**
* @ var PropertyMetadata []
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getPropertyMetadata ()} instead .
*/
public $properties = array ();
/**
* @ var GetterMetadata []
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getPropertyMetadata ()} instead .
*/
public $getters = array ();
/**
* @ var array
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getGroupSequence ()} instead .
*/
public $groupSequence = array ();
/**
* @ var bool
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link isGroupSequenceProvider ()} instead .
*/
public $groupSequenceProvider = false ;
/**
* The strategy for traversing traversable objects .
*
* By default , only instances of { @ link \Traversable } are traversed .
*
* @ var int
*
* @ internal This property is public in order to reduce the size of the
* class ' serialized representation . Do not access it . Use
* { @ link getTraversalStrategy ()} instead .
*/
public $traversalStrategy = TraversalStrategy :: IMPLICIT ;
/**
* @ var \ReflectionClass
*/
private $reflClass ;
/**
* Constructs a metadata for the given class .
*
* @ param string $class
*/
public function __construct ( $class )
{
$this -> name = $class ;
// class name without namespace
if ( false !== $nsSep = strrpos ( $class , '\\' )) {
$this -> defaultGroup = substr ( $class , $nsSep + 1 );
} else {
$this -> defaultGroup = $class ;
}
}
/**
* { @ inheritdoc }
*
* @ deprecated since version 2.5 , to be removed in 3.0 .
*/
public function accept ( ValidationVisitorInterface $visitor , $value , $group , $propertyPath , $propagatedGroup = null )
{
2015-08-27 19:03:05 +00:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.5 and will be removed in 3.0.' , E_USER_DEPRECATED );
2015-08-18 00:00:26 +00:00
if ( null === $propagatedGroup && Constraint :: DEFAULT_GROUP === $group
&& ( $this -> hasGroupSequence () || $this -> isGroupSequenceProvider ())) {
if ( $this -> hasGroupSequence ()) {
$groups = $this -> getGroupSequence () -> groups ;
} else {
$groups = $value -> getGroupSequence ();
}
foreach ( $groups as $group ) {
$this -> accept ( $visitor , $value , $group , $propertyPath , Constraint :: DEFAULT_GROUP );
if ( count ( $visitor -> getViolations ()) > 0 ) {
break ;
}
}
return ;
}
$visitor -> visit ( $this , $value , $group , $propertyPath );
if ( null !== $value ) {
$pathPrefix = empty ( $propertyPath ) ? '' : $propertyPath . '.' ;
foreach ( $this -> getConstrainedProperties () as $property ) {
foreach ( $this -> getPropertyMetadata ( $property ) as $member ) {
$member -> accept ( $visitor , $member -> getPropertyValue ( $value ), $group , $pathPrefix . $property , $propagatedGroup );
}
}
}
}
/**
* { @ inheritdoc }
*/
public function __sleep ()
{
$parentProperties = parent :: __sleep ();
// Don't store the cascading strategy. Classes never cascade.
unset ( $parentProperties [ array_search ( 'cascadingStrategy' , $parentProperties )]);
return array_merge ( $parentProperties , array (
'getters' ,
'groupSequence' ,
'groupSequenceProvider' ,
'members' ,
'name' ,
'properties' ,
'defaultGroup' ,
));
}
/**
* { @ inheritdoc }
*/
public function getClassName ()
{
return $this -> name ;
}
/**
* Returns the name of the default group for this class .
*
* For each class , the group " Default " is an alias for the group
* " <ClassName> " , where < ClassName > is the non - namespaced name of the
* class . All constraints implicitly or explicitly assigned to group
* " Default " belong to both of these groups , unless the class defines
* a group sequence .
*
* If a class defines a group sequence , validating the class in " Default "
* will validate the group sequence . The constraints assigned to " Default "
* can still be validated by validating the class in " <ClassName> " .
*
* @ return string The name of the default group
*/
public function getDefaultGroup ()
{
return $this -> defaultGroup ;
}
/**
* { @ inheritdoc }
*/
public function addConstraint ( Constraint $constraint )
{
if ( ! in_array ( Constraint :: CLASS_CONSTRAINT , ( array ) $constraint -> getTargets ())) {
throw new ConstraintDefinitionException ( sprintf (
'The constraint "%s" cannot be put on classes.' ,
get_class ( $constraint )
));
}
if ( $constraint instanceof Valid ) {
throw new ConstraintDefinitionException ( sprintf (
'The constraint "%s" cannot be put on classes.' ,
get_class ( $constraint )
));
}
if ( $constraint instanceof Traverse ) {
if ( $constraint -> traverse ) {
// If traverse is true, traversal should be explicitly enabled
$this -> traversalStrategy = TraversalStrategy :: TRAVERSE ;
} else {
// If traverse is false, traversal should be explicitly disabled
$this -> traversalStrategy = TraversalStrategy :: NONE ;
}
// The constraint is not added
return $this ;
}
$constraint -> addImplicitGroupName ( $this -> getDefaultGroup ());
parent :: addConstraint ( $constraint );
return $this ;
}
/**
* Adds a constraint to the given property .
*
* @ param string $property The name of the property
* @ param Constraint $constraint The constraint
*
* @ return ClassMetadata This object
*/
public function addPropertyConstraint ( $property , Constraint $constraint )
{
if ( ! isset ( $this -> properties [ $property ])) {
$this -> properties [ $property ] = new PropertyMetadata ( $this -> getClassName (), $property );
$this -> addPropertyMetadata ( $this -> properties [ $property ]);
}
$constraint -> addImplicitGroupName ( $this -> getDefaultGroup ());
$this -> properties [ $property ] -> addConstraint ( $constraint );
return $this ;
}
/**
* @ param string $property
* @ param Constraint [] $constraints
*
* @ return ClassMetadata
*/
public function addPropertyConstraints ( $property , array $constraints )
{
foreach ( $constraints as $constraint ) {
$this -> addPropertyConstraint ( $property , $constraint );
}
return $this ;
}
/**
* Adds a constraint to the getter of the given property .
*
* The name of the getter is assumed to be the name of the property with an
* uppercased first letter and either the prefix " get " or " is " .
*
* @ param string $property The name of the property
* @ param Constraint $constraint The constraint
*
* @ return ClassMetadata This object
*/
public function addGetterConstraint ( $property , Constraint $constraint )
{
if ( ! isset ( $this -> getters [ $property ])) {
$this -> getters [ $property ] = new GetterMetadata ( $this -> getClassName (), $property );
$this -> addPropertyMetadata ( $this -> getters [ $property ]);
}
$constraint -> addImplicitGroupName ( $this -> getDefaultGroup ());
$this -> getters [ $property ] -> addConstraint ( $constraint );
return $this ;
}
/**
* @ param string $property
* @ param Constraint [] $constraints
*
* @ return ClassMetadata
*/
public function addGetterConstraints ( $property , array $constraints )
{
foreach ( $constraints as $constraint ) {
$this -> addGetterConstraint ( $property , $constraint );
}
return $this ;
}
/**
* Merges the constraints of the given metadata into this object .
*
* @ param ClassMetadata $source The source metadata
*/
public function mergeConstraints ( ClassMetadata $source )
{
foreach ( $source -> getConstraints () as $constraint ) {
$this -> addConstraint ( clone $constraint );
}
foreach ( $source -> getConstrainedProperties () as $property ) {
foreach ( $source -> getPropertyMetadata ( $property ) as $member ) {
$member = clone $member ;
foreach ( $member -> getConstraints () as $constraint ) {
$constraint -> addImplicitGroupName ( $this -> getDefaultGroup ());
}
$this -> addPropertyMetadata ( $member );
if ( $member instanceof MemberMetadata && ! $member -> isPrivate ( $this -> name )) {
$property = $member -> getPropertyName ();
if ( $member instanceof PropertyMetadata && ! isset ( $this -> properties [ $property ])) {
$this -> properties [ $property ] = $member ;
} elseif ( $member instanceof GetterMetadata && ! isset ( $this -> getters [ $property ])) {
$this -> getters [ $property ] = $member ;
}
}
}
}
}
/**
* Adds a member metadata .
*
* @ param MemberMetadata $metadata
*
* @ deprecated since version 2.6 , to be removed in 3.0 .
*/
protected function addMemberMetadata ( MemberMetadata $metadata )
{
2015-08-27 19:03:05 +00:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.6 and will be removed in 3.0. Use the addPropertyMetadata() method instead.' , E_USER_DEPRECATED );
2015-08-18 00:00:26 +00:00
$this -> addPropertyMetadata ( $metadata );
}
/**
* Returns true if metadatas of members is present for the given property .
*
* @ param string $property The name of the property
*
* @ return bool
*
* @ deprecated since version 2.6 , to be removed in 3.0 . Use { @ link hasPropertyMetadata } instead .
*/
public function hasMemberMetadatas ( $property )
{
2015-08-27 19:03:05 +00:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.6 and will be removed in 3.0. Use the hasPropertyMetadata() method instead.' , E_USER_DEPRECATED );
2015-08-18 00:00:26 +00:00
return $this -> hasPropertyMetadata ( $property );
}
/**
* Returns all metadatas of members describing the given property .
*
* @ param string $property The name of the property
*
* @ return MemberMetadata [] An array of MemberMetadata
*
* @ deprecated since version 2.6 , to be removed in 3.0 . Use { @ link getPropertyMetadata } instead .
*/
public function getMemberMetadatas ( $property )
{
2015-08-27 19:03:05 +00:00
@ trigger_error ( 'The ' . __METHOD__ . ' method is deprecated since version 2.6 and will be removed in 3.0. Use the getPropertyMetadata() method instead.' , E_USER_DEPRECATED );
2015-08-18 00:00:26 +00:00
return $this -> getPropertyMetadata ( $property );
}
/**
* { @ inheritdoc }
*/
public function hasPropertyMetadata ( $property )
{
return array_key_exists ( $property , $this -> members );
}
/**
* { @ inheritdoc }
*/
public function getPropertyMetadata ( $property )
{
if ( ! isset ( $this -> members [ $property ])) {
return array ();
}
return $this -> members [ $property ];
}
/**
* { @ inheritdoc }
*/
public function getConstrainedProperties ()
{
return array_keys ( $this -> members );
}
/**
* Sets the default group sequence for this class .
*
* @ param array $groupSequence An array of group names
*
* @ return ClassMetadata
*
* @ throws GroupDefinitionException
*/
public function setGroupSequence ( $groupSequence )
{
if ( $this -> isGroupSequenceProvider ()) {
throw new GroupDefinitionException ( 'Defining a static group sequence is not allowed with a group sequence provider' );
}
if ( is_array ( $groupSequence )) {
$groupSequence = new GroupSequence ( $groupSequence );
}
if ( in_array ( Constraint :: DEFAULT_GROUP , $groupSequence -> groups , true )) {
throw new GroupDefinitionException ( sprintf ( 'The group "%s" is not allowed in group sequences' , Constraint :: DEFAULT_GROUP ));
}
if ( ! in_array ( $this -> getDefaultGroup (), $groupSequence -> groups , true )) {
throw new GroupDefinitionException ( sprintf ( 'The group "%s" is missing in the group sequence' , $this -> getDefaultGroup ()));
}
$this -> groupSequence = $groupSequence ;
return $this ;
}
/**
* { @ inheritdoc }
*/
public function hasGroupSequence ()
{
return $this -> groupSequence && count ( $this -> groupSequence -> groups ) > 0 ;
}
/**
* { @ inheritdoc }
*/
public function getGroupSequence ()
{
return $this -> groupSequence ;
}
/**
* Returns a ReflectionClass instance for this class .
*
* @ return \ReflectionClass
*/
public function getReflectionClass ()
{
if ( ! $this -> reflClass ) {
$this -> reflClass = new \ReflectionClass ( $this -> getClassName ());
}
return $this -> reflClass ;
}
/**
* Sets whether a group sequence provider should be used .
*
* @ param bool $active
*
* @ throws GroupDefinitionException
*/
public function setGroupSequenceProvider ( $active )
{
if ( $this -> hasGroupSequence ()) {
throw new GroupDefinitionException ( 'Defining a group sequence provider is not allowed with a static group sequence' );
}
if ( ! $this -> getReflectionClass () -> implementsInterface ( 'Symfony\Component\Validator\GroupSequenceProviderInterface' )) {
throw new GroupDefinitionException ( sprintf ( 'Class "%s" must implement GroupSequenceProviderInterface' , $this -> name ));
}
$this -> groupSequenceProvider = $active ;
}
/**
* { @ inheritdoc }
*/
public function isGroupSequenceProvider ()
{
return $this -> groupSequenceProvider ;
}
/**
* Class nodes are never cascaded .
*
* { @ inheritdoc }
*/
public function getCascadingStrategy ()
{
return CascadingStrategy :: NONE ;
}
/**
* Adds a property metadata .
*
* @ param PropertyMetadataInterface $metadata
*/
private function addPropertyMetadata ( PropertyMetadataInterface $metadata )
{
$property = $metadata -> getPropertyName ();
$this -> members [ $property ][] = $metadata ;
}
}