Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Json/Checker.php
<?php
/**
 *
 * JSON validation class to determine syntactic correctness prior to decoding.
 *  
 * A port of JSON_checker.c designed to quickly scan a block of JSON text
 * and determine if it is syntactically correct.
 * 
 * Learn more about Pushdown automatons at
 * <http://en.wikipedia.org/wiki/Pushdown_automaton>
 * 
 * @category Solar
 * 
 * @package Solar_Json
 * 
 * @author Douglas Crockford <hide@address.com>
 * 
 * @author Clay Loveless <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @copyright Copyright (c) 2005 JSON.org
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * The Software shall be used for Good, not Evil.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * 
 * @version $Id: Checker.php 3988 2009-09-04 13:51:51Z pmjones $
 * 
 */
class Solar_Json_Checker extends Solar_Base
{
    /**
     * Error
     * @constant
     */
    const S_ERR = -1;
    
    /**
     * Space
     * @constant
     */
    const S_SPA = 0;
    
    /**
     * Other whitespace
     * @constant
     */
    const S_WSP = 1;
    
    /**
     * {
     * @constant
     */
    const S_LBE = 2;
    
    /**
     * }
     * @constant
     */
    const S_RBE = 3;
    
    /**
     * [
     * @constant
     */
    const S_LBT = 4;
    
    /**
     * ]
     * @constant
     */
    const S_RBT = 5;
    
    /**
     * :
     * @constant
     */
    const S_COL = 6;
    
    /**
     * ,
     * @constant
     */
    const S_COM = 7;
    
    /**
     * "
     * @constant
     */
    const S_QUO = 8;
    
    /**
     * \
     * @constant
     */
    const S_BAC = 9;
    
    /**
     * /
     * @constant
     */
    const S_SLA = 10;
    
    /**
     * +
     * @constant
     */
    const S_PLU = 11;
    
    /**
     * -
     * @constant
     */
    const S_MIN = 12;
    
    /**
     * .
     * @constant
     */
    const S_DOT = 13;
    
    /**
     * 0
     * @constant
     */
    const S_ZER = 14;
    
    /**
     * 123456789
     * @constant
     */
    const S_DIG = 15;
    
    /**
     * a
     * @constant
     */
    const S__A_ = 16;
    
    /**
     * b
     * @constant
     */
    const S__B_ = 17;
    
    /**
     * c
     * @constant
     */
    const S__C_ = 18;
    
    /**
     * d
     * @constant
     */
    const S__D_ = 19;
    
    /**
     * e
     * @constant
     */
    const S__E_ = 20;
    
    /**
     * f
     * @constant
     */
    const S__F_ = 21;
    
    /**
     * l
     * @constant
     */
    const S__L_ = 22;
    
    /**
     * n
     * @constant
     */
    const S__N_ = 23;
    
    /**
     * r
     * @constant
     */
    const S__R_ = 24;
    
    /**
     * s
     * @constant
     */
    const S__S_ = 25;
    
    /**
     * t
     * @constant
     */
    const S__T_ = 26;
    
    /**
     * u
     * @constant
     */
    const S__U_ = 27;
    
    /**
     * ABCDF
     * @constant
     */
    const S_A_F = 28;
    
    /**
     * E
     * @constant
     */
    const S_E = 29;
    
    /**
     * Everything else
     * @constant
     */
    const S_ETC = 30;
    
    /**
     * 
     * Map of 128 ASCII characters into the 32 character classes.
     * 
     * The remaining Unicode characters should be mapped to S_ETC.
     * 
     * @var array
     * 
     */
    protected $_ascii_class = array();
    
    /**
     * 
     * State transition table.
     * 
     * @var array
     * 
     */
    protected $_state_transition_table = array();
    
    /**
     * 
     * These modes can be pushed on the "pushdown automata" (PDA) stack.
     * 
     * @constant
     * 
     */
    const MODE_DONE     = 1;
    const MODE_KEY      = 2;
    const MODE_OBJECT   = 3;
    const MODE_ARRAY    = 4;
    
    /**
     * 
     * Max depth allowed for nested structures.
     * 
     * @constant
     * 
     */
    const MAX_DEPTH = 20;
    
    /**
     * 
     * The stack to maintain the state of nested structures.
     * 
     * @var array
     * 
     */
    protected $_the_stack = array();
    
