Find this useful? Enter your email to receive occasional updates for securing PHP code.

Signing you up...

Thank you for signing up!

PHP Decode

<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@..

Decoded Output download

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Translation;

use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * This translator should only be used in a development environment.
 */
final class PseudoLocalizationTranslator implements TranslatorInterface
{
    private const EXPANSION_CHARACTER = '~';

    private TranslatorInterface $translator;
    private bool $accents;
    private float $expansionFactor;
    private bool $brackets;
    private bool $parseHTML;

    /**
     * @var string[]
     */
    private array $localizableHTMLAttributes;

    /**
     * Available options:
     *  * accents:
     *      type: boolean
     *      default: true
     *      description: replace ASCII characters of the translated string with accented versions or similar characters
     *      example: if true, "foo" => "".
     *
     *  * expansion_factor:
     *      type: float
     *      default: 1
     *      validation: it must be greater than or equal to 1
     *      description: expand the translated string by the given factor with spaces and tildes
     *      example: if 2, "foo" => "~foo ~"
     *
     *  * brackets:
     *      type: boolean
     *      default: true
     *      description: wrap the translated string with brackets
     *      example: if true, "foo" => "[foo]"
     *
     *  * parse_html:
     *      type: boolean
     *      default: false
     *      description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML
     *      warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo <div>bar" => "foo <div>bar</div>"
     *
     *  * localizable_html_attributes:
     *      type: string[]
     *      default: []
     *      description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true
     *      example: if ["title"], and with the "accents" option set to true, "<a href="#" title="Go to your profile">Profile</a>" => "<a href="#" title=""></a>" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged.
     */
    public function __construct(TranslatorInterface $translator, array $options = [])
    {
        $this->translator = $translator;
        $this->accents = $options['accents'] ?? true;

        if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) {
            throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.');
        }

        $this->brackets = $options['brackets'] ?? true;

