gluon-web-remote

Web remote Administration for big size of gluon routers
git clone git://archive.git.mtrnord.blog/MTRNord/gluon-web-remote.git
Log | Files | Refs | README

Inline.php (19810B)


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