web/lib/Zend/Json.php
changeset 807 877f952ae2bd
parent 207 621fa6caec0c
child 1230 68c69c656a2c
equal deleted inserted replaced
805:5e7a0fedabdf 807:877f952ae2bd
    12  * obtain it through the world-wide-web, please send an email
    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.
    13  * to license@zend.com so we can send you a copy immediately.
    14  *
    14  *
    15  * @category   Zend
    15  * @category   Zend
    16  * @package    Zend_Json
    16  * @package    Zend_Json
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    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
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    19  * @version    $Id: Json.php 20615 2010-01-25 19:54:12Z matthew $
    19  * @version    $Id: Json.php 24593 2012-01-05 20:35:02Z matthew $
    20  */
    20  */
    21 
    21 
    22 /**
    22 /**
    23  * Zend_Json_Expr.
    23  * Zend_Json_Expr.
    24  *
    24  *
    31  * Class for encoding to and decoding from JSON.
    31  * Class for encoding to and decoding from JSON.
    32  *
    32  *
    33  * @category   Zend
    33  * @category   Zend
    34  * @package    Zend_Json
    34  * @package    Zend_Json
    35  * @uses       Zend_Json_Expr
    35  * @uses       Zend_Json_Expr
    36  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
    36  * @copyright  Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
    37  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    37  * @license    http://framework.zend.com/license/new-bsd     New BSD License
    38  */
    38  */
    39 class Zend_Json
    39 class Zend_Json
    40 {
    40 {
    41     /**
    41     /**
   123      * @param  array $options Additional options used during encoding
   123      * @param  array $options Additional options used during encoding
   124      * @return string JSON encoded object
   124      * @return string JSON encoded object
   125      */
   125      */
   126     public static function encode($valueToEncode, $cycleCheck = false, $options = array())
   126     public static function encode($valueToEncode, $cycleCheck = false, $options = array())
   127     {
   127     {
   128         if (is_object($valueToEncode) && method_exists($valueToEncode, 'toJson')) {
   128         if (is_object($valueToEncode)) {
   129             return $valueToEncode->toJson();
   129             if (method_exists($valueToEncode, 'toJson')) {
       
   130                 return $valueToEncode->toJson();
       
   131             } elseif (method_exists($valueToEncode, 'toArray')) {
       
   132                 return self::encode($valueToEncode->toArray(), $cycleCheck, $options);
       
   133             }
   130         }
   134         }
   131 
   135 
   132         // Pre-encoding look for Zend_Json_Expr objects and replacing by tmp ids
   136         // Pre-encoding look for Zend_Json_Expr objects and replacing by tmp ids
   133         $javascriptExpressions = array();
   137         $javascriptExpressions = array();
   134         if(isset($options['enableJsonExprFinder'])
   138         if(isset($options['enableJsonExprFinder'])
   177      * NOTE this method is recursive.
   181      * NOTE this method is recursive.
   178      *
   182      *
   179      * NOTE: This method is used internally by the encode method.
   183      * NOTE: This method is used internally by the encode method.
   180      *
   184      *
   181      * @see encode
   185      * @see encode
   182      * @param mixed $valueToCheck a string - object property to be encoded
   186      * @param array|object|Zend_Json_Expr $value a string - object property to be encoded
       
   187      * @param array $javascriptExpressions
       
   188      * @param null $currentKey
       
   189      *
       
   190      * @internal param mixed $valueToCheck
   183      * @return void
   191      * @return void
   184      */
   192      */
   185     protected static function _recursiveJsonExprFinder(
   193     protected static function _recursiveJsonExprFinder(&$value, array &$javascriptExpressions, $currentKey = null)
   186         &$value, array &$javascriptExpressions, $currentKey = null
   194     {
   187     ) {
       
   188          if ($value instanceof Zend_Json_Expr) {
   195          if ($value instanceof Zend_Json_Expr) {
   189             // TODO: Optimize with ascii keys, if performance is bad
   196             // TODO: Optimize with ascii keys, if performance is bad
   190             $magicKey = "____" . $currentKey . "_" . (count($javascriptExpressions));
   197             $magicKey = "____" . $currentKey . "_" . (count($javascriptExpressions));
   191             $javascriptExpressions[] = array(
   198             $javascriptExpressions[] = array(
   192 
   199 
   206         }
   213         }
   207         return $value;
   214         return $value;
   208     }
   215     }
   209 
   216 
   210     /**
   217     /**
   211      * fromXml - Converts XML to JSON
   218      * Return the value of an XML attribute text or the text between
   212      *
   219      * the XML tags
   213      * Converts a XML formatted string into a JSON formatted string.
   220      *
   214      * The value returned will be a string in JSON format.
   221      * In order to allow Zend_Json_Expr from xml, we check if the node
   215      *
   222      * matchs the pattern that try to detect if it is a new Zend_Json_Expr
   216      * The caller of this function needs to provide only the first parameter,
   223      * if it matches, we return a new Zend_Json_Expr instead of a text node
   217      * which is an XML formatted String. The second parameter is optional, which
   224      *
   218      * lets the user to select if the XML attributes in the input XML string
   225      * @param SimpleXMLElement $simpleXmlElementObject
   219      * should be included or ignored in xml2json conversion.
   226      * @return Zend_Json_Expr|string
   220      *
   227      */
   221      * This function converts the XML formatted string into a PHP array by
   228     protected static function _getXmlValue($simpleXmlElementObject) {
   222      * calling a recursive (protected static) function in this class. Then, it
   229         $pattern = '/^[\s]*new Zend_Json_Expr[\s]*\([\s]*[\"\']{1}(.*)[\"\']{1}[\s]*\)[\s]*$/';
   223      * converts that PHP array into JSON by calling the "encode" static funcion.
   230         $matchings = array();
   224      *
   231         $match = preg_match ($pattern, $simpleXmlElementObject, $matchings);
   225      * Throws a Zend_Json_Exception if the input not a XML formatted string.
   232         if ($match) {
   226      * NOTE: Encoding native javascript expressions via Zend_Json_Expr is not possible.
   233             return new Zend_Json_Expr($matchings[1]);
   227      *
   234         } else {
   228      * @static
   235             return (trim(strval($simpleXmlElementObject)));
   229      * @access public
   236         }
   230      * @param string $xmlStringContents XML String to be converted
   237     }
   231      * @param boolean $ignoreXmlAttributes Include or exclude XML attributes in
       
   232      * the xml2json conversion process.
       
   233      * @return mixed - JSON formatted string on success
       
   234      * @throws Zend_Json_Exception
       
   235      */
       
   236     public static function fromXml ($xmlStringContents, $ignoreXmlAttributes=true) {
       
   237         // Load the XML formatted string into a Simple XML Element object.
       
   238         $simpleXmlElementObject = simplexml_load_string($xmlStringContents);
       
   239 
       
   240         // If it is not a valid XML content, throw an exception.
       
   241         if ($simpleXmlElementObject == null) {
       
   242             require_once 'Zend/Json/Exception.php';
       
   243             throw new Zend_Json_Exception('Function fromXml was called with an invalid XML formatted string.');
       
   244         } // End of if ($simpleXmlElementObject == null)
       
   245 
       
   246         $resultArray = null;
       
   247 
       
   248         // Call the recursive function to convert the XML into a PHP array.
       
   249         $resultArray = self::_processXml($simpleXmlElementObject, $ignoreXmlAttributes);
       
   250 
       
   251         // Convert the PHP array to JSON using Zend_Json encode method.
       
   252         // It is just that simple.
       
   253         $jsonStringOutput = self::encode($resultArray);
       
   254         return($jsonStringOutput);
       
   255     } // End of function fromXml.
       
   256 
       
   257     /**
   238     /**
   258      * _processXml - Contains the logic for xml2json
   239      * _processXml - Contains the logic for xml2json
   259      *
   240      *
   260      * The logic in this function is a recursive one.
   241      * The logic in this function is a recursive one.
   261      *
   242      *
   268      * calling a recursive (protected static) function in this class. Once all
   249      * calling a recursive (protected static) function in this class. Once all
   269      * the XML elements are stored in the PHP array, it is returned to the caller.
   250      * the XML elements are stored in the PHP array, it is returned to the caller.
   270      *
   251      *
   271      * Throws a Zend_Json_Exception if the XML tree is deeper than the allowed limit.
   252      * Throws a Zend_Json_Exception if the XML tree is deeper than the allowed limit.
   272      *
   253      *
   273      * @static
   254      * @param SimpleXMLElement $simpleXmlElementObject
   274      * @access protected
   255      * @param boolean $ignoreXmlAttributes
   275      * @param SimpleXMLElement $simpleXmlElementObject XML element to be converted
   256      * @param integer $recursionDepth
   276      * @param boolean $ignoreXmlAttributes Include or exclude XML attributes in
   257      * @return array
   277      * the xml2json conversion process.
   258      */
   278      * @param int $recursionDepth Current recursion depth of this function
   259     protected static function _processXml($simpleXmlElementObject, $ignoreXmlAttributes, $recursionDepth=0)
   279      * @return mixed - On success, a PHP associative array of traversed XML elements
   260     {
   280      * @throws Zend_Json_Exception
       
   281      */
       
   282     protected static function _processXml ($simpleXmlElementObject, $ignoreXmlAttributes, $recursionDepth=0) {
       
   283         // Keep an eye on how deeply we are involved in recursion.
   261         // Keep an eye on how deeply we are involved in recursion.
   284         if ($recursionDepth > self::$maxRecursionDepthAllowed) {
   262         if ($recursionDepth > self::$maxRecursionDepthAllowed) {
   285             // XML tree is too deep. Exit now by throwing an exception.
   263             // XML tree is too deep. Exit now by throwing an exception.
   286             require_once 'Zend/Json/Exception.php';
   264             require_once 'Zend/Json/Exception.php';
   287             throw new Zend_Json_Exception(
   265             throw new Zend_Json_Exception(
   288                 "Function _processXml exceeded the allowed recursion depth of " .
   266                 "Function _processXml exceeded the allowed recursion depth of " .
   289                 self::$maxRecursionDepthAllowed);
   267                 self::$maxRecursionDepthAllowed);
   290         } // End of if ($recursionDepth > self::$maxRecursionDepthAllowed)
   268         } // End of if ($recursionDepth > self::$maxRecursionDepthAllowed)
   291 
   269 
   292         if ($recursionDepth == 0) {
   270         $children = $simpleXmlElementObject->children();
   293             // Store the original SimpleXmlElementObject sent by the caller.
   271         $name = $simpleXmlElementObject->getName();
   294             // We will need it at the very end when we return from here for good.
   272         $value = self::_getXmlValue($simpleXmlElementObject);
   295             $callerProvidedSimpleXmlElementObject = $simpleXmlElementObject;
   273         $attributes = (array) $simpleXmlElementObject->attributes();
   296         } // End of if ($recursionDepth == 0)
   274 
   297 
   275         if (count($children) == 0) {
   298         if ($simpleXmlElementObject instanceof SimpleXMLElement) {
   276             if (!empty($attributes) && !$ignoreXmlAttributes) {
   299             // Get a copy of the simpleXmlElementObject
   277                 foreach ($attributes['@attributes'] as $k => $v) {
   300             $copyOfSimpleXmlElementObject = $simpleXmlElementObject;
   278                     $attributes['@attributes'][$k]= self::_getXmlValue($v);
   301             // Get the object variables in the SimpleXmlElement object for us to iterate.
   279                 }
   302             $simpleXmlElementObject = get_object_vars($simpleXmlElementObject);
   280                 if (!empty($value)) {
   303         } // End of if (get_class($simpleXmlElementObject) == "SimpleXMLElement")
   281                     $attributes['@text'] = $value;
   304 
   282                 } 
   305         // It needs to be an array of object variables.
   283                 return array($name => $attributes);
   306         if (is_array($simpleXmlElementObject)) {
   284             } else {
   307             // Initialize a result array.
   285                return array($name => $value);
   308             $resultArray = array();
   286             }
   309             // Is the input array size 0? Then, we reached the rare CDATA text if any.
       
   310             if (count($simpleXmlElementObject) <= 0) {
       
   311                 // Let us return the lonely CDATA. It could even be
       
   312                 // an empty element or just filled with whitespaces.
       
   313                 return (trim(strval($copyOfSimpleXmlElementObject)));
       
   314             } // End of if (count($simpleXmlElementObject) <= 0)
       
   315 
       
   316             // Let us walk through the child elements now.
       
   317             foreach($simpleXmlElementObject as $key=>$value) {
       
   318                 // Check if we need to ignore the XML attributes.
       
   319                 // If yes, you can skip processing the XML attributes.
       
   320                 // Otherwise, add the XML attributes to the result array.
       
   321                 if(($ignoreXmlAttributes == true) && (is_string($key)) && ($key == "@attributes")) {
       
   322                     continue;
       
   323                 } // End of if(($ignoreXmlAttributes == true) && ($key == "@attributes"))
       
   324 
       
   325                 // Let us recursively process the current XML element we just visited.
       
   326                 // Increase the recursion depth by one.
       
   327                 $recursionDepth++;
       
   328                 $resultArray[$key] = self::_processXml ($value, $ignoreXmlAttributes, $recursionDepth);
       
   329 
       
   330                 // Decrease the recursion depth by one.
       
   331                 $recursionDepth--;
       
   332             } // End of foreach($simpleXmlElementObject as $key=>$value) {
       
   333 
       
   334             if ($recursionDepth == 0) {
       
   335                 // That is it. We are heading to the exit now.
       
   336                 // Set the XML root element name as the root [top-level] key of
       
   337                 // the associative array that we are going to return to the original
       
   338                 // caller of this recursive function.
       
   339                 $tempArray = $resultArray;
       
   340                 $resultArray = array();
       
   341                 $resultArray[$callerProvidedSimpleXmlElementObject->getName()] = $tempArray;
       
   342             } // End of if ($recursionDepth == 0)
       
   343 
       
   344             return($resultArray);
       
   345         } else {
   287         } else {
   346             // We are now looking at either the XML attribute text or
   288             $childArray= array();
   347             // the text between the XML tags.
   289             foreach ($children as $child) {
   348 
   290                 $childname = $child->getName();
   349             // In order to allow Zend_Json_Expr from xml, we check if the node
   291                 $element = self::_processXml($child,$ignoreXmlAttributes,$recursionDepth+1);
   350             // matchs the pattern that try to detect if it is a new Zend_Json_Expr
   292                 if (array_key_exists($childname, $childArray)) {
   351             // if it matches, we return a new Zend_Json_Expr instead of a text node
   293                     if (empty($subChild[$childname])) {
   352             $pattern = '/^[\s]*new Zend_Json_Expr[\s]*\([\s]*[\"\']{1}(.*)[\"\']{1}[\s]*\)[\s]*$/';
   294                         $childArray[$childname] = array($childArray[$childname]);
   353             $matchings = array();
   295                         $subChild[$childname] = true;
   354             $match = preg_match ($pattern, $simpleXmlElementObject, $matchings);
   296                     }
   355             if ($match) {
   297                     $childArray[$childname][] = $element[$childname];
   356                 return new Zend_Json_Expr($matchings[1]);
   298                 } else {
   357             } else {
   299                     $childArray[$childname] = $element[$childname];
   358                 return (trim(strval($simpleXmlElementObject)));
   300                 }
   359             }
   301             }
   360 
   302             if (!empty($attributes) && !$ignoreXmlAttributes) {
   361         } // End of if (is_array($simpleXmlElementObject))
   303                 foreach ($attributes['@attributes'] as $k => $v) {
   362     } // End of function _processXml.
   304                     $attributes['@attributes'][$k] = self::_getXmlValue($v);
       
   305                 }
       
   306                 $childArray['@attributes'] = $attributes['@attributes'];
       
   307             }
       
   308             if (!empty($value)) {
       
   309                 $childArray['@text'] = $value;
       
   310             }
       
   311             return array($name => $childArray);
       
   312         }
       
   313     }
       
   314 
       
   315     /**
       
   316      * fromXml - Converts XML to JSON
       
   317      *
       
   318      * Converts a XML formatted string into a JSON formatted string.
       
   319      * The value returned will be a string in JSON format.
       
   320      *
       
   321      * The caller of this function needs to provide only the first parameter,
       
   322      * which is an XML formatted String. The second parameter is optional, which
       
   323      * lets the user to select if the XML attributes in the input XML string
       
   324      * should be included or ignored in xml2json conversion.
       
   325      *
       
   326      * This function converts the XML formatted string into a PHP array by
       
   327      * calling a recursive (protected static) function in this class. Then, it
       
   328      * converts that PHP array into JSON by calling the "encode" static funcion.
       
   329      *
       
   330      * Throws a Zend_Json_Exception if the input not a XML formatted string.
       
   331      * NOTE: Encoding native javascript expressions via Zend_Json_Expr is not possible.
       
   332      *
       
   333      * @static
       
   334      * @access public
       
   335      * @param string $xmlStringContents XML String to be converted
       
   336      * @param boolean $ignoreXmlAttributes Include or exclude XML attributes in
       
   337      * the xml2json conversion process.
       
   338      * @return mixed - JSON formatted string on success
       
   339      * @throws Zend_Json_Exception
       
   340      */
       
   341     public static function fromXml($xmlStringContents, $ignoreXmlAttributes=true)
       
   342     {
       
   343         // Load the XML formatted string into a Simple XML Element object.
       
   344         $simpleXmlElementObject = simplexml_load_string($xmlStringContents);
       
   345 
       
   346         // If it is not a valid XML content, throw an exception.
       
   347         if ($simpleXmlElementObject == null) {
       
   348             require_once 'Zend/Json/Exception.php';
       
   349             throw new Zend_Json_Exception('Function fromXml was called with an invalid XML formatted string.');
       
   350         } // End of if ($simpleXmlElementObject == null)
       
   351 
       
   352         $resultArray = null;
       
   353 
       
   354         // Call the recursive function to convert the XML into a PHP array.
       
   355         $resultArray = self::_processXml($simpleXmlElementObject, $ignoreXmlAttributes);
       
   356 
       
   357         // Convert the PHP array to JSON using Zend_Json encode method.
       
   358         // It is just that simple.
       
   359         $jsonStringOutput = self::encode($resultArray);
       
   360         return($jsonStringOutput);
       
   361     }
       
   362 
   363     
   363     
       
   364 
   364     /**
   365     /**
   365      * Pretty-print JSON string
   366      * Pretty-print JSON string
   366      * 
   367      *
   367      * Use 'indent' option to select indentation string - by default it's a tab
   368      * Use 'format' option to select output format - currently html and txt supported, txt is default
   368      * 
   369      * Use 'indent' option to override the indentation string set in the format - by default for the 'txt' format it's a tab
       
   370      *
   369      * @param string $json Original JSON string
   371      * @param string $json Original JSON string
   370      * @param array $options Encoding options
   372      * @param array $options Encoding options
   371      * @return string
   373      * @return string
   372      */
   374      */
   373     public static function prettyPrint($json, $options = array())
   375     public static function prettyPrint($json, $options = array())
   374     {
   376     {
   375         $tokens = preg_split('|([\{\}\]\[,])|', $json, -1, PREG_SPLIT_DELIM_CAPTURE);
   377         $tokens = preg_split('|([\{\}\]\[,])|', $json, -1, PREG_SPLIT_DELIM_CAPTURE);
   376         $result = "";
   378         $result = '';
   377         $indent = 0;
   379         $indent = 0;
   378         
   380 
       
   381         $format= 'txt';
       
   382 
   379         $ind = "\t";
   383         $ind = "\t";
   380         if(isset($options['indent'])) {
   384 
       
   385         if (isset($options['format'])) {
       
   386             $format = $options['format'];
       
   387         }
       
   388 
       
   389         switch ($format) {
       
   390             case 'html':
       
   391                 $lineBreak = '<br />';
       
   392                 $ind = '&nbsp;&nbsp;&nbsp;&nbsp;';
       
   393                 break;
       
   394             default:
       
   395             case 'txt':
       
   396                 $lineBreak = "\n";
       
   397                 $ind = "\t";
       
   398                 break;
       
   399         }
       
   400 
       
   401         // override the defined indent setting with the supplied option
       
   402         if (isset($options['indent'])) {
   381             $ind = $options['indent'];
   403             $ind = $options['indent'];
   382         }
   404         }
   383         
   405 
       
   406         $inLiteral = false;
   384         foreach($tokens as $token) {
   407         foreach($tokens as $token) {
   385             if($token == "") continue;
   408             if($token == '') {
   386             
   409                 continue;
       
   410             }
       
   411 
   387             $prefix = str_repeat($ind, $indent);
   412             $prefix = str_repeat($ind, $indent);
   388             if($token == "{" || $token == "[") {
   413             if (!$inLiteral && ($token == '{' || $token == '[')) {
   389                 $indent++;
   414                 $indent++;
   390                 if($result != "" && $result[strlen($result)-1] == "\n") {
   415                 if (($result != '') && ($result[(strlen($result)-1)] == $lineBreak)) {
   391                     $result .= $prefix;
   416                     $result .= $prefix;
   392                 }
   417                 }
   393                 $result .= "$token\n";
   418                 $result .= $token . $lineBreak;
   394             } else if($token == "}" || $token == "]") {
   419             } elseif (!$inLiteral && ($token == '}' || $token == ']')) {
   395                 $indent--;
   420                 $indent--;
   396                 $prefix = str_repeat($ind, $indent);
   421                 $prefix = str_repeat($ind, $indent);
   397                 $result .= "\n$prefix$token";                
   422                 $result .= $lineBreak . $prefix . $token;
   398             } else if($token == ",") {
   423             } elseif (!$inLiteral && $token == ',') {
   399                 $result .= "$token\n";
   424                 $result .= $token . $lineBreak;
   400             } else {
   425             } else {
   401                 $result .= $prefix.$token;
   426                 $result .= ( $inLiteral ? '' : $prefix ) . $token;
       
   427                 
       
   428                 // Count # of unescaped double-quotes in token, subtract # of
       
   429                 // escaped double-quotes and if the result is odd then we are 
       
   430                 // inside a string literal
       
   431                 if ((substr_count($token, "\"")-substr_count($token, "\\\"")) % 2 != 0) {
       
   432                     $inLiteral = !$inLiteral;
       
   433                 }
   402             }
   434             }
   403         }
   435         }
   404         return $result;
   436         return $result;
   405    }
   437    }
   406 }
   438 }