vendor/symfony/dependency-injection/Definition.php line 922

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\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\Argument\BoundArgument;
  12. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  13. use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException;
  14. /**
  15.  * Definition represents a service definition.
  16.  *
  17.  * @author Fabien Potencier <fabien@symfony.com>
  18.  */
  19. class Definition
  20. {
  21.     private const DEFAULT_DEPRECATION_TEMPLATE 'The "%service_id%" service is deprecated. You should stop using it, as it will be removed in the future.';
  22.     private $class;
  23.     private $file;
  24.     private $factory;
  25.     private $shared true;
  26.     private $deprecated false;
  27.     private $deprecationTemplate;
  28.     private $properties = [];
  29.     private $calls = [];
  30.     private $instanceof = [];
  31.     private $autoconfigured false;
  32.     private $configurator;
  33.     private $tags = [];
  34.     private $public true;
  35.     private $private true;
  36.     private $synthetic false;
  37.     private $abstract false;
  38.     private $lazy false;
  39.     private $decoratedService;
  40.     private $autowired false;
  41.     private $changes = [];
  42.     private $bindings = [];
  43.     private $errors = [];
  44.     protected $arguments = [];
  45.     /**
  46.      * @internal
  47.      *
  48.      * Used to store the name of the inner id when using service decoration together with autowiring
  49.      */
  50.     public $innerServiceId;
  51.     /**
  52.      * @internal
  53.      *
  54.      * Used to store the behavior to follow when using service decoration and the decorated service is invalid
  55.      */
  56.     public $decorationOnInvalid;
  57.     /**
  58.      * @param string|null $class     The service class
  59.      * @param array       $arguments An array of arguments to pass to the service constructor
  60.      */
  61.     public function __construct($class null, array $arguments = [])
  62.     {
  63.         if (null !== $class) {
  64.             $this->setClass($class);
  65.         }
  66.         $this->arguments $arguments;
  67.     }
  68.     /**
  69.      * Returns all changes tracked for the Definition object.
  70.      *
  71.      * @return array An array of changes for this Definition
  72.      */
  73.     public function getChanges()
  74.     {
  75.         return $this->changes;
  76.     }
  77.     /**
  78.      * Sets the tracked changes for the Definition object.
  79.      *
  80.      * @param array $changes An array of changes for this Definition
  81.      *
  82.      * @return $this
  83.      */
  84.     public function setChanges(array $changes)
  85.     {
  86.         $this->changes $changes;
  87.         return $this;
  88.     }
  89.     /**
  90.      * Sets a factory.
  91.      *
  92.      * @param string|array|Reference|null $factory A PHP function, reference or an array containing a class/Reference and a method to call
  93.      *
  94.      * @return $this
  95.      */
  96.     public function setFactory($factory)
  97.     {
  98.         $this->changes['factory'] = true;
  99.         if (\is_string($factory) && str_contains($factory'::')) {
  100.             $factory explode('::'$factory2);
  101.         } elseif ($factory instanceof Reference) {
  102.             $factory = [$factory'__invoke'];
  103.         }
  104.         $this->factory $factory;
  105.         return $this;
  106.     }
  107.     /**
  108.      * Gets the factory.
  109.      *
  110.      * @return string|array|null The PHP function or an array containing a class/Reference and a method to call
  111.      */
  112.     public function getFactory()
  113.     {
  114.         return $this->factory;
  115.     }
  116.     /**
  117.      * Sets the service that this service is decorating.
  118.      *
  119.      * @param string|null $id              The decorated service id, use null to remove decoration
  120.      * @param string|null $renamedId       The new decorated service id
  121.      * @param int         $priority        The priority of decoration
  122.      * @param int         $invalidBehavior The behavior to adopt when decorated is invalid
  123.      *
  124.      * @return $this
  125.      *
  126.      * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals
  127.      */
  128.     public function setDecoratedService($id$renamedId null$priority 0/* , int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE */)
  129.     {
  130.         if ($renamedId && $id === $renamedId) {
  131.             throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.'$id));
  132.         }
  133.         $invalidBehavior < \func_num_args() ? (int) func_get_arg(3) : ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE;
  134.         $this->changes['decorated_service'] = true;
  135.         if (null === $id) {
  136.             $this->decoratedService null;
  137.         } else {
  138.             $this->decoratedService = [$id$renamedId, (int) $priority];
  139.             if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
  140.                 $this->decoratedService[] = $invalidBehavior;
  141.             }
  142.         }
  143.         return $this;
  144.     }
  145.     /**
  146.      * Gets the service that this service is decorating.
  147.      *
  148.      * @return array|null An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated
  149.      */
  150.     public function getDecoratedService()
  151.     {
  152.         return $this->decoratedService;
  153.     }
  154.     /**
  155.      * Sets the service class.
  156.      *
  157.      * @param string $class The service class
  158.      *
  159.      * @return $this
  160.      */
  161.     public function setClass($class)
  162.     {
  163.         if ($class instanceof Parameter) {
  164.             @trigger_error(sprintf('Passing an instance of %s as class name to %s in deprecated in Symfony 4.4 and will result in a TypeError in 5.0. Please pass the string "%%%s%%" instead.'Parameter::class, __CLASS__, (string) $class), \E_USER_DEPRECATED);
  165.         }
  166.         if (null !== $class && !\is_string($class)) {
  167.             @trigger_error(sprintf('The class name passed to %s is expected to be a string. Passing a %s is deprecated in Symfony 4.4 and will result in a TypeError in 5.0.'__CLASS__, \is_object($class) ? \get_class($class) : \gettype($class)), \E_USER_DEPRECATED);
  168.         }
  169.         $this->changes['class'] = true;
  170.         $this->class $class;
  171.         return $this;
  172.     }
  173.     /**
  174.      * Gets the service class.
  175.      *
  176.      * @return string|null The service class
  177.      */
  178.     public function getClass()
  179.     {
  180.         return $this->class;
  181.     }
  182.     /**
  183.      * Sets the arguments to pass to the service constructor/factory method.
  184.      *
  185.      * @return $this
  186.      */
  187.     public function setArguments(array $arguments)
  188.     {
  189.         $this->arguments $arguments;
  190.         return $this;
  191.     }
  192.     /**
  193.      * Sets the properties to define when creating the service.
  194.      *
  195.      * @return $this
  196.      */
  197.     public function setProperties(array $properties)
  198.     {
  199.         $this->properties $properties;
  200.         return $this;
  201.     }
  202.     /**
  203.      * Gets the properties to define when creating the service.
  204.      *
  205.      * @return array
  206.      */
  207.     public function getProperties()
  208.     {
  209.         return $this->properties;
  210.     }
  211.     /**
  212.      * Sets a specific property.
  213.      *
  214.      * @param string $name
  215.      * @param mixed  $value
  216.      *
  217.      * @return $this
  218.      */
  219.     public function setProperty($name$value)
  220.     {
  221.         $this->properties[$name] = $value;
  222.         return $this;
  223.     }
  224.     /**
  225.      * Adds an argument to pass to the service constructor/factory method.
  226.      *
  227.      * @param mixed $argument An argument
  228.      *
  229.      * @return $this
  230.      */
  231.     public function addArgument($argument)
  232.     {
  233.         $this->arguments[] = $argument;
  234.         return $this;
  235.     }
  236.     /**
  237.      * Replaces a specific argument.
  238.      *
  239.      * @param int|string $index
  240.      * @param mixed      $argument
  241.      *
  242.      * @return $this
  243.      *
  244.      * @throws OutOfBoundsException When the replaced argument does not exist
  245.      */
  246.     public function replaceArgument($index$argument)
  247.     {
  248.         if (=== \count($this->arguments)) {
  249.             throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.');
  250.         }
  251.         if (\is_int($index) && ($index || $index > \count($this->arguments) - 1)) {
  252.             throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].'$index, \count($this->arguments) - 1));
  253.         }
  254.         if (!\array_key_exists($index$this->arguments)) {
  255.             throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.'$index));
  256.         }
  257.         $this->arguments[$index] = $argument;
  258.         return $this;
  259.     }
  260.     /**
  261.      * Sets a specific argument.
  262.      *
  263.      * @param int|string $key
  264.      * @param mixed      $value
  265.      *
  266.      * @return $this
  267.      */
  268.     public function setArgument($key$value)
  269.     {
  270.         $this->arguments[$key] = $value;
  271.         return $this;
  272.     }
  273.     /**
  274.      * Gets the arguments to pass to the service constructor/factory method.
  275.      *
  276.      * @return array The array of arguments
  277.      */
  278.     public function getArguments()
  279.     {
  280.         return $this->arguments;
  281.     }
  282.     /**
  283.      * Gets an argument to pass to the service constructor/factory method.
  284.      *
  285.      * @param int|string $index
  286.      *
  287.      * @return mixed The argument value
  288.      *
  289.      * @throws OutOfBoundsException When the argument does not exist
  290.      */
  291.     public function getArgument($index)
  292.     {
  293.         if (!\array_key_exists($index$this->arguments)) {
  294.             throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.'$index));
  295.         }
  296.         return $this->arguments[$index];
  297.     }
  298.     /**
  299.      * Sets the methods to call after service initialization.
  300.      *
  301.      * @return $this
  302.      */
  303.     public function setMethodCalls(array $calls = [])
  304.     {
  305.         $this->calls = [];
  306.         foreach ($calls as $call) {
  307.             $this->addMethodCall($call[0], $call[1], $call[2] ?? false);
  308.         }
  309.         return $this;
  310.     }
  311.     /**
  312.      * Adds a method to call after service initialization.
  313.      *
  314.      * @param string $method       The method name to call
  315.      * @param array  $arguments    An array of arguments to pass to the method call
  316.      * @param bool   $returnsClone Whether the call returns the service instance or not
  317.      *
  318.      * @return $this
  319.      *
  320.      * @throws InvalidArgumentException on empty $method param
  321.      */
  322.     public function addMethodCall($method, array $arguments = []/* , bool $returnsClone = false */)
  323.     {
  324.         if (empty($method)) {
  325.             throw new InvalidArgumentException('Method name cannot be empty.');
  326.         }
  327.         $this->calls[] = < \func_num_args() && func_get_arg(2) ? [$method$argumentstrue] : [$method$arguments];
  328.         return $this;
  329.     }
  330.     /**
  331.      * Removes a method to call after service initialization.
  332.      *
  333.      * @param string $method The method name to remove
  334.      *
  335.      * @return $this
  336.      */
  337.     public function removeMethodCall($method)
  338.     {
  339.         foreach ($this->calls as $i => $call) {
  340.             if ($call[0] === $method) {
  341.                 unset($this->calls[$i]);
  342.             }
  343.         }
  344.         return $this;
  345.     }
  346.     /**
  347.      * Check if the current definition has a given method to call after service initialization.
  348.      *
  349.      * @param string $method The method name to search for
  350.      *
  351.      * @return bool
  352.      */
  353.     public function hasMethodCall($method)
  354.     {
  355.         foreach ($this->calls as $call) {
  356.             if ($call[0] === $method) {
  357.                 return true;
  358.             }
  359.         }
  360.         return false;
  361.     }
  362.     /**
  363.      * Gets the methods to call after service initialization.
  364.      *
  365.      * @return array An array of method calls
  366.      */
  367.     public function getMethodCalls()
  368.     {
  369.         return $this->calls;
  370.     }
  371.     /**
  372.      * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
  373.      *
  374.      * @param ChildDefinition[] $instanceof
  375.      *
  376.      * @return $this
  377.      */
  378.     public function setInstanceofConditionals(array $instanceof)
  379.     {
  380.         $this->instanceof $instanceof;
  381.         return $this;
  382.     }
  383.     /**
  384.      * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class.
  385.      *
  386.      * @return ChildDefinition[]
  387.      */
  388.     public function getInstanceofConditionals()
  389.     {
  390.         return $this->instanceof;
  391.     }
  392.     /**
  393.      * Sets whether or not instanceof conditionals should be prepended with a global set.
  394.      *
  395.      * @param bool $autoconfigured
  396.      *
  397.      * @return $this
  398.      */
  399.     public function setAutoconfigured($autoconfigured)
  400.     {
  401.         $this->changes['autoconfigured'] = true;
  402.         $this->autoconfigured $autoconfigured;
  403.         return $this;
  404.     }
  405.     /**
  406.      * @return bool
  407.      */
  408.     public function isAutoconfigured()
  409.     {
  410.         return $this->autoconfigured;
  411.     }
  412.     /**
  413.      * Sets tags for this definition.
  414.      *
  415.      * @return $this
  416.      */
  417.     public function setTags(array $tags)
  418.     {
  419.         $this->tags $tags;
  420.         return $this;
  421.     }
  422.     /**
  423.      * Returns all tags.
  424.      *
  425.      * @return array An array of tags
  426.      */
  427.     public function getTags()
  428.     {
  429.         return $this->tags;
  430.     }
  431.     /**
  432.      * Gets a tag by name.
  433.      *
  434.      * @param string $name The tag name
  435.      *
  436.      * @return array An array of attributes
  437.      */
  438.     public function getTag($name)
  439.     {
  440.         return $this->tags[$name] ?? [];
  441.     }
  442.     /**
  443.      * Adds a tag for this definition.
  444.      *
  445.      * @param string $name       The tag name
  446.      * @param array  $attributes An array of attributes
  447.      *
  448.      * @return $this
  449.      */
  450.     public function addTag($name, array $attributes = [])
  451.     {
  452.         $this->tags[$name][] = $attributes;
  453.         return $this;
  454.     }
  455.     /**
  456.      * Whether this definition has a tag with the given name.
  457.      *
  458.      * @param string $name
  459.      *
  460.      * @return bool
  461.      */
  462.     public function hasTag($name)
  463.     {
  464.         return isset($this->tags[$name]);
  465.     }
  466.     /**
  467.      * Clears all tags for a given name.
  468.      *
  469.      * @param string $name The tag name
  470.      *
  471.      * @return $this
  472.      */
  473.     public function clearTag($name)
  474.     {
  475.         unset($this->tags[$name]);
  476.         return $this;
  477.     }
  478.     /**
  479.      * Clears the tags for this definition.
  480.      *
  481.      * @return $this
  482.      */
  483.     public function clearTags()
  484.     {
  485.         $this->tags = [];
  486.         return $this;
  487.     }
  488.     /**
  489.      * Sets a file to require before creating the service.
  490.      *
  491.      * @param string $file A full pathname to include
  492.      *
  493.      * @return $this
  494.      */
  495.     public function setFile($file)
  496.     {
  497.         $this->changes['file'] = true;
  498.         $this->file $file;
  499.         return $this;
  500.     }
  501.     /**
  502.      * Gets the file to require before creating the service.
  503.      *
  504.      * @return string|null The full pathname to include
  505.      */
  506.     public function getFile()
  507.     {
  508.         return $this->file;
  509.     }
  510.     /**
  511.      * Sets if the service must be shared or not.
  512.      *
  513.      * @param bool $shared Whether the service must be shared or not
  514.      *
  515.      * @return $this
  516.      */
  517.     public function setShared($shared)
  518.     {
  519.         $this->changes['shared'] = true;
  520.         $this->shared = (bool) $shared;
  521.         return $this;
  522.     }
  523.     /**
  524.      * Whether this service is shared.
  525.      *
  526.      * @return bool
  527.      */
  528.     public function isShared()
  529.     {
  530.         return $this->shared;
  531.     }
  532.     /**
  533.      * Sets the visibility of this service.
  534.      *
  535.      * @param bool $boolean
  536.      *
  537.      * @return $this
  538.      */
  539.     public function setPublic($boolean)
  540.     {
  541.         $this->changes['public'] = true;
  542.         $this->public = (bool) $boolean;
  543.         $this->private false;
  544.         return $this;
  545.     }
  546.     /**
  547.      * Whether this service is public facing.
  548.      *
  549.      * @return bool
  550.      */
  551.     public function isPublic()
  552.     {
  553.         return $this->public;
  554.     }
  555.     /**
  556.      * Sets if this service is private.
  557.      *
  558.      * When set, the "private" state has a higher precedence than "public".
  559.      * In version 3.4, a "private" service always remains publicly accessible,
  560.      * but triggers a deprecation notice when accessed from the container,
  561.      * so that the service can be made really private in 4.0.
  562.      *
  563.      * @param bool $boolean
  564.      *
  565.      * @return $this
  566.      */
  567.     public function setPrivate($boolean)
  568.     {
  569.         $this->private = (bool) $boolean;
  570.         return $this;
  571.     }
  572.     /**
  573.      * Whether this service is private.
  574.      *
  575.      * @return bool
  576.      */
  577.     public function isPrivate()
  578.     {
  579.         return $this->private;
  580.     }
  581.     /**
  582.      * Sets the lazy flag of this service.
  583.      *
  584.      * @param bool $lazy
  585.      *
  586.      * @return $this
  587.      */
  588.     public function setLazy($lazy)
  589.     {
  590.         $this->changes['lazy'] = true;
  591.         $this->lazy = (bool) $lazy;
  592.         return $this;
  593.     }
  594.     /**
  595.      * Whether this service is lazy.
  596.      *
  597.      * @return bool
  598.      */
  599.     public function isLazy()
  600.     {
  601.         return $this->lazy;
  602.     }
  603.     /**
  604.      * Sets whether this definition is synthetic, that is not constructed by the
  605.      * container, but dynamically injected.
  606.      *
  607.      * @param bool $boolean
  608.      *
  609.      * @return $this
  610.      */
  611.     public function setSynthetic($boolean)
  612.     {
  613.         $this->synthetic = (bool) $boolean;
  614.         return $this;
  615.     }
  616.     /**
  617.      * Whether this definition is synthetic, that is not constructed by the
  618.      * container, but dynamically injected.
  619.      *
  620.      * @return bool
  621.      */
  622.     public function isSynthetic()
  623.     {
  624.         return $this->synthetic;
  625.     }
  626.     /**
  627.      * Whether this definition is abstract, that means it merely serves as a
  628.      * template for other definitions.
  629.      *
  630.      * @param bool $boolean
  631.      *
  632.      * @return $this
  633.      */
  634.     public function setAbstract($boolean)
  635.     {
  636.         $this->abstract = (bool) $boolean;
  637.         return $this;
  638.     }
  639.     /**
  640.      * Whether this definition is abstract, that means it merely serves as a
  641.      * template for other definitions.
  642.      *
  643.      * @return bool
  644.      */
  645.     public function isAbstract()
  646.     {
  647.         return $this->abstract;
  648.     }
  649.     /**
  650.      * Whether this definition is deprecated, that means it should not be called
  651.      * anymore.
  652.      *
  653.      * @param bool   $status
  654.      * @param string $template Template message to use if the definition is deprecated
  655.      *
  656.      * @return $this
  657.      *
  658.      * @throws InvalidArgumentException when the message template is invalid
  659.      */
  660.     public function setDeprecated($status true$template null)
  661.     {
  662.         if (null !== $template) {
  663.             if (preg_match('#[\r\n]|\*/#'$template)) {
  664.                 throw new InvalidArgumentException('Invalid characters found in deprecation template.');
  665.             }
  666.             if (!str_contains($template'%service_id%')) {
  667.                 throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.');
  668.             }
  669.             $this->deprecationTemplate $template;
  670.         }
  671.         $this->changes['deprecated'] = true;
  672.         $this->deprecated = (bool) $status;
  673.         return $this;
  674.     }
  675.     /**
  676.      * Whether this definition is deprecated, that means it should not be called
  677.      * anymore.
  678.      *
  679.      * @return bool
  680.      */
  681.     public function isDeprecated()
  682.     {
  683.         return $this->deprecated;
  684.     }
  685.     /**
  686.      * Message to use if this definition is deprecated.
  687.      *
  688.      * @param string $id Service id relying on this definition
  689.      *
  690.      * @return string
  691.      */
  692.     public function getDeprecationMessage($id)
  693.     {
  694.         return str_replace('%service_id%'$id$this->deprecationTemplate ?: self::DEFAULT_DEPRECATION_TEMPLATE);
  695.     }
  696.     /**
  697.      * Sets a configurator to call after the service is fully initialized.
  698.      *
  699.      * @param string|array|Reference|null $configurator A PHP function, reference or an array containing a class/Reference and a method to call
  700.      *
  701.      * @return $this
  702.      */
  703.     public function setConfigurator($configurator)
  704.     {
  705.         $this->changes['configurator'] = true;
  706.         if (\is_string($configurator) && str_contains($configurator'::')) {
  707.             $configurator explode('::'$configurator2);
  708.         } elseif ($configurator instanceof Reference) {
  709.             $configurator = [$configurator'__invoke'];
  710.         }
  711.         $this->configurator $configurator;
  712.         return $this;
  713.     }
  714.     /**
  715.      * Gets the configurator to call after the service is fully initialized.
  716.      *
  717.      * @return callable|array|null
  718.      */
  719.     public function getConfigurator()
  720.     {
  721.         return $this->configurator;
  722.     }
  723.     /**
  724.      * Is the definition autowired?
  725.      *
  726.      * @return bool
  727.      */
  728.     public function isAutowired()
  729.     {
  730.         return $this->autowired;
  731.     }
  732.     /**
  733.      * Enables/disables autowiring.
  734.      *
  735.      * @param bool $autowired
  736.      *
  737.      * @return $this
  738.      */
  739.     public function setAutowired($autowired)
  740.     {
  741.         $this->changes['autowired'] = true;
  742.         $this->autowired = (bool) $autowired;
  743.         return $this;
  744.     }
  745.     /**
  746.      * Gets bindings.
  747.      *
  748.      * @return array|BoundArgument[]
  749.      */
  750.     public function getBindings()
  751.     {
  752.         return $this->bindings;
  753.     }
  754.     /**
  755.      * Sets bindings.
  756.      *
  757.      * Bindings map $named or FQCN arguments to values that should be
  758.      * injected in the matching parameters (of the constructor, of methods
  759.      * called and of controller actions).
  760.      *
  761.      * @return $this
  762.      */
  763.     public function setBindings(array $bindings)
  764.     {
  765.         foreach ($bindings as $key => $binding) {
  766.             if (strpos($key'$') && $key !== $k preg_replace('/[ \t]*\$/'' $'$key)) {
  767.                 unset($bindings[$key]);
  768.                 $bindings[$key $k] = $binding;
  769.             }
  770.             if (!$binding instanceof BoundArgument) {
  771.                 $bindings[$key] = new BoundArgument($binding);
  772.             }
  773.         }
  774.         $this->bindings $bindings;
  775.         return $this;
  776.     }
  777.     /**
  778.      * Add an error that occurred when building this Definition.
  779.      *
  780.      * @param string|\Closure|self $error
  781.      *
  782.      * @return $this
  783.      */
  784.     public function addError($error)
  785.     {
  786.         if ($error instanceof self) {
  787.             $this->errors array_merge($this->errors$error->errors);
  788.         } else {
  789.             $this->errors[] = $error;
  790.         }
  791.         return $this;
  792.     }
  793.     /**
  794.      * Returns any errors that occurred while building this Definition.
  795.      *
  796.      * @return array
  797.      */
  798.     public function getErrors()
  799.     {
  800.         foreach ($this->errors as $i => $error) {
  801.             if ($error instanceof \Closure) {
  802.                 $this->errors[$i] = (string) $error();
  803.             } elseif (!\is_string($error)) {
  804.                 $this->errors[$i] = (string) $error;
  805.             }
  806.         }
  807.         return $this->errors;
  808.     }
  809.     public function hasErrors(): bool
  810.     {
  811.         return (bool) $this->errors;
  812.     }
  813. }