File indexing completed on 2024-04-28 06:00:02

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 = '&nbsp;&nbsp;&nbsp;&nbsp;';
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 }