2009-10-12 3 views
13

У меня есть PHP строка, содержащая сериализацию объектов JavaScript:Синтаксический Javascript (не JSON) в PHP

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

Фактическая строка является гораздо более сложным, конечно, но все-таки хорошо сформированная JavaScript. Это не стандартный JSON, поэтому json_decode не работает. Вы знаете какую-либо библиотеку php, которая будет анализировать эту строку и возвращать ассоциативный массив php?

+7

Пожалуйста, объясните, почему вы не можете JSONify. Это упростит все. – gnud

+0

Как сказано, это * не * действительный JSON. Действительный JSON требует, чтобы ключи объектов формировались как строки, т.е. заключенные в двойные кавычки. – Alsciende

+0

Да, я понял. Я спрашивал, почему вы не можете превратить его в действительный json в js? – gnud

ответ

9

Pear Services_JSON проанализирует эту строку (проверенная версия 1.31). Но учитывая, что это парсер JSON и что это недействительно JSON, у вас нет гарантии, что будущие версии будут работать.

+0

Спасибо, это прекрасно :) – Alsciende

16

Это звучало как весело вызов, поэтому я закодирован крошечный анализатор: 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.')'); 
    } 
} 

Использование:

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

Дает:

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

испытан с этими строками:

'{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

Это действительно очень приятно. Два замечания: Ключи объектов Javascript могут быть заключены в простые кавычки или двойные кавычки (обычно, если ключ не является допустимым идентификатором (например, содержит пробел)). И undefined - действительное значение. – Alsciende

+0

Я думаю, что это не сработает, если объект включен в значение другого объекта, а strrpos не прав, например '{fu: {fu: "bar"}, bar: {fu: "bar"}}' – Alsciende

+0

Я думаю, что теперь, я исправил некоторые ошибки для dicts в dicts и массивах в массивах. Теперь ваша тестовая строка работает. – gnud

1

Я узнал, что функция YII-framework CJSON::decode() обрабатывает объекты Javascript.

Если вы не используете Yii, вы должны быть в состоянии использовать только source code

Смежные вопросы