Constraint.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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 <bernhard.schussek@symfony.com>
  24. */
  25. abstract class Constraint
  26. {
  27. /**
  28. * The name of the group given to all constraints with no explicit group
  29. * @var string
  30. */
  31. const DEFAULT_GROUP = 'Default';
  32. /**
  33. * Marks a constraint that can be put onto classes
  34. * @var string
  35. */
  36. const CLASS_CONSTRAINT = 'class';
  37. /**
  38. * Marks a constraint that can be put onto properties
  39. * @var string
  40. */
  41. const PROPERTY_CONSTRAINT = 'property';
  42. /**
  43. * @var array
  44. */
  45. public $groups = array(self::DEFAULT_GROUP);
  46. /**
  47. * Initializes the constraint with options.
  48. *
  49. * You should pass an associative array. The keys should be the names of
  50. * existing properties in this class. The values should be the value for these
  51. * properties.
  52. *
  53. * Alternatively you can override the method getDefaultOption() to return the
  54. * name of an existing property. If no associative array is passed, this
  55. * property is set instead.
  56. *
  57. * You can force that certain options are set by overriding
  58. * getRequiredOptions() to return the names of these options. If any
  59. * option is not set here, an exception is thrown.
  60. *
  61. * @param mixed $options The options (as associative array)
  62. * or the value for the default
  63. * option (any other type)
  64. *
  65. * @throws InvalidOptionsException When you pass the names of non-existing
  66. * options
  67. * @throws MissingOptionsException When you don't pass any of the options
  68. * returned by getRequiredOptions()
  69. * @throws ConstraintDefinitionException When you don't pass an associative
  70. * array, but getDefaultOption() returns
  71. * NULL
  72. */
  73. public function __construct($options = null)
  74. {
  75. $invalidOptions = array();
  76. $missingOptions = array_flip((array)$this->getRequiredOptions());
  77. if (is_array($options) && count($options) == 1 && isset($options['value'])) {
  78. $options = $options['value'];
  79. }
  80. if (is_array($options) && count($options) > 0 && is_string(key($options))) {
  81. foreach ($options as $option => $value) {
  82. if (property_exists($this, $option)) {
  83. $this->$option = $value;
  84. unset($missingOptions[$option]);
  85. } else {
  86. $invalidOptions[] = $option;
  87. }
  88. }
  89. } else if (null !== $options && ! (is_array($options) && count($options) === 0)) {
  90. $option = $this->getDefaultOption();
  91. if (null === $option) {
  92. throw new ConstraintDefinitionException(
  93. sprintf('No default option is configured for constraint %s', get_class($this))
  94. );
  95. }
  96. if (property_exists($this, $option)) {
  97. $this->$option = $options;
  98. unset($missingOptions[$option]);
  99. } else {
  100. $invalidOptions[] = $option;
  101. }
  102. }
  103. if (count($invalidOptions) > 0) {
  104. throw new InvalidOptionsException(
  105. sprintf('The options "%s" do not exist in constraint %s', implode('", "', $invalidOptions), get_class($this)),
  106. $invalidOptions
  107. );
  108. }
  109. if (count($missingOptions) > 0) {
  110. throw new MissingOptionsException(
  111. sprintf('The options "%s" must be set for constraint %s', implode('", "', array_keys($missingOptions)), get_class($this)),
  112. array_keys($missingOptions)
  113. );
  114. }
  115. $this->groups = (array)$this->groups;
  116. }
  117. /**
  118. * Unsupported operation.
  119. */
  120. public function __set($option, $value)
  121. {
  122. throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint %s', $option, get_class($this)), array($option));
  123. }
  124. /**
  125. * Adds the given group if this constraint is in the Default group
  126. *
  127. * @param string $group
  128. */
  129. public function addImplicitGroupName($group)
  130. {
  131. if (in_array(Constraint::DEFAULT_GROUP, $this->groups) && !in_array($group, $this->groups)) {
  132. $this->groups[] = $group;
  133. }
  134. }
  135. /**
  136. * Returns the name of the default option
  137. *
  138. * Override this method to define a default option.
  139. *
  140. * @return string
  141. * @see __construct()
  142. */
  143. public function getDefaultOption()
  144. {
  145. return null;
  146. }
  147. /**
  148. * Returns the name of the required options
  149. *
  150. * Override this method if you want to define required options.
  151. *
  152. * @return array
  153. * @see __construct()
  154. */
  155. public function getRequiredOptions()
  156. {
  157. return array();
  158. }
  159. /**
  160. * Returns the name of the class that validates this constraint
  161. *
  162. * By default, this is the fully qualified name of the constraint class
  163. * suffixed with "Validator". You can override this method to change that
  164. * behaviour.
  165. *
  166. * @return string
  167. */
  168. public function validatedBy()
  169. {
  170. return get_class($this).'Validator';
  171. }
  172. /**
  173. * Returns whether the constraint can be put onto classes, properties or
  174. * both
  175. *
  176. * This method should return one or more of the constants
  177. * Constraint::CLASS_CONSTRAINT and Constraint::PROPERTY_CONSTRAINT.
  178. *
  179. * @return string|array One or more constant values
  180. */
  181. abstract public function getTargets();
  182. }