"Fossies" - the Fresh Open Source Software Archive

Member "grav/vendor/rockettheme/toolbox/Compat/src/Yaml/Inline.php" (1 Sep 2020, 17941 Bytes) of package /linux/www/grav-v1.6.27.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Inline.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 /*
    4  * This file is part of the Symfony package.
    5  *
    6  * (c) Fabien Potencier <fabien@symfony.com>
    7  *
    8  * For the full copyright and license information, please view the LICENSE
    9  * file that was distributed with this source code.
   10  */
   11 
   12 namespace RocketTheme\Toolbox\Compat\Yaml;
   13 
   14 use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
   15 
   16 /**
   17  * Inline implements a YAML parser/dumper for the YAML inline syntax.
   18  *
   19  * @author Fabien Potencier <fabien@symfony.com>
   20  */
   21 class Inline
   22 {
   23     const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
   24 
   25     private static $exceptionOnInvalidType = false;
   26     private static $objectSupport = false;
   27     private static $objectForMap = false;
   28 
   29     /**
   30      * Converts a YAML string to a PHP value.
   31      *
   32      * @param string $value                  A YAML string
   33      * @param bool   $exceptionOnInvalidType True if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
   34      * @param bool   $objectSupport          True if object support is enabled, false otherwise
   35      * @param bool   $objectForMap           True if maps should return a stdClass instead of array()
   36      * @param array  $references             Mapping of variable names to values
   37      *
   38      * @return mixed A PHP value
   39      *
   40      * @throws ParseException
   41      */
   42     public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array())
   43     {
   44         self::$exceptionOnInvalidType = $exceptionOnInvalidType;
   45         self::$objectSupport = $objectSupport;
   46         self::$objectForMap = $objectForMap;
   47 
   48         $value = trim($value);
   49 
   50         if ('' === $value) {
   51             return '';
   52         }
   53 
   54         if (2 /* MB_OVERLOAD_STRING */ & (int) ini_get('mbstring.func_overload')) {
   55             $mbEncoding = mb_internal_encoding();
   56             mb_internal_encoding('ASCII');
   57         }
   58 
   59         $i = 0;
   60         switch ($value[0]) {
   61             case '[':
   62                 $result = self::parseSequence($value, $i, $references);
   63                 ++$i;
   64                 break;
   65             case '{':
   66                 $result = self::parseMapping($value, $i, $references);
   67                 ++$i;
   68                 break;
   69             default:
   70                 $result = self::parseScalar($value, null, array('"', "'"), $i, true, $references);
   71         }
   72 
   73         // some comments are allowed at the end
   74         if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
   75             throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
   76         }
   77 
   78         if (isset($mbEncoding)) {
   79             mb_internal_encoding($mbEncoding);
   80         }
   81 
   82         return $result;
   83     }
   84 
   85     /**
   86      * Check if given array is hash or just normal indexed array.
   87      *
   88      * @internal
   89      *
   90      * @param array $value The PHP array to check
   91      *
   92      * @return bool true if value is hash array, false otherwise
   93      */
   94     public static function isHash(array $value)
   95     {
   96         $expectedKey = 0;
   97 
   98         foreach ($value as $key => $val) {
   99             if ($key !== $expectedKey++) {
  100                 return true;
  101             }
  102         }
  103 
  104         return false;
  105     }
  106 
  107     /**
  108      * Parses a YAML scalar.
  109      *
  110      * @param string   $scalar
  111      * @param string[] $delimiters
  112      * @param string[] $stringDelimiters
  113      * @param int      &$i
  114      * @param bool     $evaluate
  115      * @param array    $references
  116      *
  117      * @return string
  118      *
  119      * @throws ParseException When malformed inline YAML string is parsed
  120      *
  121      * @internal
  122      */
  123     public static function parseScalar($scalar, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
  124     {
  125         if (in_array($scalar[$i], $stringDelimiters)) {
  126             // quoted scalar
  127             $output = self::parseQuotedScalar($scalar, $i);
  128 
  129             if (null !== $delimiters) {
  130                 $tmp = ltrim(substr($scalar, $i), ' ');
  131                 if (!in_array($tmp[0], $delimiters)) {
  132                     throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
  133                 }
  134             }
  135         } else {
  136             // "normal" string
  137             if (!$delimiters) {
  138                 $output = substr($scalar, $i);
  139                 $i += strlen($output);
  140 
  141                 // remove comments
  142                 if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
  143                     $output = substr($output, 0, $match[0][1]);
  144                 }
  145             } elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
  146                 $output = $match[1];
  147                 $i += strlen($output);
  148             } else {
  149                 throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
  150             }
  151 
  152             // a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
  153             if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
  154                 @trigger_error(sprintf('Not quoting the scalar "%s" starting with "%s" is deprecated since Symfony 2.8 and will throw a ParseException in 3.0.', $output, $output[0]), E_USER_DEPRECATED);
  155 
  156                 // to be thrown in 3.0
  157                 // throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
  158             }
  159 
  160             if ($evaluate) {
  161                 $output = self::evaluateScalar($output, $references);
  162             }
  163         }
  164 
  165         return $output;
  166     }
  167 
  168     /**
  169      * Parses a YAML quoted scalar.
  170      *
  171      * @param string $scalar
  172      * @param int    &$i
  173      *
  174      * @return string
  175      *
  176      * @throws ParseException When malformed inline YAML string is parsed
  177      */
  178     private static function parseQuotedScalar($scalar, &$i)
  179     {
  180         if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
  181             throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
  182         }
  183 
  184         $output = substr($match[0], 1, strlen($match[0]) - 2);
  185 
  186         $unescaper = new Unescaper();
  187         if ('"' == $scalar[$i]) {
  188             $output = $unescaper->unescapeDoubleQuotedString($output);
  189         } else {
  190             $output = $unescaper->unescapeSingleQuotedString($output);
  191         }
  192 
  193         $i += strlen($match[0]);
  194 
  195         return $output;
  196     }
  197 
  198     /**
  199      * Parses a YAML sequence.
  200      *
  201      * @param string $sequence
  202      * @param int    &$i
  203      * @param array  $references
  204      *
  205      * @return array
  206      *
  207      * @throws ParseException When malformed inline YAML string is parsed
  208      */
  209     private static function parseSequence($sequence, &$i = 0, $references = array())
  210     {
  211         $output = array();
  212         $len = strlen($sequence);
  213         ++$i;
  214 
  215         // [foo, bar, ...]
  216         while ($i < $len) {
  217             switch ($sequence[$i]) {
  218                 case '[':
  219                     // nested sequence
  220                     $output[] = self::parseSequence($sequence, $i, $references);
  221                     break;
  222                 case '{':
  223                     // nested mapping
  224                     $output[] = self::parseMapping($sequence, $i, $references);
  225                     break;
  226                 case ']':
  227                     return $output;
  228                 case ',':
  229                 case ' ':
  230                     break;
  231                 default:
  232                     $isQuoted = in_array($sequence[$i], array('"', "'"));
  233                     $value = self::parseScalar($sequence, array(',', ']'), array('"', "'"), $i, true, $references);
  234 
  235                     // the value can be an array if a reference has been resolved to an array var
  236                     if (!is_array($value) && !$isQuoted && false !== strpos($value, ': ')) {
  237                         // embedded mapping?
  238                         try {
  239                             $pos = 0;
  240                             $value = self::parseMapping('{'.$value.'}', $pos, $references);
  241                         } catch (\InvalidArgumentException $e) {
  242                             // no, it's not
  243                         }
  244                     }
  245 
  246                     $output[] = $value;
  247 
  248                     --$i;
  249             }
  250 
  251             ++$i;
  252         }
  253 
  254         throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
  255     }
  256 
  257     /**
  258      * Parses a YAML mapping.
  259      *
  260      * @param string $mapping
  261      * @param int    &$i
  262      * @param array  $references
  263      *
  264      * @return array|\stdClass
  265      *
  266      * @throws ParseException When malformed inline YAML string is parsed
  267      */
  268     private static function parseMapping($mapping, &$i = 0, $references = array())
  269     {
  270         $output = array();
  271         $len = strlen($mapping);
  272         ++$i;
  273         $allowOverwrite = false;
  274 
  275         // {foo: bar, bar:foo, ...}
  276         while ($i < $len) {
  277             switch ($mapping[$i]) {
  278                 case ' ':
  279                 case ',':
  280                     ++$i;
  281                     continue 2;
  282                 case '}':
  283                     if (self::$objectForMap) {
  284                         return (object) $output;
  285                     }
  286 
  287                     return $output;
  288             }
  289 
  290             // key
  291             $key = self::parseScalar($mapping, array(':', ' '), array('"', "'"), $i, false);
  292 
  293             if ('<<' === $key) {
  294                 $allowOverwrite = true;
  295             }
  296 
  297             // value
  298             $done = false;
  299 
  300             while ($i < $len) {
  301                 switch ($mapping[$i]) {
  302                     case '[':
  303                         // nested sequence
  304                         $value = self::parseSequence($mapping, $i, $references);
  305                         // Spec: Keys MUST be unique; first one wins.
  306                         // Parser cannot abort this mapping earlier, since lines
  307                         // are processed sequentially.
  308                         // But overwriting is allowed when a merge node is used in current block.
  309                         if ('<<' === $key) {
  310                             foreach ($value as $parsedValue) {
  311                                 $output += $parsedValue;
  312                             }
  313                         } elseif ($allowOverwrite || !isset($output[$key])) {
  314                             $output[$key] = $value;
  315                         }
  316                         $done = true;
  317                         break;
  318                     case '{':
  319                         // nested mapping
  320                         $value = self::parseMapping($mapping, $i, $references);
  321                         // Spec: Keys MUST be unique; first one wins.
  322                         // Parser cannot abort this mapping earlier, since lines
  323                         // are processed sequentially.
  324                         // But overwriting is allowed when a merge node is used in current block.
  325                         if ('<<' === $key) {
  326                             $output += $value;
  327                         } elseif ($allowOverwrite || !isset($output[$key])) {
  328                             $output[$key] = $value;
  329                         }
  330                         $done = true;
  331                         break;
  332                     case ':':
  333                     case ' ':
  334                         break;
  335                     default:
  336                         $value = self::parseScalar($mapping, array(',', '}'), array('"', "'"), $i, true, $references);
  337                         // Spec: Keys MUST be unique; first one wins.
  338                         // Parser cannot abort this mapping earlier, since lines
  339                         // are processed sequentially.
  340                         // But overwriting is allowed when a merge node is used in current block.
  341                         if ('<<' === $key) {
  342                             $output += $value;
  343                         } elseif ($allowOverwrite || !isset($output[$key])) {
  344                             $output[$key] = $value;
  345                         }
  346                         $done = true;
  347                         --$i;
  348                 }
  349 
  350                 ++$i;
  351 
  352                 if ($done) {
  353                     continue 2;
  354                 }
  355             }
  356         }
  357 
  358         throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
  359     }
  360 
  361     /**
  362      * Evaluates scalars and replaces magic values.
  363      *
  364      * @param string $scalar
  365      * @param array  $references
  366      *
  367      * @return mixed The evaluated YAML string
  368      *
  369      * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved
  370      */
  371     private static function evaluateScalar($scalar, $references = array())
  372     {
  373         $scalar = trim($scalar);
  374         $scalarLower = strtolower($scalar);
  375 
  376         if (0 === strpos($scalar, '*')) {
  377             if (false !== $pos = strpos($scalar, '#')) {
  378                 $value = substr($scalar, 1, $pos - 2);
  379             } else {
  380                 $value = substr($scalar, 1);
  381             }
  382 
  383             // an unquoted *
  384             if (false === $value || '' === $value) {
  385                 throw new ParseException('A reference must contain at least one character.');
  386             }
  387 
  388             if (!array_key_exists($value, $references)) {
  389                 throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
  390             }
  391 
  392             return $references[$value];
  393         }
  394 
  395         switch (true) {
  396             case 'null' === $scalarLower:
  397             case '' === $scalar:
  398             case '~' === $scalar:
  399                 return;
  400             case 'true' === $scalarLower:
  401                 return true;
  402             case 'false' === $scalarLower:
  403                 return false;
  404             // Optimise for returning strings.
  405             case '+' === $scalar[0] || '-' === $scalar[0] || '.' === $scalar[0] || '!' === $scalar[0] || is_numeric($scalar[0]):
  406                 switch (true) {
  407                     case 0 === strpos($scalar, '!str'):
  408                         return (string) substr($scalar, 5);
  409                     case 0 === strpos($scalar, '! '):
  410                         return (int) self::parseScalar(substr($scalar, 2));
  411                     case 0 === strpos($scalar, '!php/object:'):
  412                         if (self::$objectSupport) {
  413                             return unserialize(substr($scalar, 12));
  414                         }
  415 
  416                         if (self::$exceptionOnInvalidType) {
  417                             throw new ParseException('Object support when parsing a YAML file has been disabled.');
  418                         }
  419 
  420                         return;
  421                     case 0 === strpos($scalar, '!!php/object:'):
  422                         if (self::$objectSupport) {
  423                             return unserialize(substr($scalar, 13));
  424                         }
  425 
  426                         if (self::$exceptionOnInvalidType) {
  427                             throw new ParseException('Object support when parsing a YAML file has been disabled.');
  428                         }
  429 
  430                         return;
  431                     case 0 === strpos($scalar, '!!float '):
  432                         return (float) substr($scalar, 8);
  433                     case ctype_digit($scalar):
  434                         $raw = $scalar;
  435                         $cast = (int) $scalar;
  436 
  437                         return '0' == $scalar[0] ? octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
  438                     case '-' === $scalar[0] && ctype_digit(substr($scalar, 1)):
  439                         $raw = $scalar;
  440                         $cast = (int) $scalar;
  441 
  442                         return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
  443                     case is_numeric($scalar):
  444                     case Parser::preg_match(self::getHexRegex(), $scalar):
  445                         return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
  446                     case '.inf' === $scalarLower:
  447                     case '.nan' === $scalarLower:
  448                         return -log(0);
  449                     case '-.inf' === $scalarLower:
  450                         return log(0);
  451                     case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
  452                         return (float) str_replace(',', '', $scalar);
  453                     case Parser::preg_match(self::getTimestampRegex(), $scalar):
  454                         $timeZone = date_default_timezone_get();
  455                         date_default_timezone_set('UTC');
  456                         $time = strtotime($scalar);
  457                         date_default_timezone_set($timeZone);
  458 
  459                         return $time;
  460                 }
  461                 // no break
  462             default:
  463                 return (string) $scalar;
  464         }
  465     }
  466 
  467     /**
  468      * Gets a regex that matches a YAML date.
  469      *
  470      * @return string The regular expression
  471      *
  472      * @see http://www.yaml.org/spec/1.2/spec.html#id2761573
  473      */
  474     private static function getTimestampRegex()
  475     {
  476         return <<<EOF
  477         ~^
  478         (?P<year>[0-9][0-9][0-9][0-9])
  479         -(?P<month>[0-9][0-9]?)
  480         -(?P<day>[0-9][0-9]?)
  481         (?:(?:[Tt]|[ \t]+)
  482         (?P<hour>[0-9][0-9]?)
  483         :(?P<minute>[0-9][0-9])
  484         :(?P<second>[0-9][0-9])
  485         (?:\.(?P<fraction>[0-9]*))?
  486         (?:[ \t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
  487         (?::(?P<tz_minute>[0-9][0-9]))?))?)?
  488         $~x
  489 EOF;
  490     }
  491 
  492     /**
  493      * Gets a regex that matches a YAML number in hexadecimal notation.
  494      *
  495      * @return string
  496      */
  497     private static function getHexRegex()
  498     {
  499         return '~^0x[0-9a-f]++$~i';
  500     }
  501 }