    /**
     * 
     * Pointer for the top of the stack.
     * 
     * @var int
     * 
     */
    protected $_the_top;
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        parent::_postConstruct();
        $this->_mapAscii();
        $this->_setStateTransitionTable();
    }
    
    /**
     * 
     * The isValid method takes a UTF-16 encoded string and determines if it is
     * a syntactically correct JSON text.
     * 
     * It is implemented as a Pushdown Automaton; that means it is a finite
     * state machine with a stack.
     * 
     * @param string $str The JSON text to validate
     * 
     * @return bool
     * 
     */
    public function isValid($str)
    {
        // string length
        $len = strlen($str);
        // the next character
        //$b = 0;
        // the next character class
        //$c = 0;
        // the next state
        //$s = 0;
        
        $_the_state = 0;
        $this->_the_top = -1;
        $this->_push(self::MODE_DONE);
        
        for ($_the_index = 0; $_the_index < $len; $_the_index++) {
            $b = $str{$_the_index};
            if (chr(ord($b) & 127) == $b) {
                $c = $this->_ascii_class[ord($b)];
                if ($c <= self::S_ERR) {
                    return false;
                }
            } else {
                $c = self::S_ETC;
            }
            
            // Get the next state from the transition table
            $s = $this->_state_transition_table[$_the_state][$c];
            
            if ($s < 0) {
                // Perform one of the predefined actions
                
                switch($s) {
                    
                    // empty }
                    case -9:
                        if (!$this->_pop(self::MODE_KEY)) {
                            return false;
                        }
                        $_the_state = 9;
                        break;
                    
                    // {
                    case -8:
                        if (!$this->_push(self::MODE_KEY)) {
                            return false;
                        }
                        $_the_state = 1;
                        break;
                    
                    // }
                    case -7:
                        if (!$this->_pop(self::MODE_OBJECT)) {
                            return false;
                        }
                        $_the_state = 9;
                        break;
                    
                    // [
                    case -6:
                        if (!$this->_push(self::MODE_ARRAY)) {
                            return false;
                        }
                        $_the_state = 2;
                        break;
                    
                    // ]
                    case -5:
                        if (!$this->_pop(self::MODE_ARRAY)) {
                            return false;
                        }
                        $_the_state = 9;
                        break;
                    
                    // "
                    case -4:
                        switch($this->_the_stack[$this->_the_top]) {
                            case self::MODE_KEY:
                                $_the_state = 27;
                                break;
                            case self::MODE_ARRAY:
                            case self::MODE_OBJECT:
                                $_the_state = 9;
                                break;
                            default:
                                return false;
                        }
                        break;
                    
                    // '
                    case -3:
                        switch($this->_the_stack[$this->_the_top]) {
                            case self::MODE_OBJECT:
                                if ($this->_pop(self::MODE_OBJECT) && $this->_push(self::MODE_KEY)) {
                                    $_the_state = 29;
                                }
                                break;
                            case self::MODE_ARRAY:
                                $_the_state = 28;
                                break;
                            default:
                                return false;
                        }
                        break;
                    
                    // :
                    case -2:
                        if ($this->_pop(self::MODE_KEY) && $this->_push(self::MODE_OBJECT)) {
                            $_the_state = 28;
                            break;
                        }
                    
                    // syntax error
                    case -1:
                        return false;
                }
            } else {
                
                // change the state and iterate
                $_the_state = $s;
            
            }
        
        }
        
        if ($_the_state == 9 && $this->_pop(self::MODE_DONE)) {
            return true;
        }
        return false;
    }
    
    /**
     * 
     * Map the 128 ASCII characters into the 32 character classes.
     * The remaining Unicode characters should be mapped to S_ETC.
     * 
     * @return void
     * 
     */
    protected function _mapAscii()
    {
        $this->_ascii_class = array(
            self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR,
            self::S_ERR, self::S_WSP, self::S_WSP, self::S_ERR, self::S_ERR, self::S_WSP, self::S_ERR, self::S_ERR,
            self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR,
            self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR, self::S_ERR,
            
            self::S_SPA, self::S_ETC, self::S_QUO, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_PLU, self::S_COM, self::S_MIN, self::S_DOT, self::S_SLA,
            self::S_ZER, self::S_DIG, self::S_DIG, self::S_DIG, self::S_DIG, self::S_DIG, self::S_DIG, self::S_DIG,
            self::S_DIG, self::S_DIG, self::S_COL, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC,
            
            self::S_ETC, self::S_A_F, self::S_A_F, self::S_A_F, self::S_A_F, self::S_E  , self::S_A_F, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_LBT, self::S_BAC, self::S_RBT, self::S_ETC, self::S_ETC,
            
            self::S_ETC, self::S__A_, self::S__B_, self::S__C_, self::S__D_, self::S__E_, self::S__F_, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_ETC, self::S__L_, self::S_ETC, self::S__N_, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S__R_, self::S__S_, self::S__T_, self::S__U_, self::S_ETC, self::S_ETC,
            self::S_ETC, self::S_ETC, self::S_ETC, self::S_LBE, self::S_ETC, self::S_RBE, self::S_ETC, self::S_ETC
        );
    }
    
    /**
     * 
     * The state transition table takes the current state and the current symbol,
     * and returns either a new state or an action. A new state is a number between
     * 0 and 29. An action is a negative number between -1 and -9. A JSON text is
     * accepted if the end of the text is in state 9 and mode is MODE_DONE.
     * 
     * @return void;
     * 
     */
    protected function _setStateTransitionTable()
    {
        $this->_state_transition_table = array(
            array( 0, 0,-8,-1,-6,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array( 1, 1,-1,-9,-1,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array( 2, 2,-8,-1,-6,-5,-1,-1, 3,-1,-1,-1,20,-1,21,22,-1,-1,-1,-1,-1,13,-1,17,-1,-1,10,-1,-1,-1,-1),
            array( 3,-1, 3, 3, 3, 3, 3, 3,-4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
            array(-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3,-1,-1,-1,-1,-1,-1, 3,-1,-1,-1, 3,-1, 3, 3,-1, 3, 5,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 6, 6, 6, 6, 6, 6, 6, 6,-1,-1,-1,-1,-1,-1, 6, 6,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 7, 7, 7, 7, 7, 7, 7, 7,-1,-1,-1,-1,-1,-1, 7, 7,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 8, 8, 8, 8, 8, 8, 8, 8,-1,-1,-1,-1,-1,-1, 8, 8,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 3, 3, 3, 3, 3, 3, 3, 3,-1,-1,-1,-1,-1,-1, 3, 3,-1),
            array( 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,11,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,12,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,15,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,16,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,18,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,19,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 9,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,21,22,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array( 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,23,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array( 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,23,22,22,-1,-1,-1,-1,24,-1,-1,-1,-1,-1,-1,-1,-1,24,-1),
            array( 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,23,23,-1,-1,-1,-1,24,-1,-1,-1,-1,-1,-1,-1,-1,24,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,25,25,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array( 9, 9,-1,-7,-1,-5,-1,-3,-1,-1,-1,-1,-1,-1,26,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(27,27,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1),
            array(28,28,-8,-1,-6,-1,-1,-1, 3,-1,-1,-1,20,-1,21,22,-1,-1,-1,-1,-1,13,-1,17,-1,-1,10,-1,-1,-1,-1),
            array(29,29,-1,-1,-1,-1,-1,-1, 3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1)
        );
    }
    
    /**
     * 
     * Push a mode onto the stack. Return false if there is overflow.
     * 
     * @param int $mode Mode to push onto the stack
     * 
     * @return bool Success/failure of stack push
     * 
     */
    protected function _push($mode)
    {
        ++$this->_the_top;
        if ($this->_the_top >= self::MAX_DEPTH) {
            return false;
        }
        $this->_the_stack[$this->_the_top] = $mode;
        return true;
    }
    
    /**
     * 
     * Pop the stack, assuring that the current mode matches the expectation.
     * Return false if there is underflow or if the modes mismatch.
     * 
     * @param int $mode Mode to pop from the stack
     * 
     * @return bool Success/failure of stack pop
     * 
     */
    protected function _pop($mode)
    {
        if ($this->_the_top < 0 || $this->_the_stack[$this->_the_top] != $mode) {
            return false;
        }
        $this->_the_stack[$this->_the_top] = 0;
        --$this->_the_top;
        return true;
    }
}
Return current item: SolarPHP