Constraint.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator;
  11. use Symfony\Component\Validator\Exception\InvalidOptionsException;
  12. use Symfony\Component\Validator\Exception\MissingOptionsException;
  13. use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
  14. /**
  15. * Contains the properties of a constraint definition.
  16. *
  17. * A constraint can be defined on a class, an option or a getter method.
  18. * The Constraint class encapsulates all the configuration required for
  19. * validating this class, option or getter result successfully.
  20. *
  21. * Constraint instances are immutable and serializable.
  22. *
  23. * @author Bernhard Schussek <bschussek@gmail.com>
  24. *
  25. * @api
  26. */
  27. abstract class Constraint
  28. {
  29. /**
  30. * The name of the group given to all constraints with no explicit group
  31. * @var string
  32. */
  33. const DEFAULT_GROUP = 'Default';
  34. /**
  35. * Marks a constraint that can be put onto classes
  36. * @var string
  37. */
  38. const CLASS_CONSTRAINT = 'class';
  39. /**
  40. * Marks a constraint that can be put onto properties
  41. * @var string
  42. */
  43. const PROPERTY_CONSTRAINT = 'property';
  44. /**
  45. * @var array
  46. */
  47. public $groups = array(self::DEFAULT_GROUP);
  48. /**
  49. * Initializes the constraint with options.
  50. *
  51. * You should pass an associative array. The keys should be the names of
  52. * existing properties in this class. The values should be the value for these
  53. * properties.
  54. *
  55. * Alternatively you can override the method getDefaultOption() to return the
  56. * name of an existing property. If no associative array is passed, this
  57. * property is set instead.
  58. *
  59. * You can force that certain options are set by overriding
  60. * getRequiredOptions() to return the names of these options. If any
  61. * option is not set here, an exception is thrown.
  62. *
  63. * @param mixed $options The options (as associative array)
  64. * or the value for the default
  65. * option (any other type)
  66. *
  67. * @throws InvalidOptionsException When you pass the names of non-existing
  68. * options
  69. * @throws MissingOptionsException When you don't pass any of the options
  70. * returned by getRequiredOptions()
  71. * @throws ConstraintDefinitionException When you don't pass an associative
  72. * array, but getDefaultOption() returns
  73. * NULL
  74. *
  75. * @api
  76. */
  77. public function __construct($options = null)
  78. {
  79. $invalidOptions = array();
  80. $missingOptions = array_flip((array) $this->getRequiredOptions());
  81. if (is_array($options) && count($options) == 1 && isset($options['value'])) {
  82. $options = $options['value'];
  83. }
  84. if (is_array($options) && count($options) > 0 && is_string(key($options))) {
  85. foreach ($options as $option => $value) {
  86. if (property_exists($this, $option)) {
  87. $this->$option = $value;
  88. unset($missingOptions[$option]);
  89. } else {
  90. $invalidOptions[] = $option;
  91. }
  92. }
  93. } elseif (null !== $options && ! (is_array($options) && count($options) === 0)) {
  94. $option = $this->getDefaultOption();
  95. if (null === $option) {
  96. throw new ConstraintDefinitionException(
  97. sprintf('No default option is configured for constraint %s', get_class($this))
  98. );
  99. }
  100. if (property_exists($this, $option)) {
  101. $this->$option = $options;
  102. unset($missingOptions[$option]);
  103. } else {
  104. $invalidOptions[] = $option;
  105. }
  106. }
  107. if (count($invalidOptions) > 0) {
  108. throw new InvalidOptionsException(
  109. sprintf('The options "%s" do not exist in constraint %s', implode('", "', $invalidOptions), get_class($this)),
  110. $invalidOptions
  111. );
  112. }
  113. if (count($missingOptions) > 0) {
  114. throw new MissingOptionsException(
  115. sprintf('The options "%s" must be set for constraint %s', implode('", "', array_keys($missingOptions)), get_class($this)),
  116. array_keys($missingOptions)
  117. );
  118. }
  119. $this->groups = (array) $this->groups;
  120. }
  121. /**
  122. * Unsupported operation.
  123. */
  124. public function __set($option, $value)
  125. {
  126. throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
  127. }
  128. /**
  129. * Adds the given group if this constraint is in the Default group
  130. *
  131. * @param string $group
  132. *
  133. * @api
  134. */
  135. public function addImplicitGroupName($group)
  136. {
  137. if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
  138. $this->groups[] = $group;
  139. }
  140. }
  141. /**
  142. * Returns the name of the default option
  143. *
  144. * Override this method to define a default option.
  145. *
  146. * @return string
  147. * @see __construct()
  148. *
  149. * @api
  150. */
  151. public function getDefaultOption()
  152. {
  153. return null;
  154. }
  155. /**
  156. * Returns the name of the required options
  157. *
  158. * Override this method if you want to define required options.
  159. *
  160. * @return array
  161. * @see __construct()
  162. *
  163. * @api
  164. */
  165. public function getRequiredOptions()
  166. {
  167. return array();
  168. }
  169. /**
  170. * Returns the name of the class that validates this constraint
  171. *
  172. * By default, this is the fully qualified name of the constraint class
  173. * suffixed with "Validator". You can override this method to change that
  174. * behaviour.
  175. *
  176. * @return string
  177. *
  178. * @api
  179. */
  180. public function validatedBy()
  181. {
  182. return get_class($this).'Validator';
  183. }
  184. /**
  185. * Returns whether the constraint can be put onto classes, properties or
  186. * both
  187. *
  188. * This method should return one or more of the constants
  189. * Constraint::CLASS_CONSTRAINT and Constraint::PROPERTY_CONSTRAINT.
  190. *
  191. * @return string|array One or more constant values
  192. *
  193. * @api
  194. */
  195. public function getTargets()
  196. {
  197. return self::PROPERTY_CONSTRAINT;
  198. }
  199. }