        $this->parseHTML = $options['parse_html'] ?? false;
        if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) {
            $this->parseHTML = false;
        }

        $this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? [];
    }

    public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
    {
        $trans = '';
        $visibleText = '';

        foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) {
            if ($visible) {
                $visibleText .= $text;
            }

            if (!$localizable) {
                $trans .= $text;

                continue;
            }

            $this->addAccents($trans, $text);
        }

        $this->expand($trans, $visibleText);

        $this->addBrackets($trans);

        return $trans;
    }

    public function getLocale(): string
    {
        return $this->translator->getLocale();
    }

    private function getParts(string $originalTrans): array
    {
        if (!$this->parseHTML) {
            return [[true, true, $originalTrans]];
        }

        $html = mb_encode_numericentity($originalTrans, [0x80, 0x10FFFF, 0, 0x1FFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8');

        $useInternalErrors = libxml_use_internal_errors(true);

        $dom = new \DOMDocument();
        $dom->loadHTML('<trans>'.$html.'</trans>');

        libxml_clear_errors();
        libxml_use_internal_errors($useInternalErrors);

        return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0));
    }

    private function parseNode(\DOMNode $node): array
    {
        $parts = [];

        foreach ($node->childNodes as $childNode) {
            if (!$childNode instanceof \DOMElement) {
                $parts[] = [true, true, $childNode->nodeValue];

                continue;
            }

            $parts[] = [false, false, '<'.$childNode->tagName];

            /** @var \DOMAttr $attribute */
            foreach ($childNode->attributes as $attribute) {
                $parts[] = [false, false, ' '.$attribute->nodeName.'="'];

                $localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, true);
                foreach (preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) {
                    if ('' === $match) {
                        continue;
                    }

                    $parts[] = [false, $localizableAttribute && 0 === $i % 2, $match];
                }

                $parts[] = [false, false, '"'];
            }

            $parts[] = [false, false, '>'];

            $parts = array_merge($parts, $this->parseNode($childNode, $parts));

            $parts[] = [false, false, '</'.$childNode->tagName.'>'];
        }

        return $parts;
    }

    private function addAccents(string &$trans, string $text): void
    {
        $trans .= $this->accents ? strtr($text, [
            ' ' => '',
            '!' => '',
            '"' => '',
            '#' => '',
            '$' => '',
            '%' => '',
            '&' => '',
            '\'' => '',
            '(' => '{',
            ')' => '}',
            '*' => '',
            '+' => '',
            ',' => '',
            '-' => '',
            '.' => '',
            '/' => '',
            '0' => '',
            '1' => '',
            '2' => '',
            '3' => '',
            '4' => '',
            '5' => '',
            '6' => '',
            '7' => '',
            '8' => '',
            '9' => '',
            ':' => '',
            ';' => '',
            '<' => '',
            '=' => '',
            '>' => '',
            '?' => '',
            '@' => '',
            'A' => '',
            'B' => '',
            'C' => '',
            'D' => '',
            'E' => '',
            'F' => '',
            'G' => '',
            'H' => '',
            'I' => '',
            'J' => '',
            'K' => '',
            'L' => '',
            'M' => '',
            'N' => '',
            'O' => '',
            'P' => '',
            'Q' => '',
            'R' => '',
            'S' => '',
            'T' => '',
            'U' => '',
            'V' => '',
            'W' => '',
            'X' => '',
            'Y' => '',
            'Z' => '',
            '[' => '',
            '\' => '',
            ']' => '',
            '^' => '',
            '_' => '',
            '`' => '',
            'a' => '',
            'b' => '',
            'c' => '',
            'd' => '',
            'e' => '',
            'f' => '',
            'g' => '',
            'h' => '',
            'i' => '',
            'j' => '',
            'k' => '',
            'l' => '',
            'm' => '',
            'n' => '',
            'o' => '',
            'p' => '',
            'q' => '',
            'r' => '',
            's' => '',
            't' => '',
            'u' => '',
            'v' => '',
            'w' => '',
            'x' => '',
            'y' => '',
            'z' => '',
            '{' => '(',
            '|' => '',
            '}' => ')',
            '~' => '',
        ]) : $text;
    }

    private function expand(string &$trans, string $visibleText): void
    {
        if (1.0 >= $this->expansionFactor) {
            return;
        }

        $visibleLength = $this->strlen($visibleText);
        $missingLength = (int) ceil($visibleLength * $this->expansionFactor) - $visibleLength;
        if ($this->brackets) {
            $missingLength -= 2;
        }

        if (0 >= $missingLength) {
            return;
        }

        $words = [];
        $wordsCount = 0;
        foreach (preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) {
            $wordLength = $this->strlen($word);

            if ($wordLength >= $missingLength) {
                continue;
            }

            if (!isset($words[$wordLength])) {
                $words[$wordLength] = 0;
            }

            ++$words[$wordLength];
            ++$wordsCount;
        }

        if (!$words) {
            $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);

            return;
        }

        arsort($words, \SORT_NUMERIC);

        $longestWordLength = max(array_keys($words));

        while (true) {
            $r = mt_rand(1, $wordsCount);

            foreach ($words as $length => $count) {
                $r -= $count;
                if ($r <= 0) {
                    break;
                }
            }

            $trans .= ' '.str_repeat(self::EXPANSION_CHARACTER, $length);

            $missingLength -= $length + 1;

            if (0 === $missingLength) {
                return;
            }

            while ($longestWordLength >= $missingLength) {
                $wordsCount -= $words[$longestWordLength];
                unset($words[$longestWordLength]);

                if (!$words) {
                    $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);

                    return;
                }

                $longestWordLength = max(array_keys($words));
            }
        }
    }

    private function addBrackets(string &$trans): void
    {
        if (!$this->brackets) {
            return;
        }

        $trans = '['.$trans.']';
    }

    private function strlen(string $s): int
    {
        return false === ($encoding = mb_detect_encoding($s, null, true)) ? \strlen($s) : mb_strlen($s, $encoding);
    }
}
 ?>

Did this file decode correctly?

Original Code

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\Translation;

