vendor/symfony/symfony/src/Symfony/Component/Yaml/Inline.php line 359

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Yaml;
  11. use Symfony\Component\Yaml\Exception\DumpException;
  12. use Symfony\Component\Yaml\Exception\ParseException;
  13. use Symfony\Component\Yaml\Tag\TaggedValue;
  14. /**
  15.  * Inline implements a YAML parser/dumper for the YAML inline syntax.
  16.  *
  17.  * @author Fabien Potencier <fabien@symfony.com>
  18.  *
  19.  * @internal
  20.  */
  21. class Inline
  22. {
  23.     const REGEX_QUOTED_STRING '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
  24.     public static $parsedLineNumber = -1;
  25.     public static $parsedFilename;
  26.     private static $exceptionOnInvalidType false;
  27.     private static $objectSupport false;
  28.     private static $objectForMap false;
  29.     private static $constantSupport false;
  30.     /**
  31.      * @param int         $flags
  32.      * @param int|null    $parsedLineNumber
  33.      * @param string|null $parsedFilename
  34.      */
  35.     public static function initialize($flags$parsedLineNumber null$parsedFilename null)
  36.     {
  37.         self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE $flags);
  38.         self::$objectSupport = (bool) (Yaml::PARSE_OBJECT $flags);
  39.         self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP $flags);
  40.         self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT $flags);
  41.         self::$parsedFilename $parsedFilename;
  42.         if (null !== $parsedLineNumber) {
  43.             self::$parsedLineNumber $parsedLineNumber;
  44.         }
  45.     }
  46.     /**
  47.      * Converts a YAML string to a PHP value.
  48.      *
  49.      * @param string $value      A YAML string
  50.      * @param int    $flags      A bit field of PARSE_* constants to customize the YAML parser behavior
  51.      * @param array  $references Mapping of variable names to values
  52.      *
  53.      * @return mixed A PHP value
  54.      *
  55.      * @throws ParseException
  56.      */
  57.     public static function parse($value$flags 0$references = [])
  58.     {
  59.         if (\is_bool($flags)) {
  60.             @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', \E_USER_DEPRECATED);
  61.             if ($flags) {
  62.                 $flags Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
  63.             } else {
  64.                 $flags 0;
  65.             }
  66.         }
  67.         if (\func_num_args() >= && !\is_array($references)) {
  68.             @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', \E_USER_DEPRECATED);
  69.             if ($references) {
  70.                 $flags |= Yaml::PARSE_OBJECT;
  71.             }
  72.             if (\func_num_args() >= 4) {
  73.                 @trigger_error('Passing a boolean flag to toggle object for map support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', \E_USER_DEPRECATED);
  74.                 if (func_get_arg(3)) {
  75.                     $flags |= Yaml::PARSE_OBJECT_FOR_MAP;
  76.                 }
  77.             }
  78.             if (\func_num_args() >= 5) {
  79.                 $references func_get_arg(4);
  80.             } else {
  81.                 $references = [];
  82.             }
  83.         }
  84.         self::initialize($flags);
  85.         $value trim($value);
  86.         if ('' === $value) {
  87.             return '';
  88.         }
  89.         if (/* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
  90.             $mbEncoding mb_internal_encoding();
  91.             mb_internal_encoding('ASCII');
  92.         }
  93.         try {
  94.             $i 0;
  95.             $tag self::parseTag($value$i$flags);
  96.             switch ($value[$i]) {
  97.                 case '[':
  98.                     $result self::parseSequence($value$flags$i$references);
  99.                     ++$i;
  100.                     break;
  101.                 case '{':
  102.                     $result self::parseMapping($value$flags$i$references);
  103.                     ++$i;
  104.                     break;
  105.                 default:
  106.                     $result self::parseScalar($value$flagsnull$inull === $tag$references);
  107.             }
  108.             // some comments are allowed at the end
  109.             if (preg_replace('/\s*#.*$/A'''substr($value$i))) {
  110.                 throw new ParseException(sprintf('Unexpected characters near "%s".'substr($value$i)), self::$parsedLineNumber 1$valueself::$parsedFilename);
  111.             }
  112.             if (null !== $tag) {
  113.                 return new TaggedValue($tag$result);
  114.             }
  115.             return $result;
  116.         } finally {
  117.             if (isset($mbEncoding)) {
  118.                 mb_internal_encoding($mbEncoding);
  119.             }
  120.         }
  121.     }
  122.     /**
  123.      * Dumps a given PHP variable to a YAML string.
  124.      *
  125.      * @param mixed $value The PHP variable to convert
  126.      * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
  127.      *
  128.      * @return string The YAML string representing the PHP value
  129.      *
  130.      * @throws DumpException When trying to dump PHP resource
  131.      */
  132.     public static function dump($value$flags 0)
  133.     {
  134.         if (\is_bool($flags)) {
  135.             @trigger_error('Passing a boolean flag to toggle exception handling is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE flag instead.', \E_USER_DEPRECATED);
  136.             if ($flags) {
  137.                 $flags Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE;
  138.             } else {
  139.                 $flags 0;
  140.             }
  141.         }
  142.         if (\func_num_args() >= 3) {
  143.             @trigger_error('Passing a boolean flag to toggle object support is deprecated since Symfony 3.1 and will be removed in 4.0. Use the Yaml::DUMP_OBJECT flag instead.', \E_USER_DEPRECATED);
  144.             if (func_get_arg(2)) {
  145.                 $flags |= Yaml::DUMP_OBJECT;
  146.             }
  147.         }
  148.         switch (true) {
  149.             case \is_resource($value):
  150.                 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE $flags) {
  151.                     throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").'get_resource_type($value)));
  152.                 }
  153.                 return 'null';
  154.             case $value instanceof \DateTimeInterface:
  155.                 return $value->format('c');
  156.             case \is_object($value):
  157.                 if ($value instanceof TaggedValue) {
  158.                     return '!'.$value->getTag().' '.self::dump($value->getValue(), $flags);
  159.                 }
  160.                 if (Yaml::DUMP_OBJECT $flags) {
  161.                     return '!php/object '.self::dump(serialize($value));
  162.                 }
  163.                 if (Yaml::DUMP_OBJECT_AS_MAP $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) {
  164.                     return self::dumpArray($value$flags & ~Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE);
  165.                 }
  166.                 if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE $flags) {
  167.                     throw new DumpException('Object support when dumping a YAML file has been disabled.');
  168.                 }
  169.                 return 'null';
  170.             case \is_array($value):
  171.                 return self::dumpArray($value$flags);
  172.             case null === $value:
  173.                 return 'null';
  174.             case true === $value:
  175.                 return 'true';
  176.             case false === $value:
  177.                 return 'false';
  178.             case ctype_digit($value):
  179.                 return \is_string($value) ? "'$value'" : (int) $value;
  180.             case is_numeric($value) && false === strpos($value"\f") && false === strpos($value"\n") && false === strpos($value"\r") && false === strpos($value"\t") && false === strpos($value"\v"):
  181.                 $locale setlocale(\LC_NUMERIC0);
  182.                 if (false !== $locale) {
  183.                     setlocale(\LC_NUMERIC'C');
  184.                 }
  185.                 if (\is_float($value)) {
  186.                     $repr = (string) $value;
  187.                     if (is_infinite($value)) {
  188.                         $repr str_ireplace('INF''.Inf'$repr);
  189.                     } elseif (floor($value) == $value && $repr == $value) {
  190.                         // Preserve float data type since storing a whole number will result in integer value.
  191.                         $repr '!!float '.$repr;
  192.                     }
  193.                 } else {
  194.                     $repr = \is_string($value) ? "'$value'" : (string) $value;
  195.                 }
  196.                 if (false !== $locale) {
  197.                     setlocale(\LC_NUMERIC$locale);
  198.                 }
  199.                 return $repr;
  200.             case '' == $value:
  201.                 return "''";
  202.             case self::isBinaryString($value):
  203.                 return '!!binary '.base64_encode($value);
  204.             case Escaper::requiresDoubleQuoting($value):
  205.                 return Escaper::escapeWithDoubleQuotes($value);
  206.             case Escaper::requiresSingleQuoting($value):
  207.             case Parser::preg_match('{^[0-9]+[_0-9]*$}'$value):
  208.             case Parser::preg_match(self::getHexRegex(), $value):
  209.             case Parser::preg_match(self::getTimestampRegex(), $value):
  210.                 return Escaper::escapeWithSingleQuotes($value);
  211.             default:
  212.                 return $value;
  213.         }
  214.     }
  215.     /**
  216.      * Check if given array is hash or just normal indexed array.
  217.      *
  218.      * @internal
  219.      *
  220.      * @param array|\ArrayObject|\stdClass $value The PHP array or array-like object to check
  221.      *
  222.      * @return bool true if value is hash array, false otherwise
  223.      */
  224.     public static function isHash($value)
  225.     {
  226.         if ($value instanceof \stdClass || $value instanceof \ArrayObject) {
  227.             return true;
  228.         }
  229.         $expectedKey 0;
  230.         foreach ($value as $key => $val) {
  231.             if ($key !== $expectedKey++) {
  232.                 return true;
  233.             }
  234.         }
  235.         return false;
  236.     }
  237.     /**
  238.      * Dumps a PHP array to a YAML string.
  239.      *
  240.      * @param array $value The PHP array to dump
  241.      * @param int   $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
  242.      *
  243.      * @return string The YAML string representing the PHP array
  244.      */
  245.     private static function dumpArray($value$flags)
  246.     {
  247.         // array
  248.         if (($value || Yaml::DUMP_EMPTY_ARRAY_AS_SEQUENCE $flags) && !self::isHash($value)) {
  249.             $output = [];
  250.             foreach ($value as $val) {
  251.                 $output[] = self::dump($val$flags);
  252.             }
  253.             return sprintf('[%s]'implode(', '$output));
  254.         }
  255.         // hash
  256.         $output = [];
  257.         foreach ($value as $key => $val) {
  258.             $output[] = sprintf('%s: %s'self::dump($key$flags), self::dump($val$flags));
  259.         }
  260.         return sprintf('{ %s }'implode(', '$output));
  261.     }
  262.     /**
  263.      * Parses a YAML scalar.
  264.      *
  265.      * @param string   $scalar
  266.      * @param int      $flags
  267.      * @param string[] $delimiters
  268.      * @param int      &$i
  269.      * @param bool     $evaluate
  270.      * @param array    $references
  271.      *
  272.      * @return string
  273.      *
  274.      * @throws ParseException When malformed inline YAML string is parsed
  275.      *
  276.      * @internal
  277.      */
  278.     public static function parseScalar($scalar$flags 0$delimiters null, &$i 0$evaluate true$references = [], $legacyOmittedKeySupport false)
  279.     {
  280.         if (\in_array($scalar[$i], ['"'"'"])) {
  281.             // quoted scalar
  282.             $output self::parseQuotedScalar($scalar$i);
  283.             if (null !== $delimiters) {
  284.                 $tmp ltrim(substr($scalar$i), ' ');
  285.                 if ('' === $tmp) {
  286.                     throw new ParseException(sprintf('Unexpected end of line, expected one of "%s".'implode(''$delimiters)), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  287.                 }
  288.                 if (!\in_array($tmp[0], $delimiters)) {
  289.                     throw new ParseException(sprintf('Unexpected characters (%s).'substr($scalar$i)), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  290.                 }
  291.             }
  292.         } else {
  293.             // "normal" string
  294.             if (!$delimiters) {
  295.                 $output substr($scalar$i);
  296.                 $i += \strlen($output);
  297.                 // remove comments
  298.                 if (Parser::preg_match('/[ \t]+#/'$output$match, \PREG_OFFSET_CAPTURE)) {
  299.                     $output substr($output0$match[0][1]);
  300.                 }
  301.             } elseif (Parser::preg_match('/^(.'.($legacyOmittedKeySupport '+' '*').'?)('.implode('|'$delimiters).')/'substr($scalar$i), $match)) {
  302.                 $output $match[1];
  303.                 $i += \strlen($output);
  304.             } else {
  305.                 throw new ParseException(sprintf('Malformed inline YAML string: "%s".'$scalar), self::$parsedLineNumber 1nullself::$parsedFilename);
  306.             }
  307.             // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
  308.             if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
  309.                 throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.'$output[0]), self::$parsedLineNumber 1$outputself::$parsedFilename);
  310.             }
  311.             if ($output && '%' === $output[0]) {
  312.                 @trigger_error(self::getDeprecationMessage(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.'$output)), \E_USER_DEPRECATED);
  313.             }
  314.             if ($evaluate) {
  315.                 $output self::evaluateScalar($output$flags$references);
  316.             }
  317.         }
  318.         return $output;
  319.     }
  320.     /**
  321.      * Parses a YAML quoted scalar.
  322.      *
  323.      * @param string $scalar
  324.      * @param int    &$i
  325.      *
  326.      * @return string
  327.      *
  328.      * @throws ParseException When malformed inline YAML string is parsed
  329.      */
  330.     private static function parseQuotedScalar($scalar, &$i)
  331.     {
  332.         if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au'substr($scalar$i), $match)) {
  333.             throw new ParseException(sprintf('Malformed inline YAML string: "%s".'substr($scalar$i)), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  334.         }
  335.         $output substr($match[0], 1, \strlen($match[0]) - 2);
  336.         $unescaper = new Unescaper();
  337.         if ('"' == $scalar[$i]) {
  338.             $output $unescaper->unescapeDoubleQuotedString($output);
  339.         } else {
  340.             $output $unescaper->unescapeSingleQuotedString($output);
  341.         }
  342.         $i += \strlen($match[0]);
  343.         return $output;
  344.     }
  345.     /**
  346.      * Parses a YAML sequence.
  347.      *
  348.      * @param string $sequence
  349.      * @param int    $flags
  350.      * @param int    &$i
  351.      * @param array  $references
  352.      *
  353.      * @return array
  354.      *
  355.      * @throws ParseException When malformed inline YAML string is parsed
  356.      */
  357.     private static function parseSequence($sequence$flags, &$i 0$references = [])
  358.     {
  359.         $output = [];
  360.         $len = \strlen($sequence);
  361.         ++$i;
  362.         // [foo, bar, ...]
  363.         while ($i $len) {
  364.             if (']' === $sequence[$i]) {
  365.                 return $output;
  366.             }
  367.             if (',' === $sequence[$i] || ' ' === $sequence[$i]) {
  368.                 ++$i;
  369.                 continue;
  370.             }
  371.             $tag self::parseTag($sequence$i$flags);
  372.             switch ($sequence[$i]) {
  373.                 case '[':
  374.                     // nested sequence
  375.                     $value self::parseSequence($sequence$flags$i$references);
  376.                     break;
  377.                 case '{':
  378.                     // nested mapping
  379.                     $value self::parseMapping($sequence$flags$i$references);
  380.                     break;
  381.                 default:
  382.                     $isQuoted = \in_array($sequence[$i], ['"'"'"]);
  383.                     $value self::parseScalar($sequence$flags, [','']'], $inull === $tag$references);
  384.                     // the value can be an array if a reference has been resolved to an array var
  385.                     if (\is_string($value) && !$isQuoted && false !== strpos($value': ')) {
  386.                         // embedded mapping?
  387.                         try {
  388.                             $pos 0;
  389.                             $value self::parseMapping('{'.$value.'}'$flags$pos$references);
  390.                         } catch (\InvalidArgumentException $e) {
  391.                             // no, it's not
  392.                         }
  393.                     }
  394.                     --$i;
  395.             }
  396.             if (null !== $tag) {
  397.                 $value = new TaggedValue($tag$value);
  398.             }
  399.             $output[] = $value;
  400.             ++$i;
  401.         }
  402.         throw new ParseException(sprintf('Malformed inline YAML string: "%s".'$sequence), self::$parsedLineNumber 1nullself::$parsedFilename);
  403.     }
  404.     /**
  405.      * Parses a YAML mapping.
  406.      *
  407.      * @param string $mapping
  408.      * @param int    $flags
  409.      * @param int    &$i
  410.      * @param array  $references
  411.      *
  412.      * @return array|\stdClass
  413.      *
  414.      * @throws ParseException When malformed inline YAML string is parsed
  415.      */
  416.     private static function parseMapping($mapping$flags, &$i 0$references = [])
  417.     {
  418.         $output = [];
  419.         $len = \strlen($mapping);
  420.         ++$i;
  421.         $allowOverwrite false;
  422.         // {foo: bar, bar:foo, ...}
  423.         while ($i $len) {
  424.             switch ($mapping[$i]) {
  425.                 case ' ':
  426.                 case ',':
  427.                     ++$i;
  428.                     continue 2;
  429.                 case '}':
  430.                     if (self::$objectForMap) {
  431.                         return (object) $output;
  432.                     }
  433.                     return $output;
  434.             }
  435.             // key
  436.             $isKeyQuoted = \in_array($mapping[$i], ['"'"'"], true);
  437.             $key self::parseScalar($mapping$flags, [':'' '], $ifalse, [], true);
  438.             if ('!php/const' === $key) {
  439.                 $key .= self::parseScalar($mapping$flags, [':'' '], $ifalse, [], true);
  440.                 if ('!php/const:' === $key && ':' !== $mapping[$i]) {
  441.                     $key '';
  442.                     --$i;
  443.                 } else {
  444.                     $key self::evaluateScalar($key$flags);
  445.                 }
  446.             }
  447.             if (':' !== $key && false === $i strpos($mapping':'$i)) {
  448.                 break;
  449.             }
  450.             if (':' === $key) {
  451.                 @trigger_error(self::getDeprecationMessage('Omitting the key of a mapping is deprecated and will throw a ParseException in 4.0.'), \E_USER_DEPRECATED);
  452.             }
  453.             if (!$isKeyQuoted) {
  454.                 $evaluatedKey self::evaluateScalar($key$flags$references);
  455.                 if ('' !== $key && $evaluatedKey !== $key && !\is_string($evaluatedKey) && !\is_int($evaluatedKey)) {
  456.                     @trigger_error(self::getDeprecationMessage('Implicit casting of incompatible mapping keys to strings is deprecated since Symfony 3.3 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0. Quote your evaluable mapping keys instead.'), \E_USER_DEPRECATED);
  457.                 }
  458.             }
  459.             if (':' !== $key && !$isKeyQuoted && (!isset($mapping[$i 1]) || !\in_array($mapping[$i 1], [' '',''['']''{''}'], true))) {
  460.                 @trigger_error(self::getDeprecationMessage('Using a colon after an unquoted mapping key that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}") is deprecated since Symfony 3.2 and will throw a ParseException in 4.0.'), \E_USER_DEPRECATED);
  461.             }
  462.             if ('<<' === $key) {
  463.                 $allowOverwrite true;
  464.             }
  465.             while ($i $len) {
  466.                 if (':' === $mapping[$i] || ' ' === $mapping[$i]) {
  467.                     ++$i;
  468.                     continue;
  469.                 }
  470.                 $tag self::parseTag($mapping$i$flags);
  471.                 switch ($mapping[$i]) {
  472.                     case '[':
  473.                         // nested sequence
  474.                         $value self::parseSequence($mapping$flags$i$references);
  475.                         // Spec: Keys MUST be unique; first one wins.
  476.                         // Parser cannot abort this mapping earlier, since lines
  477.                         // are processed sequentially.
  478.                         // But overwriting is allowed when a merge node is used in current block.
  479.                         if ('<<' === $key) {
  480.                             foreach ($value as $parsedValue) {
  481.                                 $output += $parsedValue;
  482.                             }
  483.                         } elseif ($allowOverwrite || !isset($output[$key])) {
  484.                             if (null !== $tag) {
  485.                                 $output[$key] = new TaggedValue($tag$value);
  486.                             } else {
  487.                                 $output[$key] = $value;
  488.                             }
  489.                         } elseif (isset($output[$key])) {
  490.                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'$key)), \E_USER_DEPRECATED);
  491.                         }
  492.                         break;
  493.                     case '{':
  494.                         // nested mapping
  495.                         $value self::parseMapping($mapping$flags$i$references);
  496.                         // Spec: Keys MUST be unique; first one wins.
  497.                         // Parser cannot abort this mapping earlier, since lines
  498.                         // are processed sequentially.
  499.                         // But overwriting is allowed when a merge node is used in current block.
  500.                         if ('<<' === $key) {
  501.                             $output += $value;
  502.                         } elseif ($allowOverwrite || !isset($output[$key])) {
  503.                             if (null !== $tag) {
  504.                                 $output[$key] = new TaggedValue($tag$value);
  505.                             } else {
  506.                                 $output[$key] = $value;
  507.                             }
  508.                         } elseif (isset($output[$key])) {
  509.                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'$key)), \E_USER_DEPRECATED);
  510.                         }
  511.                         break;
  512.                     default:
  513.                         $value self::parseScalar($mapping$flags, [',''}'], $inull === $tag$references);
  514.                         // Spec: Keys MUST be unique; first one wins.
  515.                         // Parser cannot abort this mapping earlier, since lines
  516.                         // are processed sequentially.
  517.                         // But overwriting is allowed when a merge node is used in current block.
  518.                         if ('<<' === $key) {
  519.                             $output += $value;
  520.                         } elseif ($allowOverwrite || !isset($output[$key])) {
  521.                             if (null !== $tag) {
  522.                                 $output[$key] = new TaggedValue($tag$value);
  523.                             } else {
  524.                                 $output[$key] = $value;
  525.                             }
  526.                         } elseif (isset($output[$key])) {
  527.                             @trigger_error(self::getDeprecationMessage(sprintf('Duplicate key "%s" detected whilst parsing YAML. Silent handling of duplicate mapping keys in YAML is deprecated since Symfony 3.2 and will throw \Symfony\Component\Yaml\Exception\ParseException in 4.0.'$key)), \E_USER_DEPRECATED);
  528.                         }
  529.                         --$i;
  530.                 }
  531.                 ++$i;
  532.                 continue 2;
  533.             }
  534.         }
  535.         throw new ParseException(sprintf('Malformed inline YAML string: "%s".'$mapping), self::$parsedLineNumber 1nullself::$parsedFilename);
  536.     }
  537.     /**
  538.      * Evaluates scalars and replaces magic values.
  539.      *
  540.      * @param string $scalar
  541.      * @param int    $flags
  542.      * @param array  $references
  543.      *
  544.      * @return mixed The evaluated YAML string
  545.      *
  546.      * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
  547.      */
  548.     private static function evaluateScalar($scalar$flags$references = [])
  549.     {
  550.         $scalar trim($scalar);
  551.         $scalarLower strtolower($scalar);
  552.         if (=== strpos($scalar'*')) {
  553.             if (false !== $pos strpos($scalar'#')) {
  554.                 $value substr($scalar1$pos 2);
  555.             } else {
  556.                 $value substr($scalar1);
  557.             }
  558.             // an unquoted *
  559.             if (false === $value || '' === $value) {
  560.                 throw new ParseException('A reference must contain at least one character.'self::$parsedLineNumber 1$valueself::$parsedFilename);
  561.             }
  562.             if (!\array_key_exists($value$references)) {
  563.                 throw new ParseException(sprintf('Reference "%s" does not exist.'$value), self::$parsedLineNumber 1$valueself::$parsedFilename);
  564.             }
  565.             return $references[$value];
  566.         }
  567.         switch (true) {
  568.             case 'null' === $scalarLower:
  569.             case '' === $scalar:
  570.             case '~' === $scalar:
  571.                 return null;
  572.             case 'true' === $scalarLower:
  573.                 return true;
  574.             case 'false' === $scalarLower:
  575.                 return false;
  576.             case '!' === $scalar[0]:
  577.                 switch (true) {
  578.                     case === strpos($scalar'!str'):
  579.                         @trigger_error(self::getDeprecationMessage('Support for the !str tag is deprecated since Symfony 3.4. Use the !!str tag instead.'), \E_USER_DEPRECATED);
  580.                         return (string) substr($scalar5);
  581.                     case === strpos($scalar'!!str '):
  582.                         return (string) substr($scalar6);
  583.                     case === strpos($scalar'! '):
  584.                         @trigger_error(self::getDeprecationMessage('Using the non-specific tag "!" is deprecated since Symfony 3.4 as its behavior will change in 4.0. It will force non-evaluating your values in 4.0. Use plain integers or !!float instead.'), \E_USER_DEPRECATED);
  585.                         return (int) self::parseScalar(substr($scalar2), $flags);
  586.                     case === strpos($scalar'!php/object:'):
  587.                         if (self::$objectSupport) {
  588.                             @trigger_error(self::getDeprecationMessage('The !php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), \E_USER_DEPRECATED);
  589.                             return unserialize(substr($scalar12));
  590.                         }
  591.                         if (self::$exceptionOnInvalidType) {
  592.                             throw new ParseException('Object support when parsing a YAML file has been disabled.'self::$parsedLineNumber 1$scalarself::$parsedFilename);
  593.                         }
  594.                         return null;
  595.                     case === strpos($scalar'!!php/object:'):
  596.                         if (self::$objectSupport) {
  597.                             @trigger_error(self::getDeprecationMessage('The !!php/object: tag to indicate dumped PHP objects is deprecated since Symfony 3.1 and will be removed in 4.0. Use the !php/object (without the colon) tag instead.'), \E_USER_DEPRECATED);
  598.                             return unserialize(substr($scalar13));
  599.                         }
  600.                         if (self::$exceptionOnInvalidType) {
  601.                             throw new ParseException('Object support when parsing a YAML file has been disabled.'self::$parsedLineNumber 1$scalarself::$parsedFilename);
  602.                         }
  603.                         return null;
  604.                     case === strpos($scalar'!php/object'):
  605.                         if (self::$objectSupport) {
  606.                             if (!isset($scalar[12])) {
  607.                                 return false;
  608.                             }
  609.                             return unserialize(self::parseScalar(substr($scalar12)));
  610.                         }
  611.                         if (self::$exceptionOnInvalidType) {
  612.                             throw new ParseException('Object support when parsing a YAML file has been disabled.'self::$parsedLineNumber 1$scalarself::$parsedFilename);
  613.                         }
  614.                         return null;
  615.                     case === strpos($scalar'!php/const:'):
  616.                         if (self::$constantSupport) {
  617.                             @trigger_error(self::getDeprecationMessage('The !php/const: tag to indicate dumped PHP constants is deprecated since Symfony 3.4 and will be removed in 4.0. Use the !php/const (without the colon) tag instead.'), \E_USER_DEPRECATED);
  618.                             if (\defined($const substr($scalar11))) {
  619.                                 return \constant($const);
  620.                             }
  621.                             throw new ParseException(sprintf('The constant "%s" is not defined.'$const), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  622.                         }
  623.                         if (self::$exceptionOnInvalidType) {
  624.                             throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?'$scalar), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  625.                         }
  626.                         return null;
  627.                     case === strpos($scalar'!php/const'):
  628.                         if (self::$constantSupport) {
  629.                             if (!isset($scalar[11])) {
  630.                                 return '';
  631.                             }
  632.                             $i 0;
  633.                             if (\defined($const self::parseScalar(substr($scalar11), 0null$ifalse))) {
  634.                                 return \constant($const);
  635.                             }
  636.                             throw new ParseException(sprintf('The constant "%s" is not defined.'$const), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  637.                         }
  638.                         if (self::$exceptionOnInvalidType) {
  639.                             throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?'$scalar), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  640.                         }
  641.                         return null;
  642.                     case === strpos($scalar'!!float '):
  643.                         return (float) substr($scalar8);
  644.                     case === strpos($scalar'!!binary '):
  645.                         return self::evaluateBinaryScalar(substr($scalar9));
  646.                     default:
  647.                         @trigger_error(self::getDeprecationMessage(sprintf('Using the unquoted scalar value "%s" is deprecated since Symfony 3.3 and will be considered as a tagged value in 4.0. You must quote it.'$scalar)), \E_USER_DEPRECATED);
  648.                 }
  649.             // Optimize for returning strings.
  650.             // no break
  651.             case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || is_numeric($scalar[0]):
  652.                 if (Parser::preg_match('{^[+-]?[0-9][0-9_]*$}'$scalar)) {
  653.                     $scalar str_replace('_''', (string) $scalar);
  654.                 }
  655.                 switch (true) {
  656.                     case ctype_digit($scalar):
  657.                         if (preg_match('/^0[0-7]+$/'$scalar)) {
  658.                             return octdec($scalar);
  659.                         }
  660.                         $cast = (int) $scalar;
  661.                         return ($scalar === (string) $cast) ? $cast $scalar;
  662.                     case '-' === $scalar[0] && ctype_digit(substr($scalar1)):
  663.                         if (preg_match('/^-0[0-7]+$/'$scalar)) {
  664.                             return -octdec(substr($scalar1));
  665.                         }
  666.                         $cast = (int) $scalar;
  667.                         return ($scalar === (string) $cast) ? $cast $scalar;
  668.                     case is_numeric($scalar):
  669.                     case Parser::preg_match(self::getHexRegex(), $scalar):
  670.                         $scalar str_replace('_'''$scalar);
  671.                         return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
  672.                     case '.inf' === $scalarLower:
  673.                     case '.nan' === $scalarLower:
  674.                         return -log(0);
  675.                     case '-.inf' === $scalarLower:
  676.                         return log(0);
  677.                     case Parser::preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/'$scalar):
  678.                     case Parser::preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/'$scalar):
  679.                         if (false !== strpos($scalar',')) {
  680.                             @trigger_error(self::getDeprecationMessage('Using the comma as a group separator for floats is deprecated since Symfony 3.2 and will be removed in 4.0.'), \E_USER_DEPRECATED);
  681.                         }
  682.                         return (float) str_replace([',''_'], ''$scalar);
  683.                     case Parser::preg_match(self::getTimestampRegex(), $scalar):
  684.                         if (Yaml::PARSE_DATETIME $flags) {
  685.                             // When no timezone is provided in the parsed date, YAML spec says we must assume UTC.
  686.                             return new \DateTime($scalar, new \DateTimeZone('UTC'));
  687.                         }
  688.                         $timeZone date_default_timezone_get();
  689.                         date_default_timezone_set('UTC');
  690.                         $time strtotime($scalar);
  691.                         date_default_timezone_set($timeZone);
  692.                         return $time;
  693.                 }
  694.         }
  695.         return (string) $scalar;
  696.     }
  697.     /**
  698.      * @param string $value
  699.      * @param int    &$i
  700.      * @param int    $flags
  701.      *
  702.      * @return string|null
  703.      */
  704.     private static function parseTag($value, &$i$flags)
  705.     {
  706.         if ('!' !== $value[$i]) {
  707.             return null;
  708.         }
  709.         $tagLength strcspn($value" \t\n"$i 1);
  710.         $tag substr($value$i 1$tagLength);
  711.         $nextOffset $i $tagLength 1;
  712.         $nextOffset += strspn($value' '$nextOffset);
  713.         // Is followed by a scalar
  714.         if ((!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[''{'], true)) && 'tagged' !== $tag) {
  715.             // Manage non-whitelisted scalars in {@link self::evaluateScalar()}
  716.             return null;
  717.         }
  718.         // Built-in tags
  719.         if ($tag && '!' === $tag[0]) {
  720.             throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.'$tag), self::$parsedLineNumber 1$valueself::$parsedFilename);
  721.         }
  722.         if (Yaml::PARSE_CUSTOM_TAGS $flags) {
  723.             $i $nextOffset;
  724.             return $tag;
  725.         }
  726.         throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".'$tag), self::$parsedLineNumber 1$valueself::$parsedFilename);
  727.     }
  728.     /**
  729.      * @param string $scalar
  730.      *
  731.      * @return string
  732.      *
  733.      * @internal
  734.      */
  735.     public static function evaluateBinaryScalar($scalar)
  736.     {
  737.         $parsedBinaryData self::parseScalar(preg_replace('/\s/'''$scalar));
  738.         if (!== (\strlen($parsedBinaryData) % 4)) {
  739.             throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', \strlen($parsedBinaryData)), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  740.         }
  741.         if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i'$parsedBinaryData)) {
  742.             throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.'$parsedBinaryData), self::$parsedLineNumber 1$scalarself::$parsedFilename);
  743.         }
  744.         return base64_decode($parsedBinaryDatatrue);
  745.     }
  746.     private static function isBinaryString($value)
  747.     {
  748.         return !preg_match('//u'$value) || preg_match('/[^\x00\x07-\x0d\x1B\x20-\xff]/'$value);
  749.     }
  750.     /**
  751.      * Gets a regex that matches a YAML date.
  752.      *
  753.      * @return string The regular expression
  754.      *
  755.      * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
  756.      */
  757.     private static function getTimestampRegex()
  758.     {
  759.         return <<<EOF
  760.         ~^
  761.         (?P<year>[0-9][0-9][0-9][0-9])
  762.         -(?P<month>[0-9][0-9]?)
  763.         -(?P<day>[0-9][0-9]?)
  764.         (?:(?:[Tt]|[ \t]+)
  765.         (?P<hour>[0-9][0-9]?)
  766.         :(?P<minute>[0-9][0-9])
  767.         :(?P<second>[0-9][0-9])
  768.         (?:\.(?P<fraction>[0-9]*))?
  769.         (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  770.         (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  771.         $~x
  772. EOF;
  773.     }
  774.     /**
  775.      * Gets a regex that matches a YAML number in hexadecimal notation.
  776.      *
  777.      * @return string
  778.      */
  779.     private static function getHexRegex()
  780.     {
  781.         return '~^0x[0-9a-f_]++$~i';
  782.     }
  783.     private static function getDeprecationMessage($message)
  784.     {
  785.         $message rtrim($message'.');
  786.         if (null !== self::$parsedFilename) {
  787.             $message .= ' in '.self::$parsedFilename;
  788.         }
  789.         if (-!== self::$parsedLineNumber) {
  790.             $message .= ' on line '.(self::$parsedLineNumber 1);
  791.         }
  792.         return $message.'.';
  793.     }
  794. }