vendor/symfony/validator/Constraint.php line 186

Open in your IDE?
  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\ConstraintDefinitionException;
  12. use Symfony\Component\Validator\Exception\InvalidArgumentException;
  13. use Symfony\Component\Validator\Exception\InvalidOptionsException;
  14. use Symfony\Component\Validator\Exception\MissingOptionsException;
  15. /**
  16.  * Contains the properties of a constraint definition.
  17.  *
  18.  * A constraint can be defined on a class, a property or a getter method.
  19.  * The Constraint class encapsulates all the configuration required for
  20.  * validating this class, property or getter result successfully.
  21.  *
  22.  * Constraint instances are immutable and serializable.
  23.  *
  24.  * @property string[] $groups The groups that the constraint belongs to
  25.  *
  26.  * @author Bernhard Schussek <bschussek@gmail.com>
  27.  */
  28. abstract class Constraint
  29. {
  30.     /**
  31.      * The name of the group given to all constraints with no explicit group.
  32.      */
  33.     public const DEFAULT_GROUP 'Default';
  34.     /**
  35.      * Marks a constraint that can be put onto classes.
  36.      */
  37.     public const CLASS_CONSTRAINT 'class';
  38.     /**
  39.      * Marks a constraint that can be put onto properties.
  40.      */
  41.     public const PROPERTY_CONSTRAINT 'property';
  42.     /**
  43.      * Maps error codes to the names of their constants.
  44.      */
  45.     protected static $errorNames = [];
  46.     /**
  47.      * Domain-specific data attached to a constraint.
  48.      *
  49.      * @var mixed
  50.      */
  51.     public $payload;
  52.     /**
  53.      * Returns the name of the given error code.
  54.      *
  55.      * @return string The name of the error code
  56.      *
  57.      * @throws InvalidArgumentException If the error code does not exist
  58.      */
  59.     public static function getErrorName(string $errorCode)
  60.     {
  61.         if (!isset(static::$errorNames[$errorCode])) {
  62.             throw new InvalidArgumentException(sprintf('The error code "%s" does not exist for constraint of type "%s".'$errorCode, static::class));
  63.         }
  64.         return static::$errorNames[$errorCode];
  65.     }
  66.     /**
  67.      * Initializes the constraint with options.
  68.      *
  69.      * You should pass an associative array. The keys should be the names of
  70.      * existing properties in this class. The values should be the value for these
  71.      * properties.
  72.      *
  73.      * Alternatively you can override the method getDefaultOption() to return the
  74.      * name of an existing property. If no associative array is passed, this
  75.      * property is set instead.
  76.      *
  77.      * You can force that certain options are set by overriding
  78.      * getRequiredOptions() to return the names of these options. If any
  79.      * option is not set here, an exception is thrown.
  80.      *
  81.      * @param mixed    $options The options (as associative array)
  82.      *                          or the value for the default
  83.      *                          option (any other type)
  84.      * @param string[] $groups  An array of validation groups
  85.      * @param mixed    $payload Domain-specific data attached to a constraint
  86.      *
  87.      * @throws InvalidOptionsException       When you pass the names of non-existing
  88.      *                                       options
  89.      * @throws MissingOptionsException       When you don't pass any of the options
  90.      *                                       returned by getRequiredOptions()
  91.      * @throws ConstraintDefinitionException When you don't pass an associative
  92.      *                                       array, but getDefaultOption() returns
  93.      *                                       null
  94.      */
  95.     public function __construct($options null, array $groups null$payload null)
  96.     {
  97.         $options $this->normalizeOptions($options);
  98.         if (null !== $groups) {
  99.             $options['groups'] = $groups;
  100.         }
  101.         $options['payload'] = $payload ?? $options['payload'] ?? null;
  102.         foreach ($options as $name => $value) {
  103.             $this->$name $value;
  104.         }
  105.     }
  106.     protected function normalizeOptions($options): array
  107.     {
  108.         $normalizedOptions = [];
  109.         $defaultOption $this->getDefaultOption();
  110.         $invalidOptions = [];
  111.         $missingOptions array_flip((array) $this->getRequiredOptions());
  112.         $knownOptions get_class_vars(static::class);
  113.         // The "groups" option is added to the object lazily
  114.         $knownOptions['groups'] = true;
  115.         if (\is_array($options) && isset($options['value']) && !property_exists($this'value')) {
  116.             if (null === $defaultOption) {
  117.                 throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint "%s".', static::class));
  118.             }
  119.             $options[$defaultOption] = $options['value'];
  120.             unset($options['value']);
  121.         }
  122.         if (\is_array($options)) {
  123.             reset($options);
  124.         }
  125.         if ($options && \is_array($options) && \is_string(key($options))) {
  126.             foreach ($options as $option => $value) {
  127.                 if (\array_key_exists($option$knownOptions)) {
  128.                     $normalizedOptions[$option] = $value;
  129.                     unset($missingOptions[$option]);
  130.                 } else {
  131.                     $invalidOptions[] = $option;
  132.                 }
  133.             }
  134.         } elseif (null !== $options && !(\is_array($options) && === \count($options))) {
  135.             if (null === $defaultOption) {
  136.                 throw new ConstraintDefinitionException(sprintf('No default option is configured for constraint "%s".', static::class));
  137.             }
  138.             if (\array_key_exists($defaultOption$knownOptions)) {
  139.                 $normalizedOptions[$defaultOption] = $options;
  140.                 unset($missingOptions[$defaultOption]);
  141.             } else {
  142.                 $invalidOptions[] = $defaultOption;
  143.             }
  144.         }
  145.         if (\count($invalidOptions) > 0) {
  146.             throw new InvalidOptionsException(sprintf('The options "%s" do not exist in constraint "%s".'implode('", "'$invalidOptions), static::class), $invalidOptions);
  147.         }
  148.         if (\count($missingOptions) > 0) {
  149.             throw new MissingOptionsException(sprintf('The options "%s" must be set for constraint "%s".'implode('", "'array_keys($missingOptions)), static::class), array_keys($missingOptions));
  150.         }
  151.         return $normalizedOptions;
  152.     }
  153.     /**
  154.      * Sets the value of a lazily initialized option.
  155.      *
  156.      * Corresponding properties are added to the object on first access. Hence
  157.      * this method will be called at most once per constraint instance and
  158.      * option name.
  159.      *
  160.      * @param mixed $value The value to set
  161.      *
  162.      * @throws InvalidOptionsException If an invalid option name is given
  163.      */
  164.     public function __set(string $option$value)
  165.     {
  166.         if ('groups' === $option) {
  167.             $this->groups = (array) $value;
  168.             return;
  169.         }
  170.         throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint "%s".'$option, static::class), [$option]);
  171.     }
  172.     /**
  173.      * Returns the value of a lazily initialized option.
  174.      *
  175.      * Corresponding properties are added to the object on first access. Hence
  176.      * this method will be called at most once per constraint instance and
  177.      * option name.
  178.      *
  179.      * @return mixed The value of the option
  180.      *
  181.      * @throws InvalidOptionsException If an invalid option name is given
  182.      *
  183.      * @internal this method should not be used or overwritten in userland code
  184.      */
  185.     public function __get(string $option)
  186.     {
  187.         if ('groups' === $option) {
  188.             $this->groups = [self::DEFAULT_GROUP];
  189.             return $this->groups;
  190.         }
  191.         throw new InvalidOptionsException(sprintf('The option "%s" does not exist in constraint "%s".'$option, static::class), [$option]);
  192.     }
  193.     /**
  194.      * @return bool
  195.      */
  196.     public function __isset(string $option)
  197.     {
  198.         return 'groups' === $option;
  199.     }
  200.     /**
  201.      * Adds the given group if this constraint is in the Default group.
  202.      */
  203.     public function addImplicitGroupName(string $group)
  204.     {
  205.         if (\in_array(self::DEFAULT_GROUP$this->groups) && !\in_array($group$this->groups)) {
  206.             $this->groups[] = $group;
  207.         }
  208.     }
  209.     /**
  210.      * Returns the name of the default option.
  211.      *
  212.      * Override this method to define a default option.
  213.      *
  214.      * @return string|null
  215.      *
  216.      * @see __construct()
  217.      */
  218.     public function getDefaultOption()
  219.     {
  220.         return null;
  221.     }
  222.     /**
  223.      * Returns the name of the required options.
  224.      *
  225.      * Override this method if you want to define required options.
  226.      *
  227.      * @return string[]
  228.      *
  229.      * @see __construct()
  230.      */
  231.     public function getRequiredOptions()
  232.     {
  233.         return [];
  234.     }
  235.     /**
  236.      * Returns the name of the class that validates this constraint.
  237.      *
  238.      * By default, this is the fully qualified name of the constraint class
  239.      * suffixed with "Validator". You can override this method to change that
  240.      * behavior.
  241.      *
  242.      * @return string
  243.      */
  244.     public function validatedBy()
  245.     {
  246.         return static::class.'Validator';
  247.     }
  248.     /**
  249.      * Returns whether the constraint can be put onto classes, properties or
  250.      * both.
  251.      *
  252.      * This method should return one or more of the constants
  253.      * Constraint::CLASS_CONSTRAINT and Constraint::PROPERTY_CONSTRAINT.
  254.      *
  255.      * @return string|string[] One or more constant values
  256.      */
  257.     public function getTargets()
  258.     {
  259.         return self::PROPERTY_CONSTRAINT;
  260.     }
  261.     /**
  262.      * Optimizes the serialized value to minimize storage space.
  263.      *
  264.      * @return array
  265.      *
  266.      * @internal
  267.      */
  268.     public function __sleep()
  269.     {
  270.         // Initialize "groups" option if it is not set
  271.         $this->groups;
  272.         return array_keys(get_object_vars($this));
  273.     }
  274. }