use Symfony\Contracts\Translation\TranslatorInterface;

/**
 * This translator should only be used in a development environment.
 */
final class PseudoLocalizationTranslator implements TranslatorInterface
{
    private const EXPANSION_CHARACTER = '~';

    private TranslatorInterface $translator;
    private bool $accents;
    private float $expansionFactor;
    private bool $brackets;
    private bool $parseHTML;

    /**
     * @var string[]
     */
    private array $localizableHTMLAttributes;

    /**
     * Available options:
     *  * accents:
     *      type: boolean
     *      default: true
     *      description: replace ASCII characters of the translated string with accented versions or similar characters
     *      example: if true, "foo" => "".
     *
     *  * expansion_factor:
     *      type: float
     *      default: 1
     *      validation: it must be greater than or equal to 1
     *      description: expand the translated string by the given factor with spaces and tildes
     *      example: if 2, "foo" => "~foo ~"
     *
     *  * brackets:
     *      type: boolean
     *      default: true
     *      description: wrap the translated string with brackets
     *      example: if true, "foo" => "[foo]"
     *
     *  * parse_html:
     *      type: boolean
     *      default: false
     *      description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML
     *      warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo <div>bar" => "foo <div>bar</div>"
     *
     *  * localizable_html_attributes:
     *      type: string[]
     *      default: []
     *      description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true
     *      example: if ["title"], and with the "accents" option set to true, "<a href="#" title="Go to your profile">Profile</a>" => "<a href="#" title=""></a>" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged.
     */
    public function __construct(TranslatorInterface $translator, array $options = [])
    {
        $this->translator = $translator;
        $this->accents = $options['accents'] ?? true;

        if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) {
            throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.');
        }

        $this->brackets = $options['brackets'] ?? true;

