PageRenderTime 123ms CodeModel.GetById 83ms app.highlight 19ms RepoModel.GetById 18ms app.codeStats 0ms

/app/lib/core/Zend/Json/Decoder.php

https://github.com/libis/providencelibiscode
PHP | 581 lines | 404 code | 41 blank | 136 comment | 33 complexity | 1d002ea797a67c95cc6c569be661b313 MD5 | raw file
  1<?php
  2/**
  3 * Zend Framework
  4 *
  5 * LICENSE
  6 *
  7 * This source file is subject to the new BSD license that is bundled
  8 * with this package in the file LICENSE.txt.
  9 * It is also available through the world-wide-web at this URL:
 10 * http://framework.zend.com/license/new-bsd
 11 * If you did not receive a copy of the license and are unable to
 12 * obtain it through the world-wide-web, please send an email
 13 * to license@zend.com so we can send you a copy immediately.
 14 *
 15 * @category   Zend
 16 * @package    Zend_Json
 17 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 18 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 19 * @version    $Id: Decoder.php 24799 2012-05-12 19:27:07Z adamlundrigan $
 20 */
 21
 22/**
 23 * @see Zend_Json
 24 */
 25require_once 'Zend/Json.php';
 26
 27/**
 28 * Decode JSON encoded string to PHP variable constructs
 29 *
 30 * @category   Zend
 31 * @package    Zend_Json
 32 * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
 33 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 34 */
 35class Zend_Json_Decoder
 36{
 37    /**
 38     * Parse tokens used to decode the JSON object. These are not
 39     * for public consumption, they are just used internally to the
 40     * class.
 41     */
 42    const EOF         = 0;
 43    const DATUM        = 1;
 44    const LBRACE    = 2;
 45    const LBRACKET    = 3;
 46    const RBRACE     = 4;
 47    const RBRACKET    = 5;
 48    const COMMA       = 6;
 49    const COLON        = 7;
 50
 51    /**
 52     * Use to maintain a "pointer" to the source being decoded
 53     *
 54     * @var string
 55     */
 56    protected $_source;
 57
 58    /**
 59     * Caches the source length
 60     *
 61     * @var int
 62     */
 63    protected $_sourceLength;
 64
 65    /**
 66     * The offset within the souce being decoded
 67     *
 68     * @var int
 69     *
 70     */
 71    protected $_offset;
 72
 73    /**
 74     * The current token being considered in the parser cycle
 75     *
 76     * @var int
 77     */
 78    protected $_token;
 79
 80    /**
 81     * Flag indicating how objects should be decoded
 82     *
 83     * @var int
 84     * @access protected
 85     */
 86    protected $_decodeType;
 87
 88    /**
 89     * Constructor
 90     *
 91     * @param string $source String source to decode
 92     * @param int $decodeType How objects should be decoded -- see
 93     * {@link Zend_Json::TYPE_ARRAY} and {@link Zend_Json::TYPE_OBJECT} for
 94     * valid values
 95     * @return void
 96     */
 97    protected function __construct($source, $decodeType)
 98    {
 99        // Set defaults
100        $this->_source       = self::decodeUnicodeString($source);
101        $this->_sourceLength = strlen($this->_source);
102        $this->_token        = self::EOF;
103        $this->_offset       = 0;
104
105        // Normalize and set $decodeType
106        if (!in_array($decodeType, array(Zend_Json::TYPE_ARRAY, Zend_Json::TYPE_OBJECT)))
107        {
108            $decodeType = Zend_Json::TYPE_ARRAY;
109        }
110        $this->_decodeType   = $decodeType;
111
112        // Set pointer at first token
113        $this->_getNextToken();
114    }
115
116    /**
117     * Decode a JSON source string
118     *
119     * Decodes a JSON encoded string. The value returned will be one of the
120     * following:
121     *        - integer
122     *        - float
123     *        - boolean
124     *        - null
125     *      - StdClass
126     *      - array
127     *         - array of one or more of the above types
128     *
129     * By default, decoded objects will be returned as associative arrays; to
130     * return a StdClass object instead, pass {@link Zend_Json::TYPE_OBJECT} to
131     * the $objectDecodeType parameter.
132     *
133     * Throws a Zend_Json_Exception if the source string is null.
134     *
135     * @static
136     * @access public
137     * @param string $source String to be decoded
138     * @param int $objectDecodeType How objects should be decoded; should be
139     * either or {@link Zend_Json::TYPE_ARRAY} or
140     * {@link Zend_Json::TYPE_OBJECT}; defaults to TYPE_ARRAY
141     * @return mixed
142     * @throws Zend_Json_Exception
143     */
144    public static function decode($source = null, $objectDecodeType = Zend_Json::TYPE_ARRAY)
145    {
146        if (null === $source) {
147            require_once 'Zend/Json/Exception.php';
148            throw new Zend_Json_Exception('Must specify JSON encoded source for decoding');
149        } elseif (!is_string($source)) {
150            require_once 'Zend/Json/Exception.php';
151            throw new Zend_Json_Exception('Can only decode JSON encoded strings');
152        }
153
154        $decoder = new self($source, $objectDecodeType);
155
156        return $decoder->_decodeValue();
157    }
158
159
160    /**
161     * Recursive driving rountine for supported toplevel tops
162     *
163     * @return mixed
164     */
165    protected function _decodeValue()
166    {
167        switch ($this->_token) {
168            case self::DATUM:
169                $result  = $this->_tokenValue;
170                $this->_getNextToken();
171                return($result);
172                break;
173            case self::LBRACE:
174                return($this->_decodeObject());
175                break;
176            case self::LBRACKET:
177                return($this->_decodeArray());
178                break;
179            default:
180                return null;
181                break;
182        }
183    }
184
185    /**
186     * Decodes an object of the form:
187     *  { "attribute: value, "attribute2" : value,...}
188     *
189     * If Zend_Json_Encoder was used to encode the original object then
190     * a special attribute called __className which specifies a class
191     * name that should wrap the data contained within the encoded source.
192     *
193     * Decodes to either an array or StdClass object, based on the value of
194     * {@link $_decodeType}. If invalid $_decodeType present, returns as an
195     * array.
196     *
197     * @return array|StdClass
198     */
199    protected function _decodeObject()
200    {
201        $members = array();
202        $tok = $this->_getNextToken();
203
204        while ($tok && $tok != self::RBRACE) {
205            if ($tok != self::DATUM || ! is_string($this->_tokenValue)) {
206                require_once 'Zend/Json/Exception.php';
207                throw new Zend_Json_Exception('Missing key in object encoding: ' . $this->_source);
208            }
209
210            $key = $this->_tokenValue;
211            $tok = $this->_getNextToken();
212
213            if ($tok != self::COLON) {
214                require_once 'Zend/Json/Exception.php';
215                throw new Zend_Json_Exception('Missing ":" in object encoding: ' . $this->_source);
216            }
217
218            $tok = $this->_getNextToken();
219            $members[$key] = $this->_decodeValue();
220            $tok = $this->_token;
221
222            if ($tok == self::RBRACE) {
223                break;
224            }
225
226            if ($tok != self::COMMA) {
227                require_once 'Zend/Json/Exception.php';
228                throw new Zend_Json_Exception('Missing "," in object encoding: ' . $this->_source);
229            }
230
231            $tok = $this->_getNextToken();
232        }
233
234        switch ($this->_decodeType) {
235            case Zend_Json::TYPE_OBJECT:
236                // Create new StdClass and populate with $members
237                $result = new StdClass();
238                foreach ($members as $key => $value) {
239                    if ($key === '') {
240                        $key = '_empty_';
241                    }
242                    $result->$key = $value;
243                }
244                break;
245            case Zend_Json::TYPE_ARRAY:
246            default:
247                $result = $members;
248                break;
249        }
250
251        $this->_getNextToken();
252        return $result;
253    }
254
255    /**
256     * Decodes a JSON array format:
257     *    [element, element2,...,elementN]
258     *
259     * @return array
260     */
261    protected function _decodeArray()
262    {
263        $result = array();
264        $starttok = $tok = $this->_getNextToken(); // Move past the '['
265        $index  = 0;
266
267        while ($tok && $tok != self::RBRACKET) {
268            $result[$index++] = $this->_decodeValue();
269
270            $tok = $this->_token;
271
272            if ($tok == self::RBRACKET || !$tok) {
273                break;
274            }
275
276            if ($tok != self::COMMA) {
277                require_once 'Zend/Json/Exception.php';
278                throw new Zend_Json_Exception('Missing "," in array encoding: ' . $this->_source);
279            }
280
281            $tok = $this->_getNextToken();
282        }
283
284        $this->_getNextToken();
285        return($result);
286    }
287
288
289    /**
290     * Removes whitepsace characters from the source input
291     */
292    protected function _eatWhitespace()
293    {
294        if (preg_match(
295                '/([\t\b\f\n\r ])*/s',
296                $this->_source,
297                $matches,
298                PREG_OFFSET_CAPTURE,
299                $this->_offset)
300            && $matches[0][1] == $this->_offset)
301        {
302            $this->_offset += strlen($matches[0][0]);
303        }
304    }
305
306
307    /**
308     * Retrieves the next token from the source stream
309     *
310     * @return int Token constant value specified in class definition
311     */
312    protected function _getNextToken()
313    {
314        $this->_token      = self::EOF;
315        $this->_tokenValue = null;
316        $this->_eatWhitespace();
317
318        if ($this->_offset >= $this->_sourceLength) {
319            return(self::EOF);
320        }
321
322        $str        = $this->_source;
323        $str_length = $this->_sourceLength;
324        $i          = $this->_offset;
325        $start      = $i;
326
327        switch ($str{$i}) {
328            case '{':
329               $this->_token = self::LBRACE;
330               break;
331            case '}':
332                $this->_token = self::RBRACE;
333                break;
334            case '[':
335                $this->_token = self::LBRACKET;
336                break;
337            case ']':
338                $this->_token = self::RBRACKET;
339                break;
340            case ',':
341                $this->_token = self::COMMA;
342                break;
343            case ':':
344                $this->_token = self::COLON;
345                break;
346            case  '"':
347                $result = '';
348                do {
349                    $i++;
350                    if ($i >= $str_length) {
351                        break;
352                    }
353
354                    $chr = $str{$i};
355
356                    if ($chr == '\\') {
357                        $i++;
358                        if ($i >= $str_length) {
359                            break;
360                        }
361                        $chr = $str{$i};
362                        switch ($chr) {
363                            case '"' :
364                                $result .= '"';
365                                break;
366                            case '\\':
367                                $result .= '\\';
368                                break;
369                            case '/' :
370                                $result .= '/';
371                                break;
372                            case 'b' :
373                                $result .= "\x08";
374                                break;
375                            case 'f' :
376                                $result .= "\x0c";
377                                break;
378                            case 'n' :
379                                $result .= "\x0a";
380                                break;
381                            case 'r' :
382                                $result .= "\x0d";
383                                break;
384                            case 't' :
385                                $result .= "\x09";
386                                break;
387                            case '\'' :
388                                $result .= '\'';
389                                break;
390                            default:
391                                require_once 'Zend/Json/Exception.php';
392                                throw new Zend_Json_Exception("Illegal escape "
393                                    .  "sequence '" . $chr . "'");
394                        }
395                    } elseif($chr == '"') {
396                        break;
397                    } else {
398                        $result .= $chr;
399                    }
400                } while ($i < $str_length);
401
402                $this->_token = self::DATUM;
403                //$this->_tokenValue = substr($str, $start + 1, $i - $start - 1);
404                $this->_tokenValue = $result;
405                break;
406            case 't':
407                if (($i+ 3) < $str_length && substr($str, $start, 4) == "true") {
408                    $this->_token = self::DATUM;
409                }
410                $this->_tokenValue = true;
411                $i += 3;
412                break;
413            case 'f':
414                if (($i+ 4) < $str_length && substr($str, $start, 5) == "false") {
415                    $this->_token = self::DATUM;
416                }
417                $this->_tokenValue = false;
418                $i += 4;
419                break;
420            case 'n':
421                if (($i+ 3) < $str_length && substr($str, $start, 4) == "null") {
422                    $this->_token = self::DATUM;
423                }
424                $this->_tokenValue = NULL;
425                $i += 3;
426                break;
427        }
428
429        if ($this->_token != self::EOF) {
430            $this->_offset = $i + 1; // Consume the last token character
431            return($this->_token);
432        }
433
434        $chr = $str{$i};
435        if ($chr == '-' || $chr == '.' || ($chr >= '0' && $chr <= '9')) {
436            if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s',
437                $str, $matches, PREG_OFFSET_CAPTURE, $start) && $matches[0][1] == $start) {
438
439                $datum = $matches[0][0];
440
441                if (is_numeric($datum)) {
442                    if (preg_match('/^0\d+$/', $datum)) {
443                        require_once 'Zend/Json/Exception.php';
444                        throw new Zend_Json_Exception("Octal notation not supported by JSON (value: $datum)");
445                    } else {
446                        $val  = intval($datum);
447                        $fVal = floatval($datum);
448                        $this->_tokenValue = ($val == $fVal ? $val : $fVal);
449                    }
450                } else {
451                    require_once 'Zend/Json/Exception.php';
452                    throw new Zend_Json_Exception("Illegal number format: $datum");
453                }
454
455                $this->_token = self::DATUM;
456                $this->_offset = $start + strlen($datum);
457            }
458        } else {
459            require_once 'Zend/Json/Exception.php';
460            throw new Zend_Json_Exception('Illegal Token');
461        }
462
463        return($this->_token);
464    }
465
466    /**
467     * Decode Unicode Characters from \u0000 ASCII syntax.
468     *
469     * This algorithm was originally developed for the
470     * Solar Framework by Paul M. Jones
471     *
472     * @link   http://solarphp.com/
473     * @link   http://svn.solarphp.com/core/trunk/Solar/Json.php
474     * @param  string $value
475     * @return string
476     */
477    public static function decodeUnicodeString($chrs)
478    {
479        $delim       = substr($chrs, 0, 1);
480        $utf8        = '';
481        $strlen_chrs = strlen($chrs);
482
483        for($i = 0; $i < $strlen_chrs; $i++) {
484
485            $substr_chrs_c_2 = substr($chrs, $i, 2);
486            $ord_chrs_c = ord($chrs[$i]);
487
488            switch (true) {
489                case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $i, 6)):
490                    // single, escaped unicode character
491                    $utf16 = chr(hexdec(substr($chrs, ($i + 2), 2)))
492                           . chr(hexdec(substr($chrs, ($i + 4), 2)));
493                    $utf8 .= self::_utf162utf8($utf16);
494                    $i += 5;
495                    break;
496                case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
497                    $utf8 .= $chrs{$i};
498                    break;
499                case ($ord_chrs_c & 0xE0) == 0xC0:
500                    // characters U-00000080 - U-000007FF, mask 110XXXXX
501                    //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
502                    $utf8 .= substr($chrs, $i, 2);
503                    ++$i;
504                    break;
505                case ($ord_chrs_c & 0xF0) == 0xE0:
506                    // characters U-00000800 - U-0000FFFF, mask 1110XXXX
507                    // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
508                    $utf8 .= substr($chrs, $i, 3);
509                    $i += 2;
510                    break;
511                case ($ord_chrs_c & 0xF8) == 0xF0:
512                    // characters U-00010000 - U-001FFFFF, mask 11110XXX
513                    // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
514                    $utf8 .= substr($chrs, $i, 4);
515                    $i += 3;
516                    break;
517                case ($ord_chrs_c & 0xFC) == 0xF8:
518                    // characters U-00200000 - U-03FFFFFF, mask 111110XX
519                    // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
520                    $utf8 .= substr($chrs, $i, 5);
521                    $i += 4;
522                    break;
523                case ($ord_chrs_c & 0xFE) == 0xFC:
524                    // characters U-04000000 - U-7FFFFFFF, mask 1111110X
525                    // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
526                    $utf8 .= substr($chrs, $i, 6);
527                    $i += 5;
528                    break;
529            }
530        }
531
532        return $utf8;
533    }
534
535    /**
536     * Convert a string from one UTF-16 char to one UTF-8 char.
537     *
538     * Normally should be handled by mb_convert_encoding, but
539     * provides a slower PHP-only method for installations
540     * that lack the multibye string extension.
541     *
542     * This method is from the Solar Framework by Paul M. Jones
543     *
544     * @link   http://solarphp.com
545     * @param  string $utf16 UTF-16 character
546     * @return string UTF-8 character
547     */
548    protected static function _utf162utf8($utf16)
549    {
550        // Check for mb extension otherwise do by hand.
551        if( function_exists('mb_convert_encoding') ) {
552            return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
553        }
554
555        $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
556
557        switch (true) {
558            case ((0x7F & $bytes) == $bytes):
559                // this case should never be reached, because we are in ASCII range
560                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
561                return chr(0x7F & $bytes);
562
563            case (0x07FF & $bytes) == $bytes:
564                // return a 2-byte UTF-8 character
565                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
566                return chr(0xC0 | (($bytes >> 6) & 0x1F))
567                     . chr(0x80 | ($bytes & 0x3F));
568
569            case (0xFFFF & $bytes) == $bytes:
570                // return a 3-byte UTF-8 character
571                // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
572                return chr(0xE0 | (($bytes >> 12) & 0x0F))
573                     . chr(0x80 | (($bytes >> 6) & 0x3F))
574                     . chr(0x80 | ($bytes & 0x3F));
575        }
576
577        // ignoring UTF-32 for now, sorry
578        return '';
579    }
580}
581