PageRenderTime 29ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/DevApp/library/ServerLibraries/ZendFramework/1.7/library/Zend/Json/Encoder.php

http://firephp.googlecode.com/
PHP | 438 lines | 204 code | 67 blank | 167 comment | 29 complexity | 31da4d12c03b51102993865093682c25 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  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.
  14. *
  15. * @category Zend
  16. * @package Zend_Json
  17. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * Zend_Json_Exception
  22. */
  23. require_once 'Zend/Json/Exception.php';
  24. /**
  25. * Encode PHP constructs to JSON
  26. *
  27. * @category Zend
  28. * @package Zend_Json
  29. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. */
  32. class Zend_Json_Encoder
  33. {
  34. /**
  35. * Whether or not to check for possible cycling
  36. *
  37. * @var boolean
  38. */
  39. protected $_cycleCheck;
  40. /**
  41. * Additional options used during encoding
  42. *
  43. * @var array
  44. */
  45. protected $_options = array();
  46. /**
  47. * Array of visited objects; used to prevent cycling.
  48. *
  49. * @var array
  50. */
  51. protected $_visited = array();
  52. /**
  53. * Constructor
  54. *
  55. * @param boolean $cycleCheck Whether or not to check for recursion when encoding
  56. * @param array $options Additional options used during encoding
  57. * @return void
  58. */
  59. protected function __construct($cycleCheck = false, $options = array())
  60. {
  61. $this->_cycleCheck = $cycleCheck;
  62. $this->_options = $options;
  63. }
  64. /**
  65. * Use the JSON encoding scheme for the value specified
  66. *
  67. * @param mixed $value The value to be encoded
  68. * @param boolean $cycleCheck Whether or not to check for possible object recursion when encoding
  69. * @param array $options Additional options used during encoding
  70. * @return string The encoded value
  71. */
  72. public static function encode($value, $cycleCheck = false, $options = array())
  73. {
  74. $encoder = new self(($cycleCheck) ? true : false, $options);
  75. return $encoder->_encodeValue($value);
  76. }
  77. /**
  78. * Recursive driver which determines the type of value to be encoded
  79. * and then dispatches to the appropriate method. $values are either
  80. * - objects (returns from {@link _encodeObject()})
  81. * - arrays (returns from {@link _encodeArray()})
  82. * - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
  83. *
  84. * @param $value mixed The value to be encoded
  85. * @return string Encoded value
  86. */
  87. protected function _encodeValue(&$value)
  88. {
  89. if (is_object($value)) {
  90. return $this->_encodeObject($value);
  91. } else if (is_array($value)) {
  92. return $this->_encodeArray($value);
  93. }
  94. return $this->_encodeDatum($value);
  95. }
  96. /**
  97. * Encode an object to JSON by encoding each of the public properties
  98. *
  99. * A special property is added to the JSON object called '__className'
  100. * that contains the name of the class of $value. This is used to decode
  101. * the object on the client into a specific class.
  102. *
  103. * @param $value object
  104. * @return string
  105. * @throws Zend_Json_Exception If recursive checks are enabled and the object has been serialized previously
  106. */
  107. protected function _encodeObject(&$value)
  108. {
  109. if ($this->_cycleCheck) {
  110. if ($this->_wasVisited($value)) {
  111. if (isset($this->_options['silenceCyclicalExceptions'])
  112. && $this->_options['silenceCyclicalExceptions']===true) {
  113. return '"* RECURSION (' . get_class($value) . ') *"';
  114. } else {
  115. throw new Zend_Json_Exception(
  116. 'Cycles not supported in JSON encoding, cycle introduced by '
  117. . 'class "' . get_class($value) . '"'
  118. );
  119. }
  120. }
  121. $this->_visited[] = $value;
  122. }
  123. $props = '';
  124. if ($value instanceof Iterator) {
  125. $propCollection = $value;
  126. } else {
  127. $propCollection = get_object_vars($value);
  128. }
  129. foreach ($propCollection as $name => $propValue) {
  130. if (isset($propValue)) {
  131. $props .= ','
  132. . $this->_encodeValue($name)
  133. . ':'
  134. . $this->_encodeValue($propValue);
  135. }
  136. }
  137. return '{"__className":"' . get_class($value) . '"'
  138. . $props . '}';
  139. }
  140. /**
  141. * Determine if an object has been serialized already
  142. *
  143. * @param mixed $value
  144. * @return boolean
  145. */
  146. protected function _wasVisited(&$value)
  147. {
  148. if (in_array($value, $this->_visited, true)) {
  149. return true;
  150. }
  151. return false;
  152. }
  153. /**
  154. * JSON encode an array value
  155. *
  156. * Recursively encodes each value of an array and returns a JSON encoded
  157. * array string.
  158. *
  159. * Arrays are defined as integer-indexed arrays starting at index 0, where
  160. * the last index is (count($array) -1); any deviation from that is
  161. * considered an associative array, and will be encoded as such.
  162. *
  163. * @param $array array
  164. * @return string
  165. */
  166. protected function _encodeArray(&$array)
  167. {
  168. $tmpArray = array();
  169. // Check for associative array
  170. if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
  171. // Associative array
  172. $result = '{';
  173. foreach ($array as $key => $value) {
  174. $key = (string) $key;
  175. $tmpArray[] = $this->_encodeString($key)
  176. . ':'
  177. . $this->_encodeValue($value);
  178. }
  179. $result .= implode(',', $tmpArray);
  180. $result .= '}';
  181. } else {
  182. // Indexed array
  183. $result = '[';
  184. $length = count($array);
  185. for ($i = 0; $i < $length; $i++) {
  186. $tmpArray[] = $this->_encodeValue($array[$i]);
  187. }
  188. $result .= implode(',', $tmpArray);
  189. $result .= ']';
  190. }
  191. return $result;
  192. }
  193. /**
  194. * JSON encode a basic data type (string, number, boolean, null)
  195. *
  196. * If value type is not a string, number, boolean, or null, the string
  197. * 'null' is returned.
  198. *
  199. * @param $value mixed
  200. * @return string
  201. */
  202. protected function _encodeDatum(&$value)
  203. {
  204. $result = 'null';
  205. if (is_int($value) || is_float($value)) {
  206. $result = (string) $value;
  207. } elseif (is_string($value)) {
  208. $result = $this->_encodeString($value);
  209. } elseif (is_bool($value)) {
  210. $result = $value ? 'true' : 'false';
  211. }
  212. return $result;
  213. }
  214. /**
  215. * JSON encode a string value by escaping characters as necessary
  216. *
  217. * @param $value string
  218. * @return string
  219. */
  220. protected function _encodeString(&$string)
  221. {
  222. // Escape these characters with a backslash:
  223. // " \ / \n \r \t \b \f
  224. $search = array('\\', "\n", "\t", "\r", "\b", "\f", '"', '/');
  225. $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\"', '\\/');
  226. $string = str_replace($search, $replace, $string);
  227. // Escape certain ASCII characters:
  228. // 0x08 => \b
  229. // 0x0c => \f
  230. $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
  231. return '"' . $string . '"';
  232. }
  233. /**
  234. * Encode the constants associated with the ReflectionClass
  235. * parameter. The encoding format is based on the class2 format
  236. *
  237. * @param $cls ReflectionClass
  238. * @return string Encoded constant block in class2 format
  239. */
  240. private static function _encodeConstants(ReflectionClass $cls)
  241. {
  242. $result = "constants : {";
  243. $constants = $cls->getConstants();
  244. $tmpArray = array();
  245. if (!empty($constants)) {
  246. foreach ($constants as $key => $value) {
  247. $tmpArray[] = "$key: " . self::encode($value);
  248. }
  249. $result .= implode(', ', $tmpArray);
  250. }
  251. return $result . "}";
  252. }
  253. /**
  254. * Encode the public methods of the ReflectionClass in the
  255. * class2 format
  256. *
  257. * @param $cls ReflectionClass
  258. * @return string Encoded method fragment
  259. *
  260. */
  261. private static function _encodeMethods(ReflectionClass $cls)
  262. {
  263. $methods = $cls->getMethods();
  264. $result = 'methods:{';
  265. $started = false;
  266. foreach ($methods as $method) {
  267. if (! $method->isPublic() || !$method->isUserDefined()) {
  268. continue;
  269. }
  270. if ($started) {
  271. $result .= ',';
  272. }
  273. $started = true;
  274. $result .= '' . $method->getName(). ':function(';
  275. if ('__construct' != $method->getName()) {
  276. $parameters = $method->getParameters();
  277. $paramCount = count($parameters);
  278. $argsStarted = false;
  279. $argNames = "var argNames=[";
  280. foreach ($parameters as $param) {
  281. if ($argsStarted) {
  282. $result .= ',';
  283. }
  284. $result .= $param->getName();
  285. if ($argsStarted) {
  286. $argNames .= ',';
  287. }
  288. $argNames .= '"' . $param->getName() . '"';
  289. $argsStarted = true;
  290. }
  291. $argNames .= "];";
  292. $result .= "){"
  293. . $argNames
  294. . 'var result = ZAjaxEngine.invokeRemoteMethod('
  295. . "this, '" . $method->getName()
  296. . "',argNames,arguments);"
  297. . 'return(result);}';
  298. } else {
  299. $result .= "){}";
  300. }
  301. }
  302. return $result . "}";
  303. }
  304. /**
  305. * Encode the public properties of the ReflectionClass in the class2
  306. * format.
  307. *
  308. * @param $cls ReflectionClass
  309. * @return string Encode properties list
  310. *
  311. */
  312. private static function _encodeVariables(ReflectionClass $cls)
  313. {
  314. $properties = $cls->getProperties();
  315. $propValues = get_class_vars($cls->getName());
  316. $result = "variables:{";
  317. $cnt = 0;
  318. $tmpArray = array();
  319. foreach ($properties as $prop) {
  320. if (! $prop->isPublic()) {
  321. continue;
  322. }
  323. $tmpArray[] = $prop->getName()
  324. . ':'
  325. . self::encode($propValues[$prop->getName()]);
  326. }
  327. $result .= implode(',', $tmpArray);
  328. return $result . "}";
  329. }
  330. /**
  331. * Encodes the given $className into the class2 model of encoding PHP
  332. * classes into JavaScript class2 classes.
  333. * NOTE: Currently only public methods and variables are proxied onto
  334. * the client machine
  335. *
  336. * @param $className string The name of the class, the class must be
  337. * instantiable using a null constructor
  338. * @param $package string Optional package name appended to JavaScript
  339. * proxy class name
  340. * @return string The class2 (JavaScript) encoding of the class
  341. * @throws Zend_Json_Exception
  342. */
  343. public static function encodeClass($className, $package = '')
  344. {
  345. $cls = new ReflectionClass($className);
  346. if (! $cls->isInstantiable()) {
  347. throw new Zend_Json_Exception("$className must be instantiable");
  348. }
  349. return "Class.create('$package$className',{"
  350. . self::_encodeConstants($cls) .","
  351. . self::_encodeMethods($cls) .","
  352. . self::_encodeVariables($cls) .'});';
  353. }
  354. /**
  355. * Encode several classes at once
  356. *
  357. * Returns JSON encoded classes, using {@link encodeClass()}.
  358. *
  359. * @param array $classNames
  360. * @param string $package
  361. * @return string
  362. */
  363. public static function encodeClasses(array $classNames, $package = '')
  364. {
  365. $result = '';
  366. foreach ($classNames as $className) {
  367. $result .= self::encodeClass($className, $package);
  368. }
  369. return $result;
  370. }
  371. }