Added the zend framework 2 library, the path is specified in line no.26 in zend_modul...
[openemr.git] / interface / modules / zend_modules / library / Zend / Json / Encoder.php
blob190edc635a908099eecac636347a7071e28a6603
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\Json;
12 use Iterator;
13 use IteratorAggregate;
14 use ReflectionClass;
15 use Zend\Json\Exception\InvalidArgumentException;
16 use Zend\Json\Exception\RecursionException;
18 /**
19 * Encode PHP constructs to JSON
21 class Encoder
23 /**
24 * Whether or not to check for possible cycling
26 * @var bool
28 protected $cycleCheck;
30 /**
31 * Additional options used during encoding
33 * @var array
35 protected $options = array();
37 /**
38 * Array of visited objects; used to prevent cycling.
40 * @var array
42 protected $visited = array();
44 /**
45 * Constructor
47 * @param bool $cycleCheck Whether or not to check for recursion when encoding
48 * @param array $options Additional options used during encoding
49 * @return Encoder
51 protected function __construct($cycleCheck = false, $options = array())
53 $this->cycleCheck = $cycleCheck;
54 $this->options = $options;
57 /**
58 * Use the JSON encoding scheme for the value specified
60 * @param mixed $value The value to be encoded
61 * @param bool $cycleCheck Whether or not to check for possible object recursion when encoding
62 * @param array $options Additional options used during encoding
63 * @return string The encoded value
65 public static function encode($value, $cycleCheck = false, $options = array())
67 $encoder = new static(($cycleCheck) ? true : false, $options);
69 return $encoder->_encodeValue($value);
72 /**
73 * Recursive driver which determines the type of value to be encoded
74 * and then dispatches to the appropriate method. $values are either
75 * - objects (returns from {@link _encodeObject()})
76 * - arrays (returns from {@link _encodeArray()})
77 * - basic datums (e.g. numbers or strings) (returns from {@link _encodeDatum()})
79 * @param $value mixed The value to be encoded
80 * @return string Encoded value
82 protected function _encodeValue(&$value)
84 if (is_object($value)) {
85 return $this->_encodeObject($value);
86 } elseif (is_array($value)) {
87 return $this->_encodeArray($value);
90 return $this->_encodeDatum($value);
94 /**
95 * Encode an object to JSON by encoding each of the public properties
97 * A special property is added to the JSON object called '__className'
98 * that contains the name of the class of $value. This is used to decode
99 * the object on the client into a specific class.
101 * @param $value object
102 * @return string
103 * @throws RecursionException If recursive checks are enabled and the
104 * object has been serialized previously
106 protected function _encodeObject(&$value)
108 if ($this->cycleCheck) {
109 if ($this->_wasVisited($value)) {
111 if (isset($this->options['silenceCyclicalExceptions'])
112 && $this->options['silenceCyclicalExceptions']===true) {
114 return '"* RECURSION (' . str_replace('\\', '\\\\', get_class($value)) . ') *"';
116 } else {
117 throw new RecursionException(
118 'Cycles not supported in JSON encoding, cycle introduced by '
119 . 'class "' . get_class($value) . '"'
124 $this->visited[] = $value;
127 $props = '';
129 if (method_exists($value, 'toJson')) {
130 $props =',' . preg_replace("/^\{(.*)\}$/","\\1", $value->toJson());
131 } else {
132 if ($value instanceof IteratorAggregate) {
133 $propCollection = $value->getIterator();
134 } elseif ($value instanceof Iterator) {
135 $propCollection = $value;
136 } else {
137 $propCollection = get_object_vars($value);
140 foreach ($propCollection as $name => $propValue) {
141 if (isset($propValue)) {
142 $props .= ','
143 . $this->_encodeValue($name)
144 . ':'
145 . $this->_encodeValue($propValue);
150 $className = get_class($value);
151 return '{"__className":'
152 . $this->_encodeString($className)
153 . $props . '}';
158 * Determine if an object has been serialized already
160 * @param mixed $value
161 * @return bool
163 protected function _wasVisited(&$value)
165 if (in_array($value, $this->visited, true)) {
166 return true;
169 return false;
174 * JSON encode an array value
176 * Recursively encodes each value of an array and returns a JSON encoded
177 * array string.
179 * Arrays are defined as integer-indexed arrays starting at index 0, where
180 * the last index is (count($array) -1); any deviation from that is
181 * considered an associative array, and will be encoded as such.
183 * @param $array array
184 * @return string
186 protected function _encodeArray(&$array)
188 $tmpArray = array();
190 // Check for associative array
191 if (!empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
192 // Associative array
193 $result = '{';
194 foreach ($array as $key => $value) {
195 $key = (string) $key;
196 $tmpArray[] = $this->_encodeString($key)
197 . ':'
198 . $this->_encodeValue($value);
200 $result .= implode(',', $tmpArray);
201 $result .= '}';
202 } else {
203 // Indexed array
204 $result = '[';
205 $length = count($array);
206 for ($i = 0; $i < $length; $i++) {
207 $tmpArray[] = $this->_encodeValue($array[$i]);
209 $result .= implode(',', $tmpArray);
210 $result .= ']';
213 return $result;
218 * JSON encode a basic data type (string, number, boolean, null)
220 * If value type is not a string, number, boolean, or null, the string
221 * 'null' is returned.
223 * @param mixed $value
224 * @return string
226 protected function _encodeDatum(&$value)
228 $result = 'null';
230 if (is_int($value) || is_float($value)) {
231 $result = (string) $value;
232 $result = str_replace(',', '.', $result);
233 } elseif (is_string($value)) {
234 $result = $this->_encodeString($value);
235 } elseif (is_bool($value)) {
236 $result = $value ? 'true' : 'false';
239 return $result;
244 * JSON encode a string value by escaping characters as necessary
246 * @param string $string
247 * @return string
249 protected function _encodeString(&$string)
251 // Escape these characters with a backslash or unicode escape:
252 // " \ / \n \r \t \b \f
253 $search = array('\\', "\n", "\t", "\r", "\b", "\f", '"', '\'', '&', '<', '>', '/');
254 $replace = array('\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\\u0022', '\\u0027', '\\u0026', '\\u003C', '\\u003E', '\\/');
255 $string = str_replace($search, $replace, $string);
257 // Escape certain ASCII characters:
258 // 0x08 => \b
259 // 0x0c => \f
260 $string = str_replace(array(chr(0x08), chr(0x0C)), array('\b', '\f'), $string);
261 $string = self::encodeUnicodeString($string);
263 return '"' . $string . '"';
268 * Encode the constants associated with the ReflectionClass
269 * parameter. The encoding format is based on the class2 format
271 * @param ReflectionClass $cls
272 * @return string Encoded constant block in class2 format
274 private static function _encodeConstants(ReflectionClass $cls)
276 $result = "constants : {";
277 $constants = $cls->getConstants();
279 $tmpArray = array();
280 if (!empty($constants)) {
281 foreach ($constants as $key => $value) {
282 $tmpArray[] = "$key: " . self::encode($value);
285 $result .= implode(', ', $tmpArray);
288 return $result . "}";
293 * Encode the public methods of the ReflectionClass in the
294 * class2 format
296 * @param ReflectionClass $cls
297 * @return string Encoded method fragment
300 private static function _encodeMethods(ReflectionClass $cls)
302 $methods = $cls->getMethods();
303 $result = 'methods:{';
305 $started = false;
306 foreach ($methods as $method) {
307 if (! $method->isPublic() || !$method->isUserDefined()) {
308 continue;
311 if ($started) {
312 $result .= ',';
314 $started = true;
316 $result .= '' . $method->getName(). ':function(';
318 if ('__construct' != $method->getName()) {
319 $parameters = $method->getParameters();
320 $paramCount = count($parameters);
321 $argsStarted = false;
323 $argNames = "var argNames=[";
324 foreach ($parameters as $param) {
325 if ($argsStarted) {
326 $result .= ',';
329 $result .= $param->getName();
331 if ($argsStarted) {
332 $argNames .= ',';
335 $argNames .= '"' . $param->getName() . '"';
337 $argsStarted = true;
339 $argNames .= "];";
341 $result .= "){"
342 . $argNames
343 . 'var result = ZAjaxEngine.invokeRemoteMethod('
344 . "this, '" . $method->getName()
345 . "',argNames,arguments);"
346 . 'return(result);}';
347 } else {
348 $result .= "){}";
352 return $result . "}";
357 * Encode the public properties of the ReflectionClass in the class2
358 * format.
360 * @param ReflectionClass $cls
361 * @return string Encode properties list
364 private static function _encodeVariables(ReflectionClass $cls)
366 $properties = $cls->getProperties();
367 $propValues = get_class_vars($cls->getName());
368 $result = "variables:{";
369 $cnt = 0;
371 $tmpArray = array();
372 foreach ($properties as $prop) {
373 if (! $prop->isPublic()) {
374 continue;
377 $tmpArray[] = $prop->getName()
378 . ':'
379 . self::encode($propValues[$prop->getName()]);
381 $result .= implode(',', $tmpArray);
383 return $result . "}";
387 * Encodes the given $className into the class2 model of encoding PHP
388 * classes into JavaScript class2 classes.
389 * NOTE: Currently only public methods and variables are proxied onto
390 * the client machine
392 * @param $className string The name of the class, the class must be
393 * instantiable using a null constructor
394 * @param $package string Optional package name appended to JavaScript
395 * proxy class name
396 * @return string The class2 (JavaScript) encoding of the class
397 * @throws InvalidArgumentException
399 public static function encodeClass($className, $package = '')
401 $cls = new \ReflectionClass($className);
402 if (! $cls->isInstantiable()) {
403 throw new InvalidArgumentException("'{$className}' must be instantiable");
406 return "Class.create('$package$className',{"
407 . self::_encodeConstants($cls) .","
408 . self::_encodeMethods($cls) .","
409 . self::_encodeVariables($cls) .'});';
414 * Encode several classes at once
416 * Returns JSON encoded classes, using {@link encodeClass()}.
418 * @param array $classNames
419 * @param string $package
420 * @return string
422 public static function encodeClasses(array $classNames, $package = '')
424 $result = '';
425 foreach ($classNames as $className) {
426 $result .= static::encodeClass($className, $package);
429 return $result;
433 * Encode Unicode Characters to \u0000 ASCII syntax.
435 * This algorithm was originally developed for the
436 * Solar Framework by Paul M. Jones
438 * @link http://solarphp.com/
439 * @link http://svn.solarphp.com/core/trunk/Solar/JSON.php
440 * @param string $value
441 * @return string
443 public static function encodeUnicodeString($value)
445 $strlenVar = strlen($value);
446 $ascii = "";
449 * Iterate over every character in the string,
450 * escaping with a slash or encoding to UTF-8 where necessary
452 for ($i = 0; $i < $strlenVar; $i++) {
453 $ordVarC = ord($value[$i]);
455 switch (true) {
456 case (($ordVarC >= 0x20) && ($ordVarC <= 0x7F)):
457 // characters U-00000000 - U-0000007F (same as ASCII)
458 $ascii .= $value[$i];
459 break;
461 case (($ordVarC & 0xE0) == 0xC0):
462 // characters U-00000080 - U-000007FF, mask 110XXXXX
463 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
464 $char = pack('C*', $ordVarC, ord($value[$i + 1]));
465 $i += 1;
466 $utf16 = self::_utf82utf16($char);
467 $ascii .= sprintf('\u%04s', bin2hex($utf16));
468 break;
470 case (($ordVarC & 0xF0) == 0xE0):
471 // characters U-00000800 - U-0000FFFF, mask 1110XXXX
472 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
473 $char = pack('C*', $ordVarC,
474 ord($value[$i + 1]),
475 ord($value[$i + 2]));
476 $i += 2;
477 $utf16 = self::_utf82utf16($char);
478 $ascii .= sprintf('\u%04s', bin2hex($utf16));
479 break;
481 case (($ordVarC & 0xF8) == 0xF0):
482 // characters U-00010000 - U-001FFFFF, mask 11110XXX
483 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
484 $char = pack('C*', $ordVarC,
485 ord($value[$i + 1]),
486 ord($value[$i + 2]),
487 ord($value[$i + 3]));
488 $i += 3;
489 $utf16 = self::_utf82utf16($char);
490 $ascii .= sprintf('\u%04s', bin2hex($utf16));
491 break;
493 case (($ordVarC & 0xFC) == 0xF8):
494 // characters U-00200000 - U-03FFFFFF, mask 111110XX
495 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
496 $char = pack('C*', $ordVarC,
497 ord($value[$i + 1]),
498 ord($value[$i + 2]),
499 ord($value[$i + 3]),
500 ord($value[$i + 4]));
501 $i += 4;
502 $utf16 = self::_utf82utf16($char);
503 $ascii .= sprintf('\u%04s', bin2hex($utf16));
504 break;
506 case (($ordVarC & 0xFE) == 0xFC):
507 // characters U-04000000 - U-7FFFFFFF, mask 1111110X
508 // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
509 $char = pack('C*', $ordVarC,
510 ord($value[$i + 1]),
511 ord($value[$i + 2]),
512 ord($value[$i + 3]),
513 ord($value[$i + 4]),
514 ord($value[$i + 5]));
515 $i += 5;
516 $utf16 = self::_utf82utf16($char);
517 $ascii .= sprintf('\u%04s', bin2hex($utf16));
518 break;
522 return $ascii;
526 * Convert a string from one UTF-8 char to one UTF-16 char.
528 * Normally should be handled by mb_convert_encoding, but
529 * provides a slower PHP-only method for installations
530 * that lack the multibyte string extension.
532 * This method is from the Solar Framework by Paul M. Jones
534 * @link http://solarphp.com
535 * @param string $utf8 UTF-8 character
536 * @return string UTF-16 character
538 protected static function _utf82utf16($utf8)
540 // Check for mb extension otherwise do by hand.
541 if (function_exists('mb_convert_encoding')) {
542 return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
545 switch (strlen($utf8)) {
546 case 1:
547 // this case should never be reached, because we are in ASCII range
548 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
549 return $utf8;
551 case 2:
552 // return a UTF-16 character from a 2-byte UTF-8 char
553 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
554 return chr(0x07 & (ord($utf8{0}) >> 2))
555 . chr((0xC0 & (ord($utf8{0}) << 6))
556 | (0x3F & ord($utf8{1})));
558 case 3:
559 // return a UTF-16 character from a 3-byte UTF-8 char
560 // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
561 return chr((0xF0 & (ord($utf8{0}) << 4))
562 | (0x0F & (ord($utf8{1}) >> 2)))
563 . chr((0xC0 & (ord($utf8{1}) << 6))
564 | (0x7F & ord($utf8{2})));
567 // ignoring UTF-32 for now, sorry
568 return '';