        $this->parseHTML = $options['parse_html'] ?? false;
        if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) {
            $this->parseHTML = false;
        }

        $this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? [];
    }

    public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
    {
        $trans = '';
        $visibleText = '';

        foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) {
            if ($visible) {
                $visibleText .= $text;
            }

            if (!$localizable) {
                $trans .= $text;

                continue;
            }

            $this->addAccents($trans, $text);
        }

        $this->expand($trans, $visibleText);

        $this->addBrackets($trans);

        return $trans;
    }

    public function getLocale(): string
    {
        return $this->translator->getLocale();
    }

    private function getParts(string $originalTrans): array
    {
        if (!$this->parseHTML) {
            return [[true, true, $originalTrans]];
        }

        $html = mb_encode_numericentity($originalTrans, [0x80, 0x10FFFF, 0, 0x1FFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8');

        $useInternalErrors = libxml_use_internal_errors(true);

        $dom = new \DOMDocument();
        $dom->loadHTML('<trans>'.$html.'</trans>');

        libxml_clear_errors();
        libxml_use_internal_errors($useInternalErrors);

        return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0));
    }

    private function parseNode(\DOMNode $node): array
    {
        $parts = [];

        foreach ($node->childNodes as $childNode) {
            if (!$childNode instanceof \DOMElement) {
                $parts[] = [true, true, $childNode->nodeValue];

                continue;
            }

            $parts[] = [false, false, '<'.$childNode->tagName];

            /** @var \DOMAttr $attribute */
            foreach ($childNode->attributes as $attribute) {
                $parts[] = [false, false, ' '.$attribute->nodeName.'="'];

                $localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, true);
                foreach (preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) {
                    if ('' === $match) {
                        continue;
                    }

                    $parts[] = [false, $localizableAttribute && 0 === $i % 2, $match];
                }

                $parts[] = [false, false, '"'];
            }

            $parts[] = [false, false, '>'];

            $parts = array_merge($parts, $this->parseNode($childNode, $parts));

            $parts[] = [false, false, '</'.$childNode->tagName.'>'];
        }

        return $parts;
    }

    private function addAccents(string &$trans, string $text): void
    {
        $trans .= $this->accents ? strtr($text, [
            ' ' => '',
            '!' => '',
            '"' => '',
            '#' => '',
            '$' => '',
            '%' => '',
            '&' => '',
            '\'' => '',
            '(' => '{',
            ')' => '}',
            '*' => '',
            '+' => '',
            ',' => '',
            '-' => '',
            '.' => '',
            '/' => '',
            '0' => '',
            '1' => '',
            '2' => '',
            '3' => '',
            '4' => '',
            '5' => '',
            '6' => '',
            '7' => '',
            '8' => '',
            '9' => '',
            ':' => '',
            ';' => '',
            '<' => '',
            '=' => '',
            '>' => '',
            '?' => '',
            '@' => '',
            'A' => '',
            'B' => '',
            'C' => '',
            'D' => '',
            'E' => '',
            'F' => '',
            'G' => '',
            'H' => '',
            'I' => '',
            'J' => '',
            'K' => '',
            'L' => '',
            'M' => '',
            'N' => '',
            'O' => '',
            'P' => '',
            'Q' => '',
            'R' => '',
            'S' => '',
            'T' => '',
            'U' => '',
            'V' => '',
            'W' => '',
            'X' => '',
            'Y' => '',
            'Z' => '',
            '[' => '',
            '\\' => '',
            ']' => '',
            '^' => '',
            '_' => '',
            '`' => '',
            'a' => '',
            'b' => '',
            'c' => '',
            'd' => '',
            'e' => '',
            'f' => '',
            'g' => '',
            'h' => '',
            'i' => '',
            'j' => '',
            'k' => '',
            'l' => '',
            'm' => '',
            'n' => '',
            'o' => '',
            'p' => '',
            'q' => '',
            'r' => '',
            's' => '',
            't' => '',
            'u' => '',
            'v' => '',
            'w' => '',
            'x' => '',
            'y' => '',
            'z' => '',
            '{' => '(',
            '|' => '',
            '}' => ')',
            '~' => '',
        ]) : $text;
    }

    private function expand(string &$trans, string $visibleText): void
    {
        if (1.0 >= $this->expansionFactor) {
            return;
        }

        $visibleLength = $this->strlen($visibleText);
        $missingLength = (int) ceil($visibleLength * $this->expansionFactor) - $visibleLength;
        if ($this->brackets) {
            $missingLength -= 2;
        }

        if (0 >= $missingLength) {
            return;
        }

        $words = [];
        $wordsCount = 0;
        foreach (preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) {
            $wordLength = $this->strlen($word);

            if ($wordLength >= $missingLength) {
                continue;
            }

            if (!isset($words[$wordLength])) {
                $words[$wordLength] = 0;
            }

            ++$words[$wordLength];
            ++$wordsCount;
        }

        if (!$words) {
            $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);

            return;
        }

        arsort($words, \SORT_NUMERIC);

        $longestWordLength = max(array_keys($words));

        while (true) {
            $r = mt_rand(1, $wordsCount);

            foreach ($words as $length => $count) {
                $r -= $count;
                if ($r <= 0) {
                    break;
                }
            }

            $trans .= ' '.str_repeat(self::EXPANSION_CHARACTER, $length);

            $missingLength -= $length + 1;

            if (0 === $missingLength) {
                return;
            }

            while ($longestWordLength >= $missingLength) {
                $wordsCount -= $words[$longestWordLength];
                unset($words[$longestWordLength]);

                if (!$words) {
                    $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1);

                    return;
                }

                $longestWordLength = max(array_keys($words));
            }
        }
    }

    private function addBrackets(string &$trans): void
    {
        if (!$this->brackets) {
            return;
        }

        $trans = '['.$trans.']';
    }

    private function strlen(string $s): int
    {
        return false === ($encoding = mb_detect_encoding($s, null, true)) ? \strlen($s) : mb_strlen($s, $encoding);
    }
}

Function Calls

None

Variables

None

Stats

MD5 f6b3289717230e3acf5f168ab3194bef
Eval Count 0
Decode Time 87 ms