Repository: symfony/symfony
File: src/Symfony/Component/Validator/Constraint.php (PHP)

Code before:
        $options = $this->normalizeOptions($options);
        if (null !== $groups) {
            $options['groups'] = $groups;
        }
        $options['payload'] = $payload ?? $options['payload'] ?? null;

        foreach ($options as $name => $value) {
            $this->$name = $value;
        }
    }

    /**
     * Checks whether the child class constructor has named parameters for each
     * required option, meaning it handles their validation itself.
     */
    private function areRequiredOptionsHandledByChildConstructor(): bool
    {
        $constructor = new \ReflectionMethod($this, '__construct');

        if (self::class === $constructor->getDeclaringClass()->getName()) {
            return false;
        }

        $requiredOptionsMethod = new \ReflectionMethod($this, 'getRequiredOptions');

        if (self::class === $requiredOptionsMethod->getDeclaringClass()->getName()) {
            return true;
        }

        $requiredOptions = $this->getRequiredOptions(false);

        if (!$requiredOptions) {
            return true;
        }

        $paramNames = [];
        foreach ($constructor->getParameters() as $param) {
            $paramNames[$param->getName()] = true;
        }

        foreach ($requiredOptions as $option) {
            if (!isset($paramNames[$option])) {
                return false;
            }
        }

        return true;
    }

    /**
     * @deprecated since Symfony 7.4

Review comment (suggestion):
```suggestion
        if (self::class === $requiredOptionsMethod->class) {
```

Code after:
        $options = $this->normalizeOptions($options);
        if (null !== $groups) {
            $options['groups'] = $groups;
        }
        $options['payload'] = $payload ?? $options['payload'] ?? null;

        foreach ($options as $name => $value) {
            $this->$name = $value;
        }
    }

    /**
     * @deprecated since Symfony 7.4
     *
     * @return array<string, mixed>
     */
    protected function normalizeOptions(mixed $options): array
    {
        $normalizedOptions = [];
        $defaultOption = $this->getDefaultOption(false);
        $invalidOptions = [];
        $missingOptions = array_flip($this->getRequiredOptions(false));
        $knownOptions = get_class_vars(static::class);

        if (\is_array($options) && isset($options['value']) && !property_exists($this, 'value')) {
            if (null === $defaultOption) {
                throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class));
            }

            $options[$defaultOption] = $options['value'];
            unset($options['value']);
        }

        if (\is_array($options)) {
            reset($options);
        }
        if ($options && \is_array($options) && \is_string(key($options))) {
            foreach ($options as $option => $value) {
                if (\array_key_exists($option, $knownOptions)) {
                    $normalizedOptions[$option] = $value;
                    unset($missingOptions[$option]);
                } else {
                    $invalidOptions[] = $option;
                }
            }
        } elseif (null !== $options && !(\is_array($options) && 0 === \count($options))) {
            if (null === $defaultOption) {
                throw new ConstraintDefinitionException(\sprintf('No default option is configured for constraint "%s".', static::class));
            }

            if (\array_key_exists($defaultOption, $knownOptions)) {