Вход Регистрация
Файл: vendor/symfony/mime/Header/AbstractHeader.php
Строк: 467
<?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 SymfonyComponentMimeHeader;

use 
SymfonyComponentMimeEncoderQpMimeHeaderEncoder;

/**
 * An abstract base MIME Header.
 *
 * @author Chris Corbyn
 */
abstract class AbstractHeader implements HeaderInterface
{
    public const 
PHRASE_PATTERN '(?:(?:(?:(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))*(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))|(?:(?:[ t]*(?:rn))?[ t])))?[a-zA-Z0-9!#$%&'*+-/=?^_`{}|~]+(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))*(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))|(?:(?:[ t]*(?:rn))?[ t])))?)|(?:(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))*(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))|(?:(?:[ t]*(?:rn))?[ t])))?"((?:(?:[ t]*(?:rn))?[ t])?(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21x23-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])))*(?:(?:[ t]*(?:rn))?[ t])?"(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))*(?:(?:(?:(?:[ t]*(?:rn))?[ t])?(((?:(?:(?:[ t]*(?:rn))?[ t])|(?:(?:[x01-x08x0Bx0Cx0E-x19x7F]|[x21-x27x2A-x5Bx5D-x7E])|(?:\[x00-x08x0Bx0Cx0E-x7F])|(?1)))*(?:(?:[ t]*(?:rn))?[ t])?)))|(?:(?:[ t]*(?:rn))?[ t])))?))+?)';

    private static QpMimeHeaderEncoder 
$encoder;

    private string 
$name;
    private int 
$lineLength = 76;
    private ?string 
$lang = null;
    private string 
$charset = 'utf-8';

    public function __construct(string 
$name)
    {
        
$this->name = $name;
    }

    /**
     * @return void
     */
    public function setCharset(string 
$charset)
    {
        
$this->charset = $charset;
    }

    public function getCharset(): ?string
    {
        return 
$this->charset;
    }

    /**
     * Set the language used in this Header.
     *
     * For example, for US English, 'en-us'.
     *
     * @return void
     */
    public function setLanguage(string 
$lang)
    {
        
$this->lang = $lang;
    }

    public function getLanguage(): ?string
    {
        return 
$this->lang;
    }

    public function getName(): string
    {
        return 
$this->name;
    }

    /**
     * @return void
     */
    public function setMaxLineLength(int 
$lineLength)
    {
        
$this->lineLength = $lineLength;
    }

    public function getMaxLineLength(): int
    {
        return 
$this->lineLength;
    }

    public function toString(): string
    {
        return 
$this->tokensToString($this->toTokens());
    }

    /**
     * Produces a compliant, formatted RFC 2822 'phrase' based on the string given.
     *
     * @param string 
$string  as displayed
     * @param bool   
$shorten the first line to make remove for header name
     */
    protected function createPhrase(HeaderInterface 
$header, string $string, string $charset, bool $shorten = false): string
    {
        // Treat token as exactly what was given
        
$phraseStr = $string;

        // If it's not valid
        if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', 
$phraseStr)) {
            // .. but it is just ascii text, try escaping some characters
            // and make it a quoted-string
            if (preg_match('/^[x00-x08x0Bx0Cx0E-x7F]*$/D', 
$phraseStr)) {
                foreach (['\', '"'] as 
$char) {
                    
$phraseStr = str_replace($char, '\'.$char$phraseStr);
                }
                
$phraseStr = '"'.$phraseStr.'"';
            } else {
                // ... otherwise it needs encoding
                // Determine space remaining on line if first line
                if (
$shorten) {
                    
$usedLength = strlen($header->getName().': ');
                } else {
                    
$usedLength = 0;
                }
                
$phraseStr = $this->encodeWords($header$string$usedLength);
            }
        } elseif (str_contains(
$phraseStr, '(')) {
            foreach (['\', '"'] as 
$char) {
                
$phraseStr = str_replace($char, '\'.$char$phraseStr);
            }
            
$phraseStr = '"'.$phraseStr.'"';
        }

        return 
$phraseStr;
    }

    /**
     * Encode needed word tokens within a string of input.
     */
    protected function encodeWords(HeaderInterface 
$header, string $input, int $usedLength = -1): string
    {
        
$value = '';
        
$tokens = $this->getEncodableWordTokens($input);
        foreach (
$tokens as $token) {
            // See RFC 2822, Sect 2.2 (really 2.2 ??)
            if (
$this->tokenNeedsEncoding($token)) {
                // Don't encode starting WSP
                
$firstChar = substr($token, 0, 1);
                switch (
$firstChar) {
                    case ' ':
                    case "t":
                        
$value .= $firstChar;
                        
$token = substr($token, 1);
                }

                if (-1 == 
$usedLength) {
                    
$usedLength = strlen($header->getName().': ') + strlen($value);
                }
                
$value .= $this->getTokenAsEncodedWord($token$usedLength);
            } else {
                
$value .= $token;
            }
        }

        return 
$value;
    }

    protected function tokenNeedsEncoding(string 
$token): bool
    {
        return (bool) preg_match('~[x00-x08x10-x19x7F-xFFrn]~', 
$token);
    }

    /**
     * Splits a string into tokens in blocks of words which can be encoded quickly.
     *
     * @return string[]
     */
    protected function getEncodableWordTokens(string 
$string): array
    {
        
$tokens = [];
        
$encodedToken = '';
        // Split at all whitespace boundaries
        foreach (preg_split('~(?=[t ])~', 
$string) as $token) {
            if (
$this->tokenNeedsEncoding($token)) {
                
$encodedToken .= $token;
            } else {
                if ('' !== 
$encodedToken) {
                    
$tokens[] = $encodedToken;
                    
$encodedToken = '';
                }
                
$tokens[] = $token;
            }
        }
        if ('' !== 
$encodedToken) {
            
$tokens[] = $encodedToken;
        }

        foreach (
$tokens as $i => $token) {
            // whitespace(s) between 2 encoded tokens
            if (
                0 < 
$i
                && isset(
$tokens[$i + 1])
                && preg_match('~^[t ]+$~', 
$token)
                && 
$this->tokenNeedsEncoding($tokens[$i - 1])
                && 
$this->tokenNeedsEncoding($tokens[$i + 1])
            ) {
                
$tokens[$i - 1] .= $token.$tokens[$i + 1];
                array_splice(
$tokens$i, 2);
            }
        }

        return 
$tokens;
    }

    /**
     * Get a token as an encoded word for safe insertion into headers.
     */
    protected function getTokenAsEncodedWord(string 
$token, int $firstLineOffset = 0): string
    {
        self::
$encoder ??= new QpMimeHeaderEncoder();

        // Adjust 
$firstLineOffset to account for space needed for syntax
        
$charsetDecl = $this->charset;
        if (null !== 
$this->lang) {
            
$charsetDecl .= '*'.$this->lang;
        }
        
$encodingWrapperLength = strlen('=?'.$charsetDecl.'?'.self::$encoder->getName().'??=');

        if (
$firstLineOffset >= 75) {
            // Does this logic need to be here?
            
$firstLineOffset = 0;
        }

        
$encodedTextLines = explode("rn",
            self::
$encoder->encodeString($token$this->charset$firstLineOffset, 75 - $encodingWrapperLength)
        );

        if ('iso-2022-jp' !== strtolower(
$this->charset)) {
            // special encoding for iso-2022-jp using mb_encode_mimeheader
            foreach (
$encodedTextLines as $lineNum => $line) {
                
$encodedTextLines[$lineNum] = '=?'.$charsetDecl.'?'.self::$encoder->getName().'?'.$line.'?=';
            }
        }

        return implode("rn ", 
$encodedTextLines);
    }

    /**
     * Generates tokens from the given string which include CRLF as individual tokens.
     *
     * @return string[]
     */
    protected function generateTokenLines(string 
$token): array
    {
        return preg_split('~(rn)~', 
$token, -1, PREG_SPLIT_DELIM_CAPTURE);
    }

    /**
     * Generate a list of all tokens in the final header.
     */
    protected function toTokens(?string 
$string = null): array
    {
        
$string ??= $this->getBodyAsString();

        
$tokens = [];
        // Generate atoms; split at all invisible boundaries followed by WSP
        foreach (preg_split('~(?=[ t])~', 
$string) as $token) {
            
$newTokens = $this->generateTokenLines($token);
            foreach (
$newTokens as $newToken) {
                
$tokens[] = $newToken;
            }
        }

        return 
$tokens;
    }

    /**
     * Takes an array of tokens which appear in the header and turns them into
     * an RFC 2822 compliant string, adding FWSP where needed.
     *
     * @param string[] 
$tokens
     */
    private function tokensToString(array 
$tokens): string
    {
        
$lineCount = 0;
        
$headerLines = [];
        
$headerLines[] = $this->name.': ';
        
$currentLine = &$headerLines[$lineCount++];

        // Build all tokens back into compliant header
        foreach (
$tokens as $i => $token) {
            // Line longer than specified maximum or token was just a new line
            if (("rn" === 
$token)
                || (
$i > 0 && strlen($currentLine.$token) > $this->lineLength)
                && '' !== 
$currentLine) {
                
$headerLines[] = '';
                
$currentLine = &$headerLines[$lineCount++];
            }

            // Append token to the line
            if ("rn" !== 
$token) {
                
$currentLine .= $token;
            }
        }

        // Implode with FWS (RFC 2822, 2.2.3)
        return implode("rn", 
$headerLines);
    }
}
Онлайн: 1
Реклама