File indexing completed on 2024-05-12 14:04:34
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Json 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Zend_Json_Expr. 0024 * 0025 * @see Zend_Json_Expr 0026 */ 0027 // require_once 'Zend/Json/Expr.php'; 0028 0029 /** @see Zend_Xml_Security */ 0030 // require_once 'Zend/Xml/Security.php'; 0031 0032 /** 0033 * Class for encoding to and decoding from JSON. 0034 * 0035 * @category Zend 0036 * @package Zend_Json 0037 * @uses Zend_Json_Expr 0038 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0039 * @license http://framework.zend.com/license/new-bsd New BSD License 0040 */ 0041 class Zend_Json 0042 { 0043 /** 0044 * How objects should be encoded -- arrays or as StdClass. TYPE_ARRAY is 1 0045 * so that it is a boolean true value, allowing it to be used with 0046 * ext/json's functions. 0047 */ 0048 const TYPE_ARRAY = 1; 0049 const TYPE_OBJECT = 0; 0050 0051 /** 0052 * To check the allowed nesting depth of the XML tree during xml2json conversion. 0053 * 0054 * @var int 0055 */ 0056 public static $maxRecursionDepthAllowed=25; 0057 0058 /** 0059 * @var bool 0060 */ 0061 public static $useBuiltinEncoderDecoder = false; 0062 0063 /** 0064 * Decodes the given $encodedValue string which is 0065 * encoded in the JSON format 0066 * 0067 * Uses ext/json's json_decode if available. 0068 * 0069 * @param string $encodedValue Encoded in JSON format 0070 * @param int $objectDecodeType Optional; flag indicating how to decode 0071 * objects. See {@link Zend_Json_Decoder::decode()} for details. 0072 * @return mixed 0073 */ 0074 public static function decode($encodedValue, $objectDecodeType = Zend_Json::TYPE_ARRAY) 0075 { 0076 $encodedValue = (string) $encodedValue; 0077 if (function_exists('json_decode') && self::$useBuiltinEncoderDecoder !== true) { 0078 $decode = json_decode($encodedValue, $objectDecodeType); 0079 0080 // php < 5.3 0081 if (!function_exists('json_last_error')) { 0082 if (strtolower($encodedValue) === 'null') { 0083 return null; 0084 } elseif ($decode === null) { 0085 // require_once 'Zend/Json/Exception.php'; 0086 throw new Zend_Json_Exception('Decoding failed'); 0087 } 0088 // php >= 5.3 0089 } elseif (($jsonLastErr = json_last_error()) != JSON_ERROR_NONE) { 0090 // require_once 'Zend/Json/Exception.php'; 0091 switch ($jsonLastErr) { 0092 case JSON_ERROR_DEPTH: 0093 throw new Zend_Json_Exception('Decoding failed: Maximum stack depth exceeded'); 0094 case JSON_ERROR_CTRL_CHAR: 0095 throw new Zend_Json_Exception('Decoding failed: Unexpected control character found'); 0096 case JSON_ERROR_SYNTAX: 0097 throw new Zend_Json_Exception('Decoding failed: Syntax error'); 0098 default: 0099 throw new Zend_Json_Exception('Decoding failed'); 0100 } 0101 } 0102 0103 return $decode; 0104 } 0105 0106 // require_once 'Zend/Json/Decoder.php'; 0107 return Zend_Json_Decoder::decode($encodedValue, $objectDecodeType); 0108 } 0109 0110 /** 0111 * Encode the mixed $valueToEncode into the JSON format 0112 * 0113 * Encodes using ext/json's json_encode() if available. 0114 * 0115 * NOTE: Object should not contain cycles; the JSON format 0116 * does not allow object reference. 0117 * 0118 * NOTE: Only public variables will be encoded 0119 * 0120 * NOTE: Encoding native javascript expressions are possible using Zend_Json_Expr. 0121 * You can enable this by setting $options['enableJsonExprFinder'] = true 0122 * 0123 * @see Zend_Json_Expr 0124 * 0125 * @param mixed $valueToEncode 0126 * @param boolean $cycleCheck Optional; whether or not to check for object recursion; off by default 0127 * @param array $options Additional options used during encoding 0128 * @return string JSON encoded object 0129 */ 0130 public static function encode($valueToEncode, $cycleCheck = false, $options = array()) 0131 { 0132 if (is_object($valueToEncode)) { 0133 if (method_exists($valueToEncode, 'toJson')) { 0134 return $valueToEncode->toJson(); 0135 } elseif (method_exists($valueToEncode, 'toArray')) { 0136 return self::encode($valueToEncode->toArray(), $cycleCheck, $options); 0137 } 0138 } 0139 0140 // Pre-encoding look for Zend_Json_Expr objects and replacing by tmp ids 0141 $javascriptExpressions = array(); 0142 if(isset($options['enableJsonExprFinder']) 0143 && ($options['enableJsonExprFinder'] == true) 0144 ) { 0145 /** 0146 * @see Zend_Json_Encoder 0147 */ 0148 // require_once "Zend/Json/Encoder.php"; 0149 $valueToEncode = self::_recursiveJsonExprFinder($valueToEncode, $javascriptExpressions); 0150 } 0151 0152 // Encoding 0153 if (function_exists('json_encode') && self::$useBuiltinEncoderDecoder !== true) { 0154 $encodedResult = json_encode($valueToEncode); 0155 } else { 0156 // require_once 'Zend/Json/Encoder.php'; 0157 $encodedResult = Zend_Json_Encoder::encode($valueToEncode, $cycleCheck, $options); 0158 } 0159 0160 //only do post-proccessing to revert back the Zend_Json_Expr if any. 0161 if (count($javascriptExpressions) > 0) { 0162 $count = count($javascriptExpressions); 0163 for($i = 0; $i < $count; $i++) { 0164 $magicKey = $javascriptExpressions[$i]['magicKey']; 0165 $value = $javascriptExpressions[$i]['value']; 0166 0167 $encodedResult = str_replace( 0168 //instead of replacing "key:magicKey", we replace directly magicKey by value because "key" never changes. 0169 '"' . $magicKey . '"', 0170 $value, 0171 $encodedResult 0172 ); 0173 } 0174 } 0175 0176 return $encodedResult; 0177 } 0178 0179 /** 0180 * Check & Replace Zend_Json_Expr for tmp ids in the valueToEncode 0181 * 0182 * Check if the value is a Zend_Json_Expr, and if replace its value 0183 * with a magic key and save the javascript expression in an array. 0184 * 0185 * NOTE this method is recursive. 0186 * 0187 * NOTE: This method is used internally by the encode method. 0188 * 0189 * @see encode 0190 * @param array|object|Zend_Json_Expr $value a string - object property to be encoded 0191 * @param array $javascriptExpressions 0192 * @param null $currentKey 0193 * 0194 * @internal param mixed $valueToCheck 0195 * @return void 0196 */ 0197 protected static function _recursiveJsonExprFinder(&$value, array &$javascriptExpressions, $currentKey = null) 0198 { 0199 if ($value instanceof Zend_Json_Expr) { 0200 // TODO: Optimize with ascii keys, if performance is bad 0201 $magicKey = "____" . $currentKey . "_" . (count($javascriptExpressions)); 0202 $javascriptExpressions[] = array( 0203 0204 //if currentKey is integer, encodeUnicodeString call is not required. 0205 "magicKey" => (is_int($currentKey)) ? $magicKey : Zend_Json_Encoder::encodeUnicodeString($magicKey), 0206 "value" => $value->__toString(), 0207 ); 0208 $value = $magicKey; 0209 } elseif (is_array($value)) { 0210 foreach ($value as $k => $v) { 0211 $value[$k] = self::_recursiveJsonExprFinder($value[$k], $javascriptExpressions, $k); 0212 } 0213 } elseif (is_object($value)) { 0214 foreach ($value as $k => $v) { 0215 $value->$k = self::_recursiveJsonExprFinder($value->$k, $javascriptExpressions, $k); 0216 } 0217 } 0218 return $value; 0219 } 0220 0221 /** 0222 * Return the value of an XML attribute text or the text between 0223 * the XML tags 0224 * 0225 * In order to allow Zend_Json_Expr from xml, we check if the node 0226 * matchs the pattern that try to detect if it is a new Zend_Json_Expr 0227 * if it matches, we return a new Zend_Json_Expr instead of a text node 0228 * 0229 * @param SimpleXMLElement $simpleXmlElementObject 0230 * @return Zend_Json_Expr|string 0231 */ 0232 protected static function _getXmlValue($simpleXmlElementObject) { 0233 $pattern = '/^[\s]*new Zend_Json_Expr[\s]*\([\s]*[\"\']{1}(.*)[\"\']{1}[\s]*\)[\s]*$/'; 0234 $matchings = array(); 0235 $match = preg_match ($pattern, $simpleXmlElementObject, $matchings); 0236 if ($match) { 0237 return new Zend_Json_Expr($matchings[1]); 0238 } else { 0239 return (trim(strval($simpleXmlElementObject))); 0240 } 0241 } 0242 /** 0243 * _processXml - Contains the logic for xml2json 0244 * 0245 * The logic in this function is a recursive one. 0246 * 0247 * The main caller of this function (i.e. fromXml) needs to provide 0248 * only the first two parameters i.e. the SimpleXMLElement object and 0249 * the flag for ignoring or not ignoring XML attributes. The third parameter 0250 * will be used internally within this function during the recursive calls. 0251 * 0252 * This function converts the SimpleXMLElement object into a PHP array by 0253 * calling a recursive (protected static) function in this class. Once all 0254 * the XML elements are stored in the PHP array, it is returned to the caller. 0255 * 0256 * Throws a Zend_Json_Exception if the XML tree is deeper than the allowed limit. 0257 * 0258 * @param SimpleXMLElement $simpleXmlElementObject 0259 * @param boolean $ignoreXmlAttributes 0260 * @param integer $recursionDepth 0261 * @return array 0262 */ 0263 protected static function _processXml($simpleXmlElementObject, $ignoreXmlAttributes, $recursionDepth=0) 0264 { 0265 // Keep an eye on how deeply we are involved in recursion. 0266 if ($recursionDepth > self::$maxRecursionDepthAllowed) { 0267 // XML tree is too deep. Exit now by throwing an exception. 0268 // require_once 'Zend/Json/Exception.php'; 0269 throw new Zend_Json_Exception( 0270 "Function _processXml exceeded the allowed recursion depth of " . 0271 self::$maxRecursionDepthAllowed); 0272 } // End of if ($recursionDepth > self::$maxRecursionDepthAllowed) 0273 0274 $children = $simpleXmlElementObject->children(); 0275 $name = $simpleXmlElementObject->getName(); 0276 $value = self::_getXmlValue($simpleXmlElementObject); 0277 $attributes = (array) $simpleXmlElementObject->attributes(); 0278 0279 if (count($children) == 0) { 0280 if (!empty($attributes) && !$ignoreXmlAttributes) { 0281 foreach ($attributes['@attributes'] as $k => $v) { 0282 $attributes['@attributes'][$k]= self::_getXmlValue($v); 0283 } 0284 if (!empty($value)) { 0285 $attributes['@text'] = $value; 0286 } 0287 return array($name => $attributes); 0288 } else { 0289 return array($name => $value); 0290 } 0291 } else { 0292 $childArray= array(); 0293 foreach ($children as $child) { 0294 $childname = $child->getName(); 0295 $element = self::_processXml($child,$ignoreXmlAttributes,$recursionDepth+1); 0296 if (array_key_exists($childname, $childArray)) { 0297 if (empty($subChild[$childname])) { 0298 $childArray[$childname] = array($childArray[$childname]); 0299 $subChild[$childname] = true; 0300 } 0301 $childArray[$childname][] = $element[$childname]; 0302 } else { 0303 $childArray[$childname] = $element[$childname]; 0304 } 0305 } 0306 if (!empty($attributes) && !$ignoreXmlAttributes) { 0307 foreach ($attributes['@attributes'] as $k => $v) { 0308 $attributes['@attributes'][$k] = self::_getXmlValue($v); 0309 } 0310 $childArray['@attributes'] = $attributes['@attributes']; 0311 } 0312 if (!empty($value)) { 0313 $childArray['@text'] = $value; 0314 } 0315 return array($name => $childArray); 0316 } 0317 } 0318 0319 /** 0320 * fromXml - Converts XML to JSON 0321 * 0322 * Converts a XML formatted string into a JSON formatted string. 0323 * The value returned will be a string in JSON format. 0324 * 0325 * The caller of this function needs to provide only the first parameter, 0326 * which is an XML formatted String. The second parameter is optional, which 0327 * lets the user to select if the XML attributes in the input XML string 0328 * should be included or ignored in xml2json conversion. 0329 * 0330 * This function converts the XML formatted string into a PHP array by 0331 * calling a recursive (protected static) function in this class. Then, it 0332 * converts that PHP array into JSON by calling the "encode" static funcion. 0333 * 0334 * Throws a Zend_Json_Exception if the input not a XML formatted string. 0335 * NOTE: Encoding native javascript expressions via Zend_Json_Expr is not possible. 0336 * 0337 * @static 0338 * @access public 0339 * @param string $xmlStringContents XML String to be converted 0340 * @param boolean $ignoreXmlAttributes Include or exclude XML attributes in 0341 * the xml2json conversion process. 0342 * @return mixed - JSON formatted string on success 0343 * @throws Zend_Json_Exception 0344 */ 0345 public static function fromXml($xmlStringContents, $ignoreXmlAttributes=true) 0346 { 0347 // Load the XML formatted string into a Simple XML Element object. 0348 $simpleXmlElementObject = Zend_Xml_Security::scan($xmlStringContents); 0349 0350 // If it is not a valid XML content, throw an exception. 0351 if ($simpleXmlElementObject == null) { 0352 // require_once 'Zend/Json/Exception.php'; 0353 throw new Zend_Json_Exception('Function fromXml was called with an invalid XML formatted string.'); 0354 } // End of if ($simpleXmlElementObject == null) 0355 0356 $resultArray = null; 0357 0358 // Call the recursive function to convert the XML into a PHP array. 0359 $resultArray = self::_processXml($simpleXmlElementObject, $ignoreXmlAttributes); 0360 0361 // Convert the PHP array to JSON using Zend_Json encode method. 0362 // It is just that simple. 0363 $jsonStringOutput = self::encode($resultArray); 0364 return($jsonStringOutput); 0365 } 0366 0367 0368 0369 /** 0370 * Pretty-print JSON string 0371 * 0372 * Use 'format' option to select output format - currently html and txt supported, txt is default 0373 * Use 'indent' option to override the indentation string set in the format - by default for the 'txt' format it's a tab 0374 * 0375 * @param string $json Original JSON string 0376 * @param array $options Encoding options 0377 * @return string 0378 */ 0379 public static function prettyPrint($json, $options = array()) 0380 { 0381 $tokens = preg_split('|([\{\}\]\[,])|', $json, -1, PREG_SPLIT_DELIM_CAPTURE); 0382 $result = ''; 0383 $indent = 0; 0384 0385 $format= 'txt'; 0386 0387 $ind = "\t"; 0388 0389 if (isset($options['format'])) { 0390 $format = $options['format']; 0391 } 0392 0393 switch ($format) { 0394 case 'html': 0395 $lineBreak = '<br />'; 0396 $ind = ' '; 0397 break; 0398 default: 0399 case 'txt': 0400 $lineBreak = "\n"; 0401 $ind = "\t"; 0402 break; 0403 } 0404 0405 // override the defined indent setting with the supplied option 0406 if (isset($options['indent'])) { 0407 $ind = $options['indent']; 0408 } 0409 0410 $inLiteral = false; 0411 foreach($tokens as $token) { 0412 if($token == '') { 0413 continue; 0414 } 0415 0416 $prefix = str_repeat($ind, $indent); 0417 if (!$inLiteral && ($token == '{' || $token == '[')) { 0418 $indent++; 0419 if (($result != '') && ($result[(strlen($result)-1)] == $lineBreak)) { 0420 $result .= $prefix; 0421 } 0422 $result .= $token . $lineBreak; 0423 } elseif (!$inLiteral && ($token == '}' || $token == ']')) { 0424 $indent--; 0425 $prefix = str_repeat($ind, $indent); 0426 $result .= $lineBreak . $prefix . $token; 0427 } elseif (!$inLiteral && $token == ',') { 0428 $result .= $token . $lineBreak; 0429 } else { 0430 $result .= ( $inLiteral ? '' : $prefix ) . $token; 0431 0432 // Count # of unescaped double-quotes in token, subtract # of 0433 // escaped double-quotes and if the result is odd then we are 0434 // inside a string literal 0435 if ((substr_count($token, "\"")-substr_count($token, "\\\"")) % 2 != 0) { 0436 $inLiteral = !$inLiteral; 0437 } 0438 } 0439 } 0440 return $result; 0441 } 0442 }