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

Code before:
            foreach ($value as $val) {
                if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) {
                    $valid = false;
                }
            }

            return $valid;
        }

        if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) {
            return true;
        }

        if (\is_array($invalidTypes) && (!$invalidTypes || $level > 0)) {
            $invalidTypes[get_debug_type($value)] = true;
        }

        return false;
    }

    /**
     * @return list<string>
     */
    private function splitOutsideParenthesis(string $type): array
    {
        if (!str_contains($type, '|')) {
            return [$type];
        }

        if (!str_contains($type, '(') && !str_contains($type, ')')) {
            return explode('|', $type);
        }

        $parts = [];
        $start = 0;
        $parenthesisLevel = 0;
        $length = \strlen($type);

        for ($i = 0; $i < $length; ++$i) {
            $char = $type[$i];

            switch ($char) {
                case '(':
                    ++$parenthesisLevel;
                    break;
                case ')':
                    --$parenthesisLevel;
                    break;
                case '|':
                    if (0 === $parenthesisLevel) {
                        $parts[] = substr($type, $start, $i - $start);

Review comment (suggestion):
Maybe could we even store a hardcoded list of known simple types somewhere to avoid the repeated calls to `str_contains()` in these cases.

Here is a list: https://www.php.net/manual/en/function.gettype.php

Code after:
            foreach ($value as $val) {
                if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) {
                    $valid = false;
                }
            }

            return $valid;
        }

        if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) {
            return true;
        }

        if (\is_array($invalidTypes) && (!$invalidTypes || $level > 0)) {
            $invalidTypes[get_debug_type($value)] = true;
        }

        return false;
    }

    /**
     * @return list<string>
     */
    private function splitOutsideParenthesis(string $type): array
    {
        return preg_split(<<<'EOF'
                /
                # Define a recursive subroutine for matching balanced parentheses
                (?(DEFINE)
                    (?<balanced>
                        \(                          # Match an opening parenthesis
                        (?:                         # Start a non-capturing group for the contents
                            [^()]                   # Match any character that is not a parenthesis
                            |                       # OR
                            (?&balanced)            # Recursively match a nested balanced group
                        )*                          # Repeat the group for all contents
                        \)                          # Match the final closing parenthesis
                    )
                )

                # Match any balanced parenthetical group, then skip it
                (?&balanced)(*SKIP)(*FAIL)          # Use the defined subroutine and discard the match

                | # OR

                \|                                  # Match the pipe delimiter (only if not inside a skipped group)
                /x
        EOF, $type);
    }

    /**