1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Validator\Mapping;
13
14use Symfony\Component\Validator\Constraint;
15use Symfony\Component\Validator\Constraints\Composite;
16use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
17
18/**
19 * Stores all metadata needed for validating a class property.
20 *
21 * The method of accessing the property's value must be specified by subclasses
22 * by implementing the {@link newReflectionMember()} method.
23 *
24 * This class supports serialization and cloning.
25 *
26 * @author Bernhard Schussek <bschussek@gmail.com>
27 *
28 * @see PropertyMetadataInterface
29 */
30abstract class MemberMetadata extends GenericMetadata implements PropertyMetadataInterface
31{
32    /**
33     * @internal This property is public in order to reduce the size of the
34     *           class' serialized representation. Do not access it. Use
35     *           {@link getClassName()} instead.
36     */
37    public $class;
38
39    /**
40     * @internal This property is public in order to reduce the size of the
41     *           class' serialized representation. Do not access it. Use
42     *           {@link getName()} instead.
43     */
44    public $name;
45
46    /**
47     * @internal This property is public in order to reduce the size of the
48     *           class' serialized representation. Do not access it. Use
49     *           {@link getPropertyName()} instead.
50     */
51    public $property;
52
53    /**
54     * @var \ReflectionMethod[]|\ReflectionProperty[]
55     */
56    private $reflMember = [];
57
58    /**
59     * @param string $class    The name of the class this member is defined on
60     * @param string $name     The name of the member
61     * @param string $property The property the member belongs to
62     */
63    public function __construct(string $class, string $name, string $property)
64    {
65        $this->class = $class;
66        $this->name = $name;
67        $this->property = $property;
68    }
69
70    /**
71     * {@inheritdoc}
72     */
73    public function addConstraint(Constraint $constraint)
74    {
75        $this->checkConstraint($constraint);
76
77        parent::addConstraint($constraint);
78
79        return $this;
80    }
81
82    /**
83     * {@inheritdoc}
84     */
85    public function __sleep()
86    {
87        return array_merge(parent::__sleep(), [
88            'class',
89            'name',
90            'property',
91        ]);
92    }
93
94    /**
95     * Returns the name of the member.
96     *
97     * @return string
98     */
99    public function getName()
100    {
101        return $this->name;
102    }
103
104    /**
105     * {@inheritdoc}
106     */
107    public function getClassName()
108    {
109        return $this->class;
110    }
111
112    /**
113     * {@inheritdoc}
114     */
115    public function getPropertyName()
116    {
117        return $this->property;
118    }
119
120    /**
121     * Returns whether this member is public.
122     *
123     * @param object|string $objectOrClassName The object or the class name
124     *
125     * @return bool
126     */
127    public function isPublic($objectOrClassName)
128    {
129        return $this->getReflectionMember($objectOrClassName)->isPublic();
130    }
131
132    /**
133     * Returns whether this member is protected.
134     *
135     * @param object|string $objectOrClassName The object or the class name
136     *
137     * @return bool
138     */
139    public function isProtected($objectOrClassName)
140    {
141        return $this->getReflectionMember($objectOrClassName)->isProtected();
142    }
143
144    /**
145     * Returns whether this member is private.
146     *
147     * @param object|string $objectOrClassName The object or the class name
148     *
149     * @return bool
150     */
151    public function isPrivate($objectOrClassName)
152    {
153        return $this->getReflectionMember($objectOrClassName)->isPrivate();
154    }
155
156    /**
157     * Returns the reflection instance for accessing the member's value.
158     *
159     * @param object|string $objectOrClassName The object or the class name
160     *
161     * @return \ReflectionMethod|\ReflectionProperty The reflection instance
162     */
163    public function getReflectionMember($objectOrClassName)
164    {
165        $className = \is_string($objectOrClassName) ? $objectOrClassName : \get_class($objectOrClassName);
166        if (!isset($this->reflMember[$className])) {
167            $this->reflMember[$className] = $this->newReflectionMember($objectOrClassName);
168        }
169
170        return $this->reflMember[$className];
171    }
172
173    /**
174     * Creates a new reflection instance for accessing the member's value.
175     *
176     * Must be implemented by subclasses.
177     *
178     * @param object|string $objectOrClassName The object or the class name
179     *
180     * @return \ReflectionMethod|\ReflectionProperty The reflection instance
181     */
182    abstract protected function newReflectionMember($objectOrClassName);
183
184    private function checkConstraint(Constraint $constraint)
185    {
186        if (!\in_array(Constraint::PROPERTY_CONSTRAINT, (array) $constraint->getTargets(), true)) {
187            throw new ConstraintDefinitionException(sprintf('The constraint "%s" cannot be put on properties or getters.', \get_class($constraint)));
188        }
189
190        if ($constraint instanceof Composite) {
191            foreach ($constraint->getNestedContraints() as $nestedContraint) {
192                $this->checkConstraint($nestedContraint);
193            }
194        }
195    }
196}
197