Parsing Javascript (not JSON) in PHP

后端 未结 5 1511
[愿得一人]
[愿得一人] 2020-12-01 11:06

I have a php string containing the serialization of a javascript object :

$string = \'{fu:\"bar\",baz:[\"bat\"]}\';

The actual string is fa

相关标签:
5条回答
  • This sounded like a fun challenge, so I coded up a tiny parser :D

    class JsParserException extends Exception {}
    function parse_jsobj($str, &$data) {
        $str = trim($str);
        if(strlen($str) < 1) return;
    
        if($str{0} != '{') {
            throw new JsParserException('The given string is not a JS object');
        }
        $str = substr($str, 1);
    
        /* While we have data, and it's not the end of this dict (the comma is needed for nested dicts) */
        while(strlen($str) && $str{0} != '}' && $str{0} != ',') { 
            /* find the key */
            if($str{0} == "'" || $str{0} == '"') {
                /* quoted key */
                list($str, $key) = parse_jsdata($str, ':');
            } else {
                $match = null;
                /* unquoted key */
                if(!preg_match('/^\s*[a-zA-z_][a-zA-Z_\d]*\s*:/', $str, $match)) {
                throw new JsParserException('Invalid key ("'.$str.'")');
                }   
                $key = $match[0];
                $str = substr($str, strlen($key));
                $key = trim(substr($key, 0, -1)); /* discard the ':' */
            }
    
            list($str, $data[$key]) = parse_jsdata($str, '}');
        }
        "Finshed dict. Str: '$str'\n";
        return substr($str, 1);
    }
    
    function comma_or_term_pos($str, $term) {
        $cpos = strpos($str, ',');
        $tpos = strpos($str, $term);
        if($cpos === false && $tpos === false) {
            throw new JsParserException('unterminated dict or array');
        } else if($cpos === false) {
            return $tpos;
        } else if($tpos === false) {
            return $cpos;
        }
        return min($tpos, $cpos);
    }
    
    function parse_jsdata($str, $term="}") {
        $str = trim($str);
    
    
        if(is_numeric($str{0}."0")) {
            /* a number (int or float) */
            $newpos = comma_or_term_pos($str, $term);
            $num = trim(substr($str, 0, $newpos));
            $str = substr($str, $newpos+1); /* discard num and comma */
            if(!is_numeric($num)) {
                throw new JsParserException('OOPSIE while parsing number: "'.$num.'"');
            }
            return array(trim($str), $num+0);
        } else if($str{0} == '"' || $str{0} == "'") {
            /* string */
            $q = $str{0};
            $offset = 1;
            do {
                $pos = strpos($str, $q, $offset);
                $offset = $pos;
            } while($str{$pos-1} == '\\'); /* find un-escaped quote */
            $data = substr($str, 1, $pos-1);
            $str = substr($str, $pos);
            $pos = comma_or_term_pos($str, $term);
            $str = substr($str, $pos+1);        
            return array(trim($str), $data);
        } else if($str{0} == '{') {
            /* dict */
            $data = array();
            $str = parse_jsobj($str, $data);
            return array($str, $data);
        } else if($str{0} == '[') {
            /* array */
            $arr = array();
            $str = substr($str, 1);
            while(strlen($str) && $str{0} != $term && $str{0} != ',') {
                $val = null;
                list($str, $val) = parse_jsdata($str, ']');
                $arr[] = $val;
                $str = trim($str);
            }
            $str = trim(substr($str, 1));
            return array($str, $arr);
        } else if(stripos($str, 'true') === 0) {
            /* true */
            $pos = comma_or_term_pos($str, $term);
            $str = substr($str, $pos+1); /* discard terminator */
            return array(trim($str), true);
        } else if(stripos($str, 'false') === 0) {
            /* false */
            $pos = comma_or_term_pos($str, $term);
            $str = substr($str, $pos+1); /* discard terminator */
            return array(trim($str), false);
        } else if(stripos($str, 'null') === 0) {
            /* null */
            $pos = comma_or_term_pos($str, $term);
            $str = substr($str, $pos+1); /* discard terminator */
            return array(trim($str), null);
        } else if(strpos($str, 'undefined') === 0) {
            /* null */
            $pos = comma_or_term_pos($str, $term);
            $str = substr($str, $pos+1); /* discard terminator */
            return array(trim($str), null);
        } else {
            throw new JsParserException('Cannot figure out how to parse "'.$str.'" (term is '.$term.')');
        }
    }
    

    Usage:

    $data = '{fu:"bar",baz:["bat"]}';    
    $parsed = array();    
    parse_jsobj($data, $parsed);    
    var_export($parsed);
    

    Gives:

    array (
      'fu' => 'bar',
      'baz' =>
      array (
        0 => 'bat',
      ),
    )
    

    Tested with these strings:

    '{fu:"bar",baz:["bat"]}',
    '{rec:{rec:{rec:false}}}',
    '{foo:[1,2,[3,4]]}',
    '{fu:{fu:"bar"},bar:{fu:"bar"}}',
    '{"quoted key":[1,2,3]}',
    '{und:undefined,"baz":[1,2,"3"]}',
    '{arr:["a","b"],"baz":"foo","gar":{"faz":false,t:"2"},f:false}',
    
    0 讨论(0)
  • 2020-12-01 11:36

    I found out that the Yii-framework's CJSON::decode() function handles Javascript objects as well.

    If you're not using Yii, you should be able to just use the source code

    0 讨论(0)
  • 2020-12-01 11:37

    thank luttkens

    the CJON::decode() class of the Yii-framework works perfectly !

    require_once ($_SERVER['DOCUMENT_ROOT']."/phplib/CJSON.php");
    
    $json = new CJSON();
    $data = $json->decode('{ url : "/jslib/maps/marker/marker_red.png", height : 34, width : 20, anchorIcon : [5,25.5], anchorText : [0,2], }', true);
    
    print_r( $data );
    

    result :

    Array
    (
        [url] => /jslib/maps/marker/marker_red.png
        [height] => 34
        [width] => 20
        [anchorIcon] => Array
            (
                [0] => 5
                [1] => 25.5
            )
    
        [anchorText] => Array
            (
                [0] => 0
                [1] => 2
            )
    
    )
    
    0 讨论(0)
  • 2020-12-01 11:45

    What about that library? http://timwhitlock.info/tag/jparser/

    I haven't tried it yet.

    0 讨论(0)
  • 2020-12-01 12:01

    Pear Services_JSON will parse that string (tested version 1.31). But given that that is a JSON parser and that this isn't valid JSON you have no guarantee that future versions will still work.

    0 讨论(0)
提交回复
热议问题