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

Code before:
        foreach ($normalizedAttributes as $name => $value) {
            if (null === $value) {
                $parameterParts[] = $name;
            } else {
                $quotedValue = \is_string($value) && str_contains($value, ' ') ? '"'.addslashes($value).'"' : $value;
                $parameterParts[] = $name.'='.$quotedValue;
            }
        }

        return implode(';', $parameterParts);
    }

    /**
     * Checks if a given header item (range) matches a queried item (value).
     *
     * @param AcceptHeaderItem $item      The item from the Accept header (e.g., text/*;format=flowed)
     * @param AcceptHeaderItem $queryItem The item being queried (e.g., text/plain;format=flowed;charset=utf-8)
     */
    private function matches(AcceptHeaderItem $item, AcceptHeaderItem $queryItem): bool
    {
        $requestedValue = $queryItem->getValue();
        if (!str_contains($requestedValue, '/')) {
            // Charset/Language: exact or *
            $rangeValue = $item->getValue();

            return strtolower($rangeValue) === strtolower($requestedValue) || '*' === strtolower($rangeValue) || '*/*' === strtolower($rangeValue);
        }
        // Media type
        [$requestedType, $requestedSubtype] = explode('/', $requestedValue, 2);
        if (!$requestedSubtype) {
            return false;
        }
        $rangeValue = $item->getValue();
        [$rangeType, $rangeSubtype] = explode('/', $rangeValue, 2) + ['', '*'];
        if ('*' !== $rangeType && strtolower($rangeType) !== strtolower($requestedType)) {
            return false;
        }
        if ('*' !== $rangeSubtype && strtolower($rangeSubtype) !== strtolower($requestedSubtype)) {
            return false;
        }
        // Check params: range params must be matched by value params
        $requestedAttributes = $queryItem->getAttributes();
        $normalizedRequestedAttrs = [];
        foreach ($requestedAttributes as $name => $val) {
            $normalizedRequestedAttrs[strtolower($name)] = $val;
        }
        $itemAttributes = $item->getAttributes();
        foreach ($itemAttributes as $name => $val) {
            $lowerName = strtolower($name);
            if ('q' === $lowerName) {
                continue;

Review comment (suggestion):
Again, can we be 100% sure that strtolower is the expected behavior of RFC9110? Also, what value does a string have, so that it's return value of `strtolower` equals `*` or '*/*'? 😇 😁 

Code after:
        if ('*' !== $rangeType && $rangeType !== $queryType) {
            return false;
        }

        if ('*' !== $rangeSubtype && $rangeSubtype !== $querySubtype) {
            return false;
        }

        // Parameters must match
        return $this->rangeParametersMatch($rangeItem, $queryItem);
    }

    /**
     * Checks if the parameters of a range item are satisfied by the query item.
     *
     * Parameters are case-insensitive; range params must be a subset of query params.
     */
    private function rangeParametersMatch(AcceptHeaderItem $rangeItem, AcceptHeaderItem $queryItem): bool
    {
        $queryAttributes = $this->getMediaParams($queryItem);
        $rangeAttributes = $this->getMediaParams($rangeItem);

        foreach ($rangeAttributes as $name => $rangeValue) {
            if (!\array_key_exists($name, $queryAttributes)) {
                return false; // Missing required param
            }

            $queryValue = $queryAttributes[$name];

            if (null === $rangeValue) {
                return null === $queryValue; // Both flags or neither
            }

            if (null === $queryValue || strtolower($queryValue) !== strtolower($rangeValue)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Calculates a specificity score for sorting: media precision + param count.
     */
    private function getSpecificity(AcceptHeaderItem $item, AcceptHeaderItem $queryItem): int
    {
        $rangeValue = strtolower($item->getValue());
        $queryValue = strtolower($queryItem->getValue());

        $paramCount = \count($this->getMediaParams($item));