Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

ParameterizedHeader.php

Namespace

Symfony\Component\Mime\Header

File

vendor/symfony/mime/Header/ParameterizedHeader.php

View source
<?php


/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Symfony\Component\Mime\Header;

use Symfony\Component\Mime\Encoder\Rfc2231Encoder;

/**
 * @author Chris Corbyn
 */
final class ParameterizedHeader extends UnstructuredHeader {
    
    /**
     * RFC 2231's definition of a token.
     *
     * @var string
     */
    public const TOKEN_REGEX = '(?:[\\x21\\x23-\\x27\\x2A\\x2B\\x2D\\x2E\\x30-\\x39\\x41-\\x5A\\x5E-\\x7E]+)';
    private ?Rfc2231Encoder $encoder = null;
    private array $parameters = [];
    public function __construct(string $name, string $value, array $parameters = []) {
        parent::__construct($name, $value);
        foreach ($parameters as $k => $v) {
            $this->setParameter($k, $v);
        }
        if ('content-type' !== strtolower($name)) {
            $this->encoder = new Rfc2231Encoder();
        }
    }
    public function setParameter(string $parameter, ?string $value) : void {
        $this->setParameters(array_merge($this->getParameters(), [
            $parameter => $value,
        ]));
    }
    public function getParameter(string $parameter) : string {
        return $this->getParameters()[$parameter] ?? '';
    }
    
    /**
     * @param string[] $parameters
     */
    public function setParameters(array $parameters) : void {
        $this->parameters = $parameters;
    }
    
    /**
     * @return string[]
     */
    public function getParameters() : array {
        return $this->parameters;
    }
    public function getBodyAsString() : string {
        $body = parent::getBodyAsString();
        foreach ($this->parameters as $name => $value) {
            if (null !== $value) {
                $body .= '; ' . $this->createParameter($name, $value);
            }
        }
        return $body;
    }
    
    /**
     * Generate a list of all tokens in the final header.
     *
     * This doesn't need to be overridden in theory, but it is for implementation
     * reasons to prevent potential breakage of attributes.
     */
    protected function toTokens(?string $string = null) : array {
        $tokens = parent::toTokens(parent::getBodyAsString());
        // Try creating any parameters
        foreach ($this->parameters as $name => $value) {
            if (null !== $value) {
                // Add the semi-colon separator
                $tokens[\count($tokens) - 1] .= ';';
                $tokens = array_merge($tokens, $this->generateTokenLines(' ' . $this->createParameter($name, $value)));
            }
        }
        return $tokens;
    }
    
    /**
     * Render an RFC 2047 compliant header parameter from the $name and $value.
     */
    private function createParameter(string $name, string $value) : string {
        $origValue = $value;
        $encoded = false;
        // Allow room for parameter name, indices, "=" and DQUOTEs
        $maxValueLength = $this->getMaxLineLength() - \strlen($name . '=*N"";') - 1;
        $firstLineOffset = 0;
        // If it's not already a valid parameter value...
        if (!preg_match('/^' . self::TOKEN_REGEX . '$/D', $value)) {
            // TODO: text, or something else??
            // ... and it's not ascii
            if (!preg_match('/^[\\x00-\\x08\\x0B\\x0C\\x0E-\\x7F]*$/D', $value)) {
                $encoded = true;
                // Allow space for the indices, charset and language
                $maxValueLength = $this->getMaxLineLength() - \strlen($name . '*N*="";') - 1;
                $firstLineOffset = \strlen($this->getCharset() . "'" . $this->getLanguage() . "'");
            }
            if (\in_array($name, [
                'name',
                'filename',
            ], true) && 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName()) && preg_match('//u', $value)) {
                // WHATWG HTML living standard 4.10.21.8 2 specifies:
                // For field names and filenames for file fields, the result of the
                // encoding in the previous bullet point must be escaped by replacing
                // any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
                // and 0x22 (") with `%22`.
                // The user agent must not perform any other escapes.
                $value = str_replace([
                    '"',
                    "\r",
                    "\n",
                ], [
                    '%22',
                    '%0D',
                    '%0A',
                ], $value);
                if (\strlen($value) <= $maxValueLength) {
                    return $name . '="' . $value . '"';
                }
                $value = $origValue;
            }
        }
        // Encode if we need to
        if ($encoded || \strlen($value) > $maxValueLength) {
            if (null !== $this->encoder) {
                $value = $this->encoder
                    ->encodeString($origValue, $this->getCharset(), $firstLineOffset, $maxValueLength);
            }
            else {
                // We have to go against RFC 2183/2231 in some areas for interoperability
                $value = $this->getTokenAsEncodedWord($origValue);
                $encoded = false;
            }
        }
        $valueLines = $this->encoder ? explode("\r\n", $value) : [
            $value,
        ];
        // Need to add indices
        if (\count($valueLines) > 1) {
            $paramLines = [];
            foreach ($valueLines as $i => $line) {
                $paramLines[] = $name . '*' . $i . $this->getEndOfParameterValue($line, true, 0 === $i);
            }
            return implode(";\r\n ", $paramLines);
        }
        return $name . $this->getEndOfParameterValue($valueLines[0], $encoded, true);
    }
    
    /**
     * Returns the parameter value from the "=" and beyond.
     *
     * @param string $value to append
     */
    private function getEndOfParameterValue(string $value, bool $encoded = false, bool $firstLine = false) : string {
        $forceHttpQuoting = 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName());
        if ($forceHttpQuoting || !preg_match('/^' . self::TOKEN_REGEX . '$/D', $value)) {
            $value = '"' . $value . '"';
        }
        $prepend = '=';
        if ($encoded) {
            $prepend = '*=';
            if ($firstLine) {
                $prepend = '*=' . $this->getCharset() . "'" . $this->getLanguage() . "'";
            }
        }
        return $prepend . $value;
    }

}

Classes

Title Deprecated Summary
ParameterizedHeader @author Chris Corbyn

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal