PK l}P
LICENSE.mdnu W+A Copyright (c) 2019-2020, Laminas Foundation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Laminas Foundation nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK l}P%'\ \ COPYRIGHT.mdnu W+A Copyright (c) 2019-2020, Laminas Foundation.
All rights reserved. (https://getlaminas.org/)
PK l}P- .travis.ymlnu W+A language: php
cache:
directories:
- $HOME/.composer/cache
env:
global:
- COMPOSER_ARGS="--no-interaction"
- COVERAGE_DEPS="php-coveralls/php-coveralls"
matrix:
fast_finish: true
include:
- php: 5.6
env:
- DEPS=lowest
- php: 5.6
env:
- DEPS=latest
- php: 7
env:
- DEPS=lowest
- php: 7
env:
- DEPS=latest
- php: 7.1
env:
- DEPS=lowest
- php: 7.1
env:
- DEPS=latest
- CS_CHECK=true
- TEST_COVERAGE=true
- php: 7.2
env:
- DEPS=lowest
- php: 7.2
env:
- DEPS=latest
- php: 7.3
env:
- DEPS=lowest
- php: 7.3
env:
- DEPS=latest
before_install:
- if [[ $TEST_COVERAGE != 'true' ]]; then phpenv config-rm xdebug.ini || return 0 ; fi
install:
- travis_retry composer install $COMPOSER_ARGS --ignore-platform-reqs
- if [[ $LEGACY_DEPS != '' ]]; then travis_retry composer update $COMPOSER_ARGS --with-dependencies $LEGACY_DEPS ; fi
- if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi
- if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi
- if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi
- stty cols 120 && composer show
script:
- if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi
- if [[ $CS_CHECK == 'true' ]]; then composer cs-check ; fi
after_script:
- if [[ $TEST_COVERAGE == 'true' ]]; then vendor/bin/php-coveralls -v ; fi
notifications:
email: false
PK l}Px= = .coveralls.ymlnu W+A coverage_clover: clover.xml
json_path: coveralls-upload.json
PK l}P= phpcs.xmlnu W+A
src
test
PK l}PT\ \ README.mdnu W+A # laminas-json
[![Build Status](https://travis-ci.com/laminas/laminas-json.svg?branch=master)](https://travis-ci.com/laminas/laminas-json)
[![Coverage Status](https://coveralls.io/repos/github/laminas/laminas-json/badge.svg?branch=master)](https://coveralls.io/github/laminas/laminas-json?branch=master)
`Laminas\Json` provides convenience methods for serializing native PHP to JSON and
decoding JSON to native PHP. For more information on JSON, visit the JSON
[project site](http://www.json.org/).
## Installation
Run the following to install this library:
```bash
$ composer require laminas/laminas-json
```
## Documentation
Browse the documentation online at https://docs.laminas.dev/laminas-json/
## Support
* [Issues](https://github.com/laminas/laminas-json/issues/)
* [Chat](https://laminas.dev/chat/)
* [Forum](https://discourse.laminas.dev/)
PK l}P@ݫ
.gitignorenu W+A /clover.xml
/composer.lock
/coveralls-upload.json
/docs/html/
/laminas-mkdoc-theme.tgz
/laminas-mkdoc-theme/
/phpunit.xml
/vendor/
PK l}PRR " src/Exception/RuntimeException.phpnu W+A = 0x20) && ($ordChrsC <= 0x7F):
$utf8 .= $chrs[$i];
break;
case ($ordChrsC & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $i, 2);
++$i;
break;
case ($ordChrsC & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $i, 3);
$i += 2;
break;
case ($ordChrsC & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $i, 4);
$i += 3;
break;
case ($ordChrsC & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $i, 5);
$i += 4;
break;
case ($ordChrsC & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $i, 6);
$i += 5;
break;
}
}
return $utf8;
}
/**
* Constructor
*
* @param string $source String source to decode
* @param int $decodeType How objects should be decoded -- see
* {@link Json::TYPE_ARRAY} and {@link Json::TYPE_OBJECT} for * valid
* values
* @throws InvalidArgumentException
*/
protected function __construct($source, $decodeType)
{
// Set defaults
$this->source = self::decodeUnicodeString($source);
$this->sourceLength = strlen($this->source);
$this->token = self::EOF;
$this->offset = 0;
switch ($decodeType) {
case Json::TYPE_ARRAY:
case Json::TYPE_OBJECT:
$this->decodeType = $decodeType;
break;
default:
throw new InvalidArgumentException(sprintf(
'Unknown decode type "%s", please use one of the Json::TYPE_* constants',
$decodeType
));
}
// Set pointer at first token
$this->getNextToken();
}
/**
* Decode a JSON source string.
*
* Decodes a JSON encoded string; the value returned will be one of the
* following:
*
* - integer
* - float
* - boolean
* - null
* - stdClass
* - array
* - array of one or more of the above types
*
* By default, decoded objects will be returned as a stdClass object;
* to return associative arrays instead, pass {@link Json::TYPE_ARRAY}
* to the $objectDecodeType parameter.
*
* @param string $source String to be decoded.
* @param int $objectDecodeType How objects should be decoded; should be
* either or {@link Json::TYPE_ARRAY} or {@link Json::TYPE_OBJECT};
* defaults to Json::TYPE_OBJECT.
* @return mixed
*/
public static function decode($source, $objectDecodeType = Json::TYPE_OBJECT)
{
$decoder = new static($source, $objectDecodeType);
return $decoder->decodeValue();
}
/**
* Recursive routine for supported toplevel types.
*
* @return mixed
*/
protected function decodeValue()
{
switch ($this->token) {
case self::DATUM:
$result = $this->tokenValue;
$this->getNextToken();
return($result);
case self::LBRACE:
return($this->decodeObject());
case self::LBRACKET:
return($this->decodeArray());
default:
return;
}
}
/**
* Decodes an object of the form { "attribute: value, "attribute2" : value, ... }
*
* If Laminas\Json\Encoder was used to encode the original object, then
* a special attribute called __className will specify a class
* name with which to wrap the data contained within the encoded source.
*
* Decodes to either an array or stdClass object, based on the value of
* {@link $decodeType}. If invalid $decodeType present, returns as an
* array.
*
* @return array|stdClass
* @throws RuntimeException
*/
protected function decodeObject()
{
$members = [];
$tok = $this->getNextToken();
while ($tok && $tok !== self::RBRACE) {
if ($tok !== self::DATUM || ! is_string($this->tokenValue)) {
throw new RuntimeException(sprintf('Missing key in object encoding: %s', $this->source));
}
$key = $this->tokenValue;
$tok = $this->getNextToken();
if ($tok !== self::COLON) {
throw new RuntimeException(sprintf('Missing ":" in object encoding: %s', $this->source));
}
$this->getNextToken();
$members[$key] = $this->decodeValue();
$tok = $this->token;
if ($tok === self::RBRACE) {
break;
}
if ($tok !== self::COMMA) {
throw new RuntimeException(sprintf('Missing "," in object encoding: %s', $this->source));
}
$tok = $this->getNextToken();
}
switch ($this->decodeType) {
case Json::TYPE_OBJECT:
// Create new stdClass and populate with $members
$result = new stdClass();
foreach ($members as $key => $value) {
if ($key === '') {
$key = '_empty_';
}
$result->$key = $value;
}
break;
case Json::TYPE_ARRAY:
// intentionally fall-through
default:
$result = $members;
break;
}
$this->getNextToken();
return $result;
}
/**
* Decodes the JSON array format [element, element2, ..., elementN]
*
* @return array
* @throws RuntimeException
*/
protected function decodeArray()
{
$result = [];
$tok = $this->getNextToken(); // Move past the '['
$index = 0;
while ($tok && $tok !== self::RBRACKET) {
$result[$index++] = $this->decodeValue();
$tok = $this->token;
if ($tok == self::RBRACKET || ! $tok) {
break;
}
if ($tok !== self::COMMA) {
throw new RuntimeException(sprintf('Missing "," in array encoding: %s', $this->source));
}
$tok = $this->getNextToken();
}
$this->getNextToken();
return $result;
}
/**
* Removes whitespace characters from the source input.
*/
protected function eatWhitespace()
{
if (preg_match('/([\t\b\f\n\r ])*/s', $this->source, $matches, PREG_OFFSET_CAPTURE, $this->offset)
&& $matches[0][1] == $this->offset
) {
$this->offset += strlen($matches[0][0]);
}
}
/**
* Retrieves the next token from the source stream.
*
* @return int Token constant value specified in class definition.
* @throws RuntimeException
*/
protected function getNextToken()
{
$this->token = self::EOF;
$this->tokenValue = null;
$this->eatWhitespace();
if ($this->offset >= $this->sourceLength) {
return(self::EOF);
}
$str = $this->source;
$strLength = $this->sourceLength;
$i = $this->offset;
$start = $i;
switch ($str[$i]) {
case '{':
$this->token = self::LBRACE;
break;
case '}':
$this->token = self::RBRACE;
break;
case '[':
$this->token = self::LBRACKET;
break;
case ']':
$this->token = self::RBRACKET;
break;
case ',':
$this->token = self::COMMA;
break;
case ':':
$this->token = self::COLON;
break;
case '"':
$result = '';
do {
$i++;
if ($i >= $strLength) {
break;
}
$chr = $str[$i];
if ($chr === '"') {
break;
}
if ($chr !== '\\') {
$result .= $chr;
continue;
}
$i++;
if ($i >= $strLength) {
break;
}
$chr = $str[$i];
switch ($chr) {
case '"':
$result .= '"';
break;
case '\\':
$result .= '\\';
break;
case '/':
$result .= '/';
break;
case 'b':
$result .= "\x08";
break;
case 'f':
$result .= "\x0c";
break;
case 'n':
$result .= "\x0a";
break;
case 'r':
$result .= "\x0d";
break;
case 't':
$result .= "\x09";
break;
case '\'':
$result .= '\'';
break;
default:
throw new RuntimeException(sprintf('Illegal escape sequence "%s"', $chr));
}
} while ($i < $strLength);
$this->token = self::DATUM;
$this->tokenValue = $result;
break;
case 't':
if (($i + 3) < $strLength && $start === strpos($str, "true", $start)) {
$this->token = self::DATUM;
}
$this->tokenValue = true;
$i += 3;
break;
case 'f':
if (($i + 4) < $strLength && $start === strpos($str, "false", $start)) {
$this->token = self::DATUM;
}
$this->tokenValue = false;
$i += 4;
break;
case 'n':
if (($i + 3) < $strLength && $start === strpos($str, "null", $start)) {
$this->token = self::DATUM;
}
$this->tokenValue = null;
$i += 3;
break;
}
if ($this->token !== self::EOF) {
$this->offset = $i + 1; // Consume the last token character
return ($this->token);
}
$chr = $str[$i];
if ($chr !== '-' && $chr !== '.' && ($chr < '0' || $chr > '9')) {
throw new RuntimeException('Illegal Token');
}
if (preg_match('/-?([0-9])*(\.[0-9]*)?((e|E)((-|\+)?)[0-9]+)?/s', $str, $matches, PREG_OFFSET_CAPTURE, $start)
&& $matches[0][1] == $start
) {
$datum = $matches[0][0];
if (! is_numeric($datum)) {
throw new RuntimeException(sprintf('Illegal number format: %s', $datum));
}
if (preg_match('/^0\d+$/', $datum)) {
throw new RuntimeException(sprintf('Octal notation not supported by JSON (value: %o)', $datum));
}
$val = intval($datum);
$fVal = floatval($datum);
$this->tokenValue = ($val == $fVal ? $val : $fVal);
$this->token = self::DATUM;
$this->offset = $start + strlen($datum);
}
return $this->token;
}
/**
* Convert a string from one UTF-16 char to one UTF-8 char.
*
* Normally should be handled by mb_convert_encoding, but provides a slower
* PHP-only method for installations that lack the multibyte string
* extension.
*
* This method is from the Solar Framework by Paul M. Jones.
*
* @link http://solarphp.com
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
*/
protected static function utf162utf8($utf16)
{
// Check for mb extension otherwise do by hand.
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
$bytes = (ord($utf16[0]) << 8) | ord($utf16[1]);
switch (true) {
case ((0x7F & $bytes) == $bytes):
// This case should never be reached, because we are in ASCII range;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
case (0x07FF & $bytes) == $bytes:
// Return a 2-byte UTF-8 character;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
case (0xFFFF & $bytes) == $bytes:
// Return a 3-byte UTF-8 character;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
// ignoring UTF-32 for now, sorry
return '';
}
}
PK l}Pdg g src/Expr.phpnu W+A
* $foo = array(
* 'integer' => 9,
* 'string' => 'test string',
* 'function' => Laminas\Json\Expr(
* 'function () { window.alert("javascript function encoded by Laminas\Json\Json") }'
* ),
* );
*
* echo Laminas\Json\Json::encode($foo, false, ['enableJsonExprFinder' => true]);
*
*
* The above returns the following JSON (formatted for readability):
*
*
* {
* "integer": 9,
* "string": "test string",
* "function": function () {window.alert("javascript function encoded by Laminas\Json\Json")}
* }
*
*/
class Expr
{
/**
* Storage for javascript expression.
*
* @var string
*/
protected $expression;
/**
* @param string $expression The expression to represent.
*/
public function __construct($expression)
{
$this->expression = (string) $expression;
}
/**
* Cast to string
*
* @return string holded javascript expression.
*/
public function __toString()
{
return $this->expression;
}
}
PK l}P4I I src/Encoder.phpnu W+A cycleCheck = $cycleCheck;
$this->options = $options;
}
/**
* Use the JSON encoding scheme for the value specified.
*
* @param mixed $value The value to be encoded.
* @param bool $cycleCheck Whether or not to check for possible object recursion when encoding.
* @param array $options Additional options used during encoding.
* @return string The encoded value.
*/
public static function encode($value, $cycleCheck = false, array $options = [])
{
$encoder = new static($cycleCheck, $options);
if ($value instanceof JsonSerializable) {
$value = $value->jsonSerialize();
}
return $encoder->encodeValue($value);
}
/**
* Encode a value to JSON.
*
* Recursive method which determines the type of value to be encoded
* and then dispatches to the appropriate method.
*
* $values are either
* - objects (returns from {@link encodeObject()})
* - arrays (returns from {@link encodeArray()})
* - scalars (returns from {@link encodeDatum()})
*
* @param $value mixed The value to be encoded.
* @return string Encoded value.
*/
protected function encodeValue(&$value)
{
if (is_object($value)) {
return $this->encodeObject($value);
}
if (is_array($value)) {
return $this->encodeArray($value);
}
return $this->encodeDatum($value);
}
/**
* Encode an object to JSON by encoding each of the public properties.
*
* A special property is added to the JSON object called '__className' that
* contains the classname of $value; this can be used by consumers of the
* resulting JSON to cast to the specific class.
*
* @param $value object
* @return string
* @throws RecursionException If recursive checks are enabled and the
* object has been serialized previously.
*/
protected function encodeObject(&$value)
{
if ($this->cycleCheck) {
if ($this->wasVisited($value)) {
if (! isset($this->options['silenceCyclicalExceptions'])
|| $this->options['silenceCyclicalExceptions'] !== true
) {
throw new RecursionException(sprintf(
'Cycles not supported in JSON encoding; cycle introduced by class "%s"',
get_class($value)
));
}
return '"* RECURSION (' . str_replace('\\', '\\\\', get_class($value)) . ') *"';
}
$this->visited[] = $value;
}
$props = '';
if (method_exists($value, 'toJson')) {
$props = ',' . preg_replace("/^\{(.*)\}$/", "\\1", $value->toJson());
} else {
if ($value instanceof IteratorAggregate) {
$propCollection = $value->getIterator();
} elseif ($value instanceof Iterator) {
$propCollection = $value;
} else {
$propCollection = get_object_vars($value);
}
foreach ($propCollection as $name => $propValue) {
if (! isset($propValue)) {
continue;
}
$props .= ','
. $this->encodeValue($name)
. ':'
. $this->encodeValue($propValue);
}
}
$className = get_class($value);
return '{"__className":'
. $this->encodeString($className)
. $props . '}';
}
/**
* Determine if an object has been serialized already.
*
* @param mixed $value
* @return bool
*/
protected function wasVisited(&$value)
{
if (in_array($value, $this->visited, true)) {
return true;
}
return false;
}
/**
* JSON encode an array value.
*
* Recursively encodes each value of an array and returns a JSON encoded
* array string.
*
* Arrays are defined as integer-indexed arrays starting at index 0, where
* the last index is (count($array) -1); any deviation from that is
* considered an associative array, and will be passed to
* {@link encodeAssociativeArray()}.
*
* @param $array array
* @return string
*/
protected function encodeArray($array)
{
// Check for associative array
if (! empty($array) && (array_keys($array) !== range(0, count($array) - 1))) {
// Associative array
return $this->encodeAssociativeArray($array);
}
// Indexed array
$tmpArray = [];
$result = '[';
$length = count($array);
for ($i = 0; $i < $length; $i++) {
$tmpArray[] = $this->encodeValue($array[$i]);
}
$result .= implode(',', $tmpArray);
$result .= ']';
return $result;
}
/**
* Encode an associative array to JSON.
*
* JSON does not have a concept of associative arrays; as such, we encode
* them to objects.
*
* @param array $array Array to encode.
* @return string
*/
protected function encodeAssociativeArray($array)
{
$tmpArray = [];
$result = '{';
foreach ($array as $key => $value) {
$tmpArray[] = sprintf(
'%s:%s',
$this->encodeString((string) $key),
$this->encodeValue($value)
);
}
$result .= implode(',', $tmpArray);
$result .= '}';
return $result;
}
/**
* JSON encode a scalar data type (string, number, boolean, null).
*
* If value type is not a string, number, boolean, or null, the string
* 'null' is returned.
*
* @param mixed $value
* @return string
*/
protected function encodeDatum($value)
{
if (is_int($value) || is_float($value)) {
return str_replace(',', '.', (string) $value);
}
if (is_string($value)) {
return $this->encodeString($value);
}
if (is_bool($value)) {
return $value ? 'true' : 'false';
}
return 'null';
}
/**
* JSON encode a string value by escaping characters as necessary.
*
* @param string $string
* @return string
*/
protected function encodeString($string)
{
// @codingStandardsIgnoreStart
// Escape these characters with a backslash or unicode escape:
// " \ / \n \r \t \b \f
$search = ['\\', "\n", "\t", "\r", "\b", "\f", '"', '\'', '&', '<', '>', '/'];
$replace = ['\\\\', '\\n', '\\t', '\\r', '\\b', '\\f', '\\u0022', '\\u0027', '\\u0026', '\\u003C', '\\u003E', '\\/'];
$string = str_replace($search, $replace, $string);
// @codingStandardsIgnoreEnd
// Escape certain ASCII characters:
// 0x08 => \b
// 0x0c => \f
$string = str_replace([chr(0x08), chr(0x0C)], ['\b', '\f'], $string);
$string = self::encodeUnicodeString($string);
return '"' . $string . '"';
}
/**
* Encode the constants associated with the ReflectionClass parameter.
*
* The encoding format is based on the class2 format.
*
* @param ReflectionClass $class
* @return string Encoded constant block in class2 format
*/
private static function encodeConstants(ReflectionClass $class)
{
$result = "constants:{";
$constants = $class->getConstants();
if (empty($constants)) {
return $result . '}';
}
$tmpArray = [];
foreach ($constants as $key => $value) {
$tmpArray[] = sprintf('%s: %s', $key, self::encode($value));
}
$result .= implode(', ', $tmpArray);
return $result . "}";
}
/**
* Encode the public methods of the ReflectionClass in the class2 format
*
* @param ReflectionClass $class
* @return string Encoded method fragment.
*/
private static function encodeMethods(ReflectionClass $class)
{
$result = 'methods:{';
$started = false;
foreach ($class->getMethods() as $method) {
if (! $method->isPublic() || ! $method->isUserDefined()) {
continue;
}
if ($started) {
$result .= ',';
}
$started = true;
$result .= sprintf('%s:function(', $method->getName());
if ('__construct' === $method->getName()) {
$result .= '){}';
continue;
}
$argsStarted = false;
$argNames = "var argNames=[";
foreach ($method->getParameters() as $param) {
if ($argsStarted) {
$result .= ',';
}
$result .= $param->getName();
if ($argsStarted) {
$argNames .= ',';
}
$argNames .= sprintf('"%s"', $param->getName());
$argsStarted = true;
}
$argNames .= "];";
$result .= "){"
. $argNames
. 'var result = ZAjaxEngine.invokeRemoteMethod('
. "this, '"
. $method->getName()
. "',argNames,arguments);"
. 'return(result);}';
}
return $result . "}";
}
/**
* Encode the public properties of the ReflectionClass in the class2 format.
*
* @param ReflectionClass $class
* @return string Encode properties list
*
*/
private static function encodeVariables(ReflectionClass $class)
{
$propValues = get_class_vars($class->getName());
$result = "variables:{";
$tmpArray = [];
foreach ($class->getProperties() as $prop) {
if (! $prop->isPublic()) {
continue;
}
$name = $prop->getName();
$tmpArray[] = sprintf('%s:%s', $name, self::encode($propValues[$name]));
}
$result .= implode(',', $tmpArray);
return $result . "}";
}
/**
* Encodes the given $className into the class2 model of encoding PHP classes into JavaScript class2 classes.
*
* NOTE: Currently only public methods and variables are proxied onto the
* client machine
*
* @param $className string The name of the class, the class must be
* instantiable using a null constructor.
* @param $package string Optional package name appended to JavaScript
* proxy class name.
* @return string The class2 (JavaScript) encoding of the class.
* @throws InvalidArgumentException
*/
public static function encodeClass($className, $package = '')
{
$class = new ReflectionClass($className);
if (! $class->isInstantiable()) {
throw new InvalidArgumentException(sprintf(
'"%s" must be instantiable',
$className
));
}
return sprintf(
'Class.create(\'%s%s\',{%s,%s,%s});',
$package,
$className,
self::encodeConstants($class),
self::encodeMethods($class),
self::encodeVariables($class)
);
}
/**
* Encode several classes at once.
*
* Returns JSON encoded classes, using {@link encodeClass()}.
*
* @param string[] $classNames
* @param string $package
* @return string
*/
public static function encodeClasses(array $classNames, $package = '')
{
$result = '';
foreach ($classNames as $className) {
$result .= static::encodeClass($className, $package);
}
return $result;
}
/**
* Encode Unicode Characters to \u0000 ASCII syntax.
*
* This algorithm was originally developed for the Solar Framework by Paul
* M. Jones.
*
* @link http://solarphp.com/
* @link https://github.com/solarphp/core/blob/master/Solar/Json.php
* @param string $value
* @return string
*/
public static function encodeUnicodeString($value)
{
$strlenVar = strlen($value);
$ascii = "";
// Iterate over every character in the string, escaping with a slash or
// encoding to UTF-8 where necessary.
for ($i = 0; $i < $strlenVar; $i++) {
$ordVarC = ord($value[$i]);
switch (true) {
case (($ordVarC >= 0x20) && ($ordVarC <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $value[$i];
break;
case (($ordVarC & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ordVarC, ord($value[$i + 1]));
$i += 1;
$utf16 = self::utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ordVarC & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack(
'C*',
$ordVarC,
ord($value[$i + 1]),
ord($value[$i + 2])
);
$i += 2;
$utf16 = self::utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ordVarC & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack(
'C*',
$ordVarC,
ord($value[$i + 1]),
ord($value[$i + 2]),
ord($value[$i + 3])
);
$i += 3;
$utf16 = self::utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ordVarC & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack(
'C*',
$ordVarC,
ord($value[$i + 1]),
ord($value[$i + 2]),
ord($value[$i + 3]),
ord($value[$i + 4])
);
$i += 4;
$utf16 = self::utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
case (($ordVarC & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack(
'C*',
$ordVarC,
ord($value[$i + 1]),
ord($value[$i + 2]),
ord($value[$i + 3]),
ord($value[$i + 4]),
ord($value[$i + 5])
);
$i += 5;
$utf16 = self::utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
return $ascii;
}
/**
* Convert a string from one UTF-8 char to one UTF-16 char.
*
* Normally should be handled by mb_convert_encoding, but provides a slower
* PHP-only method for installations that lack the multibyte string
* extension.
*
* This method is from the Solar Framework by Paul M. Jones.
*
* @link http://solarphp.com
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
*/
protected static function utf82utf16($utf8)
{
// Check for mb extension otherwise do by hand.
if (function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
switch (strlen($utf8)) {
case 1:
// This case should never be reached, because we are in ASCII range;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
case 2:
// Return a UTF-16 character from a 2-byte UTF-8 char;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8[0]) >> 2)) . chr((0xC0 & (ord($utf8[0]) << 6)) | (0x3F & ord($utf8[1])));
case 3:
// Return a UTF-16 character from a 3-byte UTF-8 char;
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8[0]) << 4))
| (0x0F & (ord($utf8[1]) >> 2))) . chr((0xC0 & (ord($utf8[1]) << 6))
| (0x7F & ord($utf8[2])));
}
// ignoring UTF-32 for now, sorry
return '';
}
}
PK l}PXĺG6 G6 src/Json.phpnu W+A toJson();
}
if (method_exists($valueToEncode, 'toArray')) {
return static::encode($valueToEncode->toArray(), $cycleCheck, $options);
}
}
// Pre-process and replace javascript expressions with placeholders
$javascriptExpressions = new SplQueue();
if (isset($options['enableJsonExprFinder'])
&& $options['enableJsonExprFinder'] == true
) {
$valueToEncode = static::recursiveJsonExprFinder($valueToEncode, $javascriptExpressions);
}
// Encoding
$prettyPrint = (isset($options['prettyPrint']) && ($options['prettyPrint'] === true));
$encodedResult = self::encodeValue($valueToEncode, $cycleCheck, $options, $prettyPrint);
// Post-process to revert back any Laminas\Json\Expr instances.
$encodedResult = self::injectJavascriptExpressions($encodedResult, $javascriptExpressions);
return $encodedResult;
}
/**
* Discover and replace javascript expressions with temporary placeholders.
*
* Check each value to determine if it is a Laminas\Json\Expr; if so, replace the value with
* a magic key and add the javascript expression to the queue.
*
* NOTE this method is recursive.
*
* NOTE: This method is used internally by the encode method.
*
* @see encode
* @param mixed $value a string - object property to be encoded
* @param SplQueue $javascriptExpressions
* @param null|string|int $currentKey
* @return mixed
*/
protected static function recursiveJsonExprFinder(
$value,
SplQueue $javascriptExpressions,
$currentKey = null
) {
if ($value instanceof Expr) {
// TODO: Optimize with ascii keys, if performance is bad
$magicKey = "____" . $currentKey . "_" . (count($javascriptExpressions));
$javascriptExpressions->enqueue([
// If currentKey is integer, encodeUnicodeString call is not required.
'magicKey' => (is_int($currentKey)) ? $magicKey : Encoder::encodeUnicodeString($magicKey),
'value' => $value,
]);
return $magicKey;
}
if (is_array($value)) {
foreach ($value as $k => $v) {
$value[$k] = static::recursiveJsonExprFinder($value[$k], $javascriptExpressions, $k);
}
return $value;
}
if (is_object($value)) {
foreach ($value as $k => $v) {
$value->$k = static::recursiveJsonExprFinder($value->$k, $javascriptExpressions, $k);
}
return $value;
}
return $value;
}
/**
* Pretty-print JSON string
*
* Use 'indent' option to select indentation string; by default, four
* spaces are used.
*
* @param string $json Original JSON string
* @param array $options Encoding options
* @return string
*/
public static function prettyPrint($json, array $options = [])
{
$indentString = isset($options['indent']) ? $options['indent'] : ' ';
$json = trim($json);
$length = strlen($json);
$stack = [];
$result = '';
$inLiteral = false;
for ($i = 0; $i < $length; ++$i) {
switch ($json[$i]) {
case '{':
case '[':
if ($inLiteral) {
break;
}
$stack[] = $json[$i];
$result .= $json[$i];
while (isset($json[$i + 1]) && preg_match('/\s/', $json[$i + 1])) {
++$i;
}
if (isset($json[$i + 1]) && $json[$i + 1] !== '}' && $json[$i + 1] !== ']') {
$result .= "\n" . str_repeat($indentString, count($stack));
}
continue 2;
case '}':
case ']':
if ($inLiteral) {
break;
}
$last = end($stack);
if (($last === '{' && $json[$i] === '}')
|| ($last === '[' && $json[$i] === ']')
) {
array_pop($stack);
}
$result .= $json[$i];
while (isset($json[$i + 1]) && preg_match('/\s/', $json[$i + 1])) {
++$i;
}
if (isset($json[$i + 1]) && ($json[$i + 1] === '}' || $json[$i + 1] === ']')) {
$result .= "\n" . str_repeat($indentString, count($stack) - 1);
}
continue 2;
case '"':
$result .= '"';
if (! $inLiteral) {
$inLiteral = true;
} else {
$backslashes = 0;
$n = $i;
while ($json[--$n] === '\\') {
++$backslashes;
}
if (($backslashes % 2) === 0) {
$inLiteral = false;
while (isset($json[$i + 1]) && preg_match('/\s/', $json[$i + 1])) {
++$i;
}
if (isset($json[$i + 1]) && ($json[$i + 1] === '}' || $json[$i + 1] === ']')) {
$result .= "\n" . str_repeat($indentString, count($stack) - 1);
}
}
}
continue 2;
case ':':
if (! $inLiteral) {
$result .= ': ';
continue 2;
}
break;
case ',':
if (! $inLiteral) {
$result .= ',' . "\n" . str_repeat($indentString, count($stack));
continue 2;
}
break;
default:
if (! $inLiteral && preg_match('/\s/', $json[$i])) {
continue 2;
}
break;
}
$result .= $json[$i];
if ($inLiteral) {
continue;
}
while (isset($json[$i + 1]) && preg_match('/\s/', $json[$i + 1])) {
++$i;
}
if (isset($json[$i + 1]) && ($json[$i + 1] === '}' || $json[$i + 1] === ']')) {
$result .= "\n" . str_repeat($indentString, count($stack) - 1);
}
}
return $result;
}
/**
* Decode a value using the PHP built-in json_decode function.
*
* @param string $encodedValue
* @param int $objectDecodeType
* @return mixed
* @throws RuntimeException
*/
private static function decodeViaPhpBuiltIn($encodedValue, $objectDecodeType)
{
$decoded = json_decode($encodedValue, (bool) $objectDecodeType);
switch (json_last_error()) {
case JSON_ERROR_NONE:
return $decoded;
case JSON_ERROR_DEPTH:
throw new RuntimeException('Decoding failed: Maximum stack depth exceeded');
case JSON_ERROR_CTRL_CHAR:
throw new RuntimeException('Decoding failed: Unexpected control character found');
case JSON_ERROR_SYNTAX:
throw new RuntimeException('Decoding failed: Syntax error');
default:
throw new RuntimeException('Decoding failed');
}
}
/**
* Encode a value to JSON.
*
* Intermediary step between injecting JavaScript expressions.
*
* Delegates to either the PHP built-in json_encode operation, or the
* Encoder component, based on availability of the built-in and/or whether
* or not the component encoder is requested.
*
* @param mixed $valueToEncode
* @param bool $cycleCheck
* @param array $options
* @param bool $prettyPrint
* @return string
*/
private static function encodeValue($valueToEncode, $cycleCheck, array $options, $prettyPrint)
{
if (function_exists('json_encode') && static::$useBuiltinEncoderDecoder !== true) {
return self::encodeViaPhpBuiltIn($valueToEncode, $prettyPrint);
}
return self::encodeViaEncoder($valueToEncode, $cycleCheck, $options, $prettyPrint);
}
/**
* Encode a value to JSON using the PHP built-in json_encode function.
*
* Uses the encoding options:
*
* - JSON_HEX_TAG
* - JSON_HEX_APOS
* - JSON_HEX_QUOT
* - JSON_HEX_AMP
*
* If $prettyPrint is boolean true, also uses JSON_PRETTY_PRINT.
*
* @param mixed $valueToEncode
* @param bool $prettyPrint
* @return string|false Boolean false return value if json_encode is not
* available, or the $useBuiltinEncoderDecoder flag is enabled.
*/
private static function encodeViaPhpBuiltIn($valueToEncode, $prettyPrint = false)
{
if (! function_exists('json_encode') || static::$useBuiltinEncoderDecoder === true) {
return false;
}
$encodeOptions = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP;
if ($prettyPrint) {
$encodeOptions |= JSON_PRETTY_PRINT;
}
return json_encode($valueToEncode, $encodeOptions);
}
/**
* Encode a value to JSON using the Encoder class.
*
* Passes the value, cycle check flag, and options to Encoder::encode().
*
* Once the result is returned, determines if pretty printing is required,
* and, if so, returns the result of that operation, otherwise returning
* the encoded value.
*
* @param mixed $valueToEncode
* @param bool $cycleCheck
* @param array $options
* @param bool $prettyPrint
* @return string
*/
private static function encodeViaEncoder($valueToEncode, $cycleCheck, array $options, $prettyPrint)
{
$encodedResult = Encoder::encode($valueToEncode, $cycleCheck, $options);
if ($prettyPrint) {
return self::prettyPrint($encodedResult, ['indent' => ' ']);
}
return $encodedResult;
}
/**
* Inject javascript expressions into the encoded value.
*
* Loops through each, substituting the "magicKey" of each with its
* associated value.
*
* @param string $encodedValue
* @param SplQueue $javascriptExpressions
* @return string
*/
private static function injectJavascriptExpressions($encodedValue, SplQueue $javascriptExpressions)
{
foreach ($javascriptExpressions as $expression) {
$encodedValue = str_replace(
sprintf('"%s"', $expression['magicKey']),
$expression['value'],
(string) $encodedValue
);
}
return $encodedValue;
}
}
PK l}P3f
mkdocs.ymlnu W+A docs_dir: docs/book
site_dir: docs/html
nav:
- Home: index.md
- Introduction: intro.md
- Reference:
- "Basic Usage": basics.md
- "Advanced Usage": advanced.md
- Migration:
- "From v2 to v3": migration/v2-to-v3.md
site_name: laminas-json
site_description: "De/Serialize JSON in PHP, including JavaScript expressions"
repo_url: 'https://github.com/laminas/laminas-json'
extra:
project: Components
PK l}P;bJ J .github/workflows/docs-build.ymlnu W+A name: docs-build
on:
push:
branches:
- master
repository_dispatch:
types: docs-build
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- name: Build Docs
uses: laminas/documentation-theme/github-actions/docs@master
env:
DOCS_DEPLOY_KEY: ${{ secrets.DOCS_DEPLOY_KEY }}
PK l}PRa .gitattributesnu W+A /.coveralls.yml export-ignore
/.gitattributes export-ignore
/.github/ export-ignore
/.gitignore export-ignore
/.travis.yml export-ignore
/docs/ export-ignore
/mkdocs.yml export-ignore
/phpcs.xml export-ignore
/phpunit.xml.dist export-ignore
/test/ export-ignore
PK l}P`n n ( test/TestAsset/TestIteratorAggregate.phpnu W+A 'bar',
'baz' => 5,
];
public function getIterator()
{
return new ArrayIterator($this->array);
}
}
PK l}PA4 test/TestAsset/ToJSONClass.phpnu W+A $this->_firstName,
'lastName' => $this->_lastName,
'email' => $this->_email
];
return Json::encode($data);
}
}
PK l}P+ + . test/TestAsset/JsonSerializableBuiltinImpl.phpnu W+A $this->_firstName,
'lastName' => $this->_lastName,
'email' => $this->_email
];
return $data;
}
}
PK l}PZ . test/TestAsset/JsonSerializableLaminasImpl.phpnu W+A new Expr($this->_expr),
'int' => $this->_int,
'string' => $this->_string
];
return Json::encode($data, false, ['enableJsonExprFinder' => true]);
}
}
PK l}PH m m test/TestAsset/Bar.phpnu W+A val = $someval;
}
/**
* Bar
*
* @param bool $one
* @param string $two
* @param mixed $three
* @return array
*/
public function foo($one, $two = 'two', $three = null)
{
return [$one, $two, $three, $this->val];
}
/**
* Baz
*
* @return void
* @throws Exception
*/
public function baz()
{
throw new Exception('application error');
}
}
PK l}Pw test/JsonTest.phpnu W+A originalUseBuiltinEncoderDecoderValue = Json\Json::$useBuiltinEncoderDecoder;
}
public function tearDown()
{
Json\Json::$useBuiltinEncoderDecoder = $this->originalUseBuiltinEncoderDecoderValue;
}
/**
* Test encoding and decoding in a single step
*
* @param scalar|array $values array of values to test against encode/decode
* @param $message
*/
public function assertEncodesToDecodable($values, $message = null)
{
$message = $message ?: 'One or more values could not be decoded after encoding';
$values = is_null($values) ? [null] : $values;
$values = is_scalar($values) ? [$values] : $values;
foreach ($values as $value) {
$encoded = Json\Encoder::encode($value);
if (is_array($value) || is_object($value)) {
$message = $message ?: sprintf(
'Value could not be decoded after encoding: %s',
var_export($value, true)
);
$this->assertEquals(
$this->toArray($value),
Json\Decoder::decode($encoded, Json\Json::TYPE_ARRAY),
$message
);
continue;
}
$message = $message ?: sprintf(
'Value could not be decoded after encoding: %s',
$value
);
$this->assertEquals($value, Json\Decoder::decode($encoded), $message);
}
}
public function testJSONWithPhpJSONExtension()
{
if (! extension_loaded('json')) {
$this->markTestSkipped('JSON extension is not loaded');
}
Json\Json::$useBuiltinEncoderDecoder = false;
$this->assertEncodesToDecodable(['string', 327, true, null]);
}
public function testJSONWithBuiltins()
{
Json\Json::$useBuiltinEncoderDecoder = true;
$this->assertEncodesToDecodable(['string', 327, true, null]);
}
/**
* test null encoding/decoding
*/
public function testNull()
{
$this->assertEncodesToDecodable(null, 'Null could not be decoded after encoding');
}
/**
* test boolean encoding/decoding
*/
public function testBoolean()
{
$this->assertTrue(Json\Decoder::decode(Json\Encoder::encode(true)));
$this->assertFalse(Json\Decoder::decode(Json\Encoder::encode(false)));
}
public function integerProvider()
{
return [
'negative' => [-1],
'zero' => [0],
'positive' => [1],
];
}
/**
* test integer encoding/decoding
* @dataProvider integerProvider
*/
public function testInteger($int)
{
$this->assertEncodesToDecodable($int);
}
public function floatProvider()
{
return [
'negative' => [-1.1],
'zero' => [0.0],
'positive' => [1.1],
];
}
/**
* test float encoding/decoding
* @dataProvider floatProvider
*/
public function testFloat($float)
{
$this->assertEncodesToDecodable($float);
}
public function stringProvider()
{
return [
'empty' => [''],
'string' => ['string'],
];
}
/**
* test string encoding/decoding
* @dataProvider stringProvider
*/
public function testString($string)
{
$this->assertEncodesToDecodable($string);
}
/**
* Test backslash escaping of string
*/
public function testString2()
{
$string = 'INFO: Path \\\\test\\123\\abc';
$expected = '"INFO: Path \\\\\\\\test\\\\123\\\\abc"';
$encoded = Json\Encoder::encode($string);
$this->assertEquals(
$expected,
$encoded,
sprintf(
'Backslash encoding incorrect: expected: %s; received: %s',
serialize($expected),
serialize($encoded)
)
);
$this->assertEncodesToDecodable($string);
}
/**
* Test newline escaping of string
*/
public function testString3()
{
$expected = '"INFO: Path\nSome more"';
$string = "INFO: Path\nSome more";
$encoded = Json\Encoder::encode($string);
$this->assertEquals(
$expected,
$encoded,
sprintf(
'Newline encoding incorrect: expected %s; received: %s',
serialize($expected),
serialize($encoded)
)
);
$this->assertEncodesToDecodable($string);
}
/**
* Test tab/non-tab escaping of string
*/
public function testString4()
{
$expected = '"INFO: Path\\t\\\\tSome more"';
$string = "INFO: Path\t\\tSome more";
$encoded = Json\Encoder::encode($string);
$this->assertEquals(
$expected,
$encoded,
sprintf(
'Tab encoding incorrect: expected %s; received: %s',
serialize($expected),
serialize($encoded)
)
);
$this->assertEncodesToDecodable($string);
}
/**
* Test double-quote escaping of string
*/
public function testString5()
{
$expected = '"INFO: Path \\u0022Some more\\u0022"';
$string = 'INFO: Path "Some more"';
$encoded = Json\Encoder::encode($string);
$this->assertEquals(
$expected,
$encoded,
'Quote encoding incorrect: expected ' . serialize($expected) . '; received: ' . serialize($encoded) . "\n"
);
$this->assertEncodesToDecodable($string); // Bug: does not accept \u0022 as token!
}
/**
* Test decoding of unicode escaped special characters
*/
public function testStringOfHtmlSpecialCharsEncodedToUnicodeEscapes()
{
Json\Json::$useBuiltinEncoderDecoder = false;
$expected = '"\\u003C\\u003E\\u0026\\u0027\\u0022"';
$string = '<>&\'"';
$encoded = Json\Encoder::encode($string);
$this->assertEquals(
$expected,
$encoded,
'Encoding error: expected ' . serialize($expected) . '; received: ' . serialize($encoded) . "\n"
);
$this->assertEncodesToDecodable($string);
}
/**
* Test decoding of unicode escaped ASCII (non-HTML special) characters
*
* Note: covers chars that MUST be escaped. Does not test any other non-printables.
*/
public function testStringOfOtherSpecialCharsEncodedToUnicodeEscapes()
{
Json\Json::$useBuiltinEncoderDecoder = false;
$string = "\\ - \n - \t - \r - " . chr(0x08) . " - " . chr(0x0C) . " - / - \v";
$encoded = '"\u005C - \u000A - \u0009 - \u000D - \u0008 - \u000C - \u002F - \u000B"';
$this->assertEquals($string, Json\Decoder::decode($encoded));
}
/**
* test indexed array encoding/decoding
*/
public function testArray()
{
$this->assertEncodesToDecodable([[1, 'one', 2, 'two']]);
}
/**
* test associative array encoding/decoding
*/
public function testAssocArray()
{
$this->assertEncodesToDecodable([['one' => 1, 'two' => 2]]);
}
/**
* test associative array encoding/decoding, with mixed key types
*/
public function testAssocArray2()
{
$this->assertEncodesToDecodable([['one' => 1, 2 => 2]]);
}
/**
* test associative array encoding/decoding, with integer keys not starting at 0
*/
public function testAssocArray3()
{
$this->assertEncodesToDecodable([[1 => 'one', 2 => 'two']]);
}
/**
* test object encoding/decoding (decoding to array)
*/
public function testObject()
{
$value = new stdClass();
$value->one = 1;
$value->two = 2;
$array = ['__className' => 'stdClass', 'one' => 1, 'two' => 2];
$encoded = Json\Encoder::encode($value);
$this->assertSame($array, Json\Decoder::decode($encoded, Json\Json::TYPE_ARRAY));
}
/**
* test object encoding/decoding (decoding to stdClass)
*/
public function testObjectAsObject()
{
$value = new stdClass();
$value->one = 1;
$value->two = 2;
$encoded = Json\Encoder::encode($value);
$decoded = Json\Decoder::decode($encoded, Json\Json::TYPE_OBJECT);
$this->assertInstanceOf('stdClass', $decoded);
$this->assertObjectHasAttribute('one', $decoded);
$this->assertEquals($value->one, $decoded->one, 'Unexpected value');
}
/**
* Test that arrays of objects decode properly; see issue #144
*/
public function testDecodeArrayOfObjects()
{
$value = '[{"id":1},{"foo":2}]';
$expect = [['id' => 1], ['foo' => 2]];
$this->assertEquals($expect, Json\Decoder::decode($value, Json\Json::TYPE_ARRAY));
}
/**
* Test that objects of arrays decode properly; see issue #107
*/
public function testDecodeObjectOfArrays()
{
// @codingStandardsIgnoreStart
$value = '{"codeDbVar" : {"age" : ["int", 5], "prenom" : ["varchar", 50]}, "234" : [22, "jb"], "346" : [64, "francois"], "21" : [12, "paul"]}';
// @codingStandardsIgnoreEnd
$expect = [
'codeDbVar' => [
'age' => ['int', 5],
'prenom' => ['varchar', 50],
],
234 => [22, 'jb'],
346 => [64, 'francois'],
21 => [12, 'paul']
];
$this->assertEquals($expect, Json\Decoder::decode($value, Json\Json::TYPE_ARRAY));
}
/**
* Cast a value to an array, if possible.
*
* Casts objects to arrays for expectation comparisons.
*
* @param mixed $value
* @return mixed
*/
protected function toArray($value)
{
if (! is_array($value) || ! is_object($value)) {
return $value;
}
$array = [];
foreach ((array) $value as $k => $v) {
$array[$k] = $this->toArray($v);
}
return $array;
}
/**
* Test that version numbers such as 4.10 are encoded and decoded properly;
* See Laminas-377
*/
public function testEncodeReleaseNumber()
{
$value = '4.10';
$this->assertEncodesToDecodable($value);
}
/**
* Tests that spaces/linebreaks prior to a closing right bracket don't throw
* exceptions. See Laminas-283.
*/
public function testEarlyLineBreak()
{
$expected = ['data' => [1, 2, 3, 4]];
$json = '{"data":[1,2,3,4' . "\n]}";
$this->assertEquals($expected, Json\Decoder::decode($json, Json\Json::TYPE_ARRAY));
$json = '{"data":[1,2,3,4 ]}';
$this->assertEquals($expected, Json\Decoder::decode($json, Json\Json::TYPE_ARRAY));
}
/**
* @group Laminas-504
*/
public function testEncodeEmptyArrayAsStruct()
{
$this->assertSame('[]', Json\Encoder::encode([]));
}
/**
* @group Laminas-504
*/
public function testDecodeBorkedJsonShouldThrowException1()
{
$this->expectException(Json\Exception\RuntimeException::class);
Json\Decoder::decode('[a"],["a],[][]');
}
/**
* @group Laminas-504
*/
public function testDecodeBorkedJsonShouldThrowException2()
{
$this->expectException(Json\Exception\RuntimeException::class);
Json\Decoder::decode('[a"],["a]');
}
/**
* @group Laminas-504
*/
public function testOctalValuesAreNotSupportedInJsonNotation()
{
$this->expectException(Json\Exception\RuntimeException::class);
Json\Decoder::decode('010');
}
/**
* Tests for Laminas-461
*
* Check to see that cycling detection works properly
*/
public function testLaminas461()
{
$item1 = new TestAsset\Item();
$item2 = new TestAsset\Item();
$everything = [];
$everything['allItems'] = [$item1, $item2];
$everything['currentItem'] = $item1;
// should not fail
$encoded = Json\Encoder::encode($everything);
// should fail
$this->expectException(Json\Exception\RecursionException::class);
Json\Encoder::encode($everything, true);
}
/**
* Test for Laminas-4053
*
* Check to see that cyclical exceptions are silenced when
* $option['silenceCyclicalExceptions'] = true is used
*/
public function testLaminas4053()
{
$item1 = new TestAsset\Item();
$item2 = new TestAsset\Item();
$everything = [];
$everything['allItems'] = [$item1, $item2];
$everything['currentItem'] = $item1;
$options = ['silenceCyclicalExceptions' => true];
Json\Json::$useBuiltinEncoderDecoder = true;
$encoded = Json\Json::encode($everything, true, $options);
// @codingStandardsIgnoreStart
$json = '{"allItems":[{"__className":"LaminasTest\\\\Json\\\\TestAsset\\\\Item"},{"__className":"LaminasTest\\\\Json\\\\TestAsset\\\\Item"}],"currentItem":"* RECURSION (LaminasTest\\\\Json\\\\TestAsset\\\\Item) *"}';
// @codingStandardsIgnoreEnd
$this->assertEquals($json, $encoded);
}
public function testEncodeObject()
{
$actual = new TestAsset\TestObject();
$encoded = Json\Encoder::encode($actual);
$decoded = Json\Decoder::decode($encoded, Json\Json::TYPE_OBJECT);
$this->assertAttributeEquals(TestAsset\TestObject::class, '__className', $decoded);
$this->assertAttributeEquals('bar', 'foo', $decoded);
$this->assertAttributeEquals('baz', 'bar', $decoded);
$this->assertFalse(isset($decoded->_foo));
}
public function testEncodeClass()
{
$encoded = Json\Encoder::encodeClass(TestAsset\TestObject::class);
$this->assertContains("Class.create('LaminasTest\\Json\\TestAsset\\TestObject'", $encoded);
$this->assertContains("ZAjaxEngine.invokeRemoteMethod(this, 'foo'", $encoded);
$this->assertContains("ZAjaxEngine.invokeRemoteMethod(this, 'bar'", $encoded);
$this->assertNotContains("ZAjaxEngine.invokeRemoteMethod(this, 'baz'", $encoded);
$this->assertContains('variables:{foo:"bar",bar:"baz"}', $encoded);
$this->assertContains('constants:{FOO: "bar"}', $encoded);
}
public function testEncodeClasses()
{
$encoded = Json\Encoder::encodeClasses(['LaminasTest\Json\TestAsset\TestObject', 'Laminas\Json\Json']);
$this->assertContains("Class.create('LaminasTest\\Json\\TestAsset\\TestObject'", $encoded);
$this->assertContains("Class.create('Laminas\\Json\\Json'", $encoded);
}
public function testToJSONSerialization()
{
$toJSONObject = new TestAsset\ToJSONClass();
$result = Json\Json::encode($toJSONObject);
$this->assertEquals('{"firstName":"John","lastName":"Doe","email":"john@doe.com"}', $result);
}
public function testJsonSerializableWithBuiltinImplementation()
{
$encoded = Json\Encoder::encode(
new TestAsset\JsonSerializableBuiltinImpl()
);
$this->assertEquals('["jsonSerialize"]', $encoded);
}
public function testJsonSerializableWithLaminasImplementation()
{
$encoded = Json\Encoder::encode(
new TestAsset\JsonSerializableLaminasImpl()
);
$this->assertEquals('["jsonSerialize"]', $encoded);
}
/**
* test encoding array with Laminas_JSON_Expr
*
* @group Laminas-4946
*/
public function testEncodingArrayWithExpr()
{
$expr = new Json\Expr('window.alert("Laminas JSON Expr")');
$array = ['expr' => $expr, 'int' => 9, 'string' => 'text'];
$result = Json\Json::encode($array, false, ['enableJsonExprFinder' => true]);
$expected = '{"expr":window.alert("Laminas JSON Expr"),"int":9,"string":"text"}';
$this->assertEquals($expected, $result);
}
/**
* test encoding object with Laminas_JSON_Expr
*
* @group Laminas-4946
*/
public function testEncodingObjectWithExprAndInternalEncoder()
{
Json\Json::$useBuiltinEncoderDecoder = true;
$expr = new Json\Expr('window.alert("Laminas JSON Expr")');
$obj = new stdClass();
$obj->expr = $expr;
$obj->int = 9;
$obj->string = 'text';
$result = Json\Json::encode($obj, false, ['enableJsonExprFinder' => true]);
$expected = '{"__className":"stdClass","expr":window.alert("Laminas JSON Expr"),"int":9,"string":"text"}';
$this->assertEquals($expected, $result);
}
/**
* Test encoding object with Laminas\Json\Expr
*
* @group Laminas-4946
*/
public function testEncodingObjectWithExprAndExtJSON()
{
if (! function_exists('json_encode')) {
$this->markTestSkipped('Test only works with ext/json enabled!');
}
Json\Json::$useBuiltinEncoderDecoder = false;
$expr = new Json\Expr('window.alert("Laminas JSON Expr")');
$obj = new stdClass();
$obj->expr = $expr;
$obj->int = 9;
$obj->string = 'text';
$result = Json\Json::encode($obj, false, ['enableJsonExprFinder' => true]);
$expected = '{"expr":window.alert("Laminas JSON Expr"),"int":9,"string":"text"}';
$this->assertEquals($expected, $result);
}
/**
* test encoding object with toJson and Laminas\Json\Expr
*
* @group Laminas-4946
*/
public function testToJSONWithExpr()
{
Json\Json::$useBuiltinEncoderDecoder = true;
$obj = new TestAsset\ToJSONWithExpr();
$result = Json\Json::encode($obj, false, ['enableJsonExprFinder' => true]);
$expected = '{"expr":window.alert("Laminas JSON Expr"),"int":9,"string":"text"}';
$this->assertEquals($expected, $result);
}
/**
* Regression tests for Laminas\Json\Expr and multiple keys with the same name.
*
* @group Laminas-4946
*/
public function testEncodingMultipleNestedSwitchingSameNameKeysWithDifferentJSONExprSettings()
{
$data = [
0 => [
"alpha" => new Json\Expr("function () {}"),
"beta" => "gamma",
],
1 => [
"alpha" => "gamma",
"beta" => new Json\Expr("function () {}"),
],
2 => [
"alpha" => "gamma",
"beta" => "gamma",
]
];
$result = Json\Json::encode($data, false, ['enableJsonExprFinder' => true]);
// @codingStandardsIgnoreStart
$this->assertEquals(
'[{"alpha":function () {},"beta":"gamma"},{"alpha":"gamma","beta":function () {}},{"alpha":"gamma","beta":"gamma"}]',
$result
);
// @codingStandardsIgnoreEnd
}
/**
* Regression tests for Laminas\Json\Expr and multiple keys with the same name.
*
* @group Laminas-4946
*/
public function testEncodingMultipleNestedIteratedSameNameKeysWithDifferentJSONExprSettings()
{
$data = [
0 => [
"alpha" => "alpha"
],
1 => [
"alpha" => "beta",
],
2 => [
"alpha" => new Json\Expr("gamma"),
],
3 => [
"alpha" => "delta",
],
4 => [
"alpha" => new Json\Expr("epsilon"),
]
];
$result = Json\Json::encode($data, false, ['enableJsonExprFinder' => true]);
// @codingStandardsIgnoreStart
$this->assertEquals('[{"alpha":"alpha"},{"alpha":"beta"},{"alpha":gamma},{"alpha":"delta"},{"alpha":epsilon}]', $result);
// @codingStandardsIgnoreEnd
}
public function testDisabledJSONExprFinder()
{
Json\Json::$useBuiltinEncoderDecoder = true;
$data = [
0 => [
"alpha" => new Json\Expr("function () {}"),
"beta" => "gamma",
],
];
$result = Json\Json::encode($data);
$this->assertEquals(
'[{"alpha":{"__className":"Laminas\\\\Json\\\\Expr"},"beta":"gamma"}]',
$result
);
}
/**
* @group Laminas-4054
*/
public function testEncodeWithUtf8IsTransformedToPackedSyntax()
{
$data = ["Отмена"];
$result = Json\Encoder::encode($data);
$this->assertEquals('["\u041e\u0442\u043c\u0435\u043d\u0430"]', $result);
}
/**
* @group Laminas-4054
*
* This test contains assertions from the Solar Framework by Paul M. Jones
* @link http://solarphp.com
*/
public function testEncodeWithUtf8IsTransformedSolarRegression()
{
$expect = '"h\u00c3\u00a9ll\u00c3\u00b6 w\u00c3\u00b8r\u00c5\u201ad"';
$this->assertEquals($expect, Json\Encoder::encode('héllö wørłd'));
$this->assertEquals('héllö wørłd', Json\Decoder::decode($expect));
$expect = '"\u0440\u0443\u0441\u0441\u0438\u0448"';
$this->assertEquals($expect, Json\Encoder::encode("руссиш"));
$this->assertEquals("руссиш", Json\Decoder::decode($expect));
}
/**
* @group Laminas-4054
*/
public function testEncodeUnicodeStringSolarRegression()
{
$value = 'héllö wørłd';
$expected = 'h\u00c3\u00a9ll\u00c3\u00b6 w\u00c3\u00b8r\u00c5\u201ad';
$this->assertEquals($expected, Json\Encoder::encodeUnicodeString($value));
$value = "\xC3\xA4";
$expected = '\u00e4';
$this->assertEquals($expected, Json\Encoder::encodeUnicodeString($value));
$value = "\xE1\x82\xA0\xE1\x82\xA8";
$expected = '\u10a0\u10a8';
$this->assertEquals($expected, Json\Encoder::encodeUnicodeString($value));
}
/**
* @group Laminas-4054
*/
public function testDecodeUnicodeStringSolarRegression()
{
$expected = 'héllö wørłd';
$value = 'h\u00c3\u00a9ll\u00c3\u00b6 w\u00c3\u00b8r\u00c5\u201ad';
$this->assertEquals($expected, Json\Decoder::decodeUnicodeString($value));
$expected = "\xC3\xA4";
$value = '\u00e4';
$this->assertEquals($expected, Json\Decoder::decodeUnicodeString($value));
$value = '\u10a0';
$expected = "\xE1\x82\xA0";
$this->assertEquals($expected, Json\Decoder::decodeUnicodeString($value));
}
/**
* @group Laminas-4054
*
* This test contains assertions from the Solar Framework by Paul M. Jones
* @link http://solarphp.com
*/
public function testEncodeWithUtf8IsTransformedSolarRegressionEqualsJSONExt()
{
if (function_exists('json_encode') == false) {
$this->markTestSkipped('Test can only be run, when ext/json is installed.');
}
$this->assertEquals(
json_encode('héllö wørłd'),
Json\Encoder::encode('héllö wørłd')
);
$this->assertEquals(
json_encode("руссиш"),
Json\Encoder::encode("руссиш")
);
}
/**
* @group Laminas-4946
*/
public function testUtf8JSONExprFinder()
{
$data = ["Отмена" => new Json\Expr("foo")];
Json\Json::$useBuiltinEncoderDecoder = true;
$result = Json\Json::encode($data, false, ['enableJsonExprFinder' => true]);
$this->assertEquals('{"\u041e\u0442\u043c\u0435\u043d\u0430":foo}', $result);
Json\Json::$useBuiltinEncoderDecoder = false;
$result = Json\Json::encode($data, false, ['enableJsonExprFinder' => true]);
$this->assertEquals('{"\u041e\u0442\u043c\u0435\u043d\u0430":foo}', $result);
}
/**
* @group Laminas-4437
*/
public function testCommaDecimalIsConvertedToCorrectJSONWithDot()
{
setlocale(LC_ALL, 'Spanish_Spain', 'es_ES', 'es_ES.utf-8');
if (strcmp('1,2', (string) floatval(1.20)) !== 0) {
$this->markTestSkipped('This test only works for platforms where "," is the decimal point separator.');
}
Json\Json::$useBuiltinEncoderDecoder = true;
$actual = Json\Encoder::encode([floatval(1.20), floatval(1.68)]);
$this->assertEquals('[1.2,1.68]', $actual);
}
public function testEncodeObjectImplementingIterator()
{
$iterator = new ArrayIterator([
'foo' => 'bar',
'baz' => 5
]);
$target = '{"__className":"ArrayIterator","foo":"bar","baz":5}';
Json\Json::$useBuiltinEncoderDecoder = true;
$this->assertEquals($target, Json\Json::encode($iterator));
}
/**
* @group Laminas-12347
*/
public function testEncodeObjectImplementingIteratorAggregate()
{
$iterator = new TestAsset\TestIteratorAggregate();
$target = '{"__className":"LaminasTest\\\\Json\\\\TestAsset\\\\TestIteratorAggregate","foo":"bar","baz":5}';
Json\Json::$useBuiltinEncoderDecoder = true;
$this->assertEquals($target, Json\Json::encode($iterator));
}
/**
* @group Laminas-8663
*/
public function testNativeJSONEncoderWillProperlyEncodeSolidusInStringValues()
{
$source = "bar";
$target = '"\u003C\/foo\u003E\u003Cfoo\u003Ebar\u003C\/foo\u003E"';
// first test ext/json
Json\Json::$useBuiltinEncoderDecoder = false;
$this->assertEquals($target, Json\Json::encode($source));
}
public function testNativeJSONEncoderWillProperlyEncodeHtmlSpecialCharsInStringValues()
{
$source = "<>&'\"";
$target = '"\u003C\u003E\u0026\u0027\u0022"';
// first test ext/json
Json\Json::$useBuiltinEncoderDecoder = false;
$this->assertEquals($target, Json\Json::encode($source));
}
/**
* @group Laminas-8663
*/
public function testBuiltinJSONEncoderWillProperlyEncodeSolidusInStringValues()
{
$source = "bar";
$target = '"\u003C\/foo\u003E\u003Cfoo\u003Ebar\u003C\/foo\u003E"';
// first test ext/json
Json\Json::$useBuiltinEncoderDecoder = true;
$this->assertEquals($target, Json\Json::encode($source));
}
public function testBuiltinJSONEncoderWillProperlyEncodeHtmlSpecialCharsInStringValues()
{
$source = "<>&'\"";
$target = '"\u003C\u003E\u0026\u0027\u0022"';
// first test ext/json
Json\Json::$useBuiltinEncoderDecoder = true;
$this->assertEquals($target, Json\Json::encode($source));
}
/**
* @group Laminas-8918
*/
public function testDecodingInvalidJSONShouldRaiseAnException()
{
$this->expectException(Json\Exception\RuntimeException::class);
Json\Json::decode(' some string ');
}
/**
* Encoding an iterator using the internal encoder should handle undefined keys
*
* @group Laminas-9416
*/
public function testIteratorWithoutDefinedKey()
{
$inputValue = new ArrayIterator(['foo']);
$encoded = Json\Encoder::encode($inputValue);
$expectedDecoding = '{"__className":"ArrayIterator",0:"foo"}';
$this->assertEquals($expectedDecoding, $encoded);
}
/**
* The default json decode type should be TYPE_OBJECT
*
* @group Laminas-8618
*/
public function testDefaultTypeObject()
{
$this->assertInstanceOf(stdClass::class, Json\Decoder::decode('{"var":"value"}'));
}
/**
* @group Laminas-10185
*/
public function testJsonPrettyPrintWorksWithArrayNotationInStringLiteral()
{
$o = new stdClass();
$o->test = 1;
$o->faz = 'fubar';
// The escaped double-quote in item 'stringwithjsonchars' ensures that
// escaped double-quotes do not throw off string literal detection in
// prettyPrint
$test = [
'simple' => 'simple test string',
'stringwithjsonchars' => '\"[1,2]',
'complex' => [
'foo' => 'bar',
'far' => 'boo',
'faz' => [
'obj' => $o,
],
'fay' => ['foo', 'bar'],
],
];
$pretty = Json\Json::prettyPrint(Json\Json::encode($test), ['indent' => ' ']);
$expected = <<assertSame($expected, $pretty);
}
public function testPrettyPrintDoublequoteFollowingEscapedBackslashShouldNotBeTreatedAsEscaped()
{
$this->assertEquals(
"[\n 1,\n \"\\\\\",\n 3\n]",
Json\Json::prettyPrint(Json\Json::encode([1, '\\', 3]))
);
$this->assertEquals(
"{\n \"a\": \"\\\\\"\n}",
Json\Json::prettyPrint(Json\Json::encode(['a' => '\\']))
);
}
public function testPrettyPrintEmptyArray()
{
$original = <<assertSame($original, Json\Json::prettyPrint($original));
}
public function testPrettyPrintEmptyObject()
{
$original = <<assertSame($original, Json\Json::prettyPrint($original));
}
public function testPrettyPrintEmptyProperties()
{
$original = <<assertSame($original, Json\Json::prettyPrint($original));
}
public function testPrettyPrintEmptyPropertiesWithWhitespace()
{
$original = <<assertSame($pretty, Json\Json::prettyPrint($original));
}
public function testJsonPrettyPrintDoesNotRemoveSpaceAroundCommaInStringValue()
{
$original = <<assertSame($original, $pretty);
}
public function testJsonPretty()
{
$original = <<assertSame($original, Json\Json::prettyPrint($original));
}
public function testPrettyPrintRePrettyPrint()
{
$expected = <<assertSame(
$expected,
Json\Json::prettyPrint($expected, ['indent' => ' '])
);
}
public function testEncodeWithPrettyPrint()
{
$o = new stdClass();
$o->test = 1;
$o->faz = 'fubar';
// The escaped double-quote in item 'stringwithjsonchars' ensures that
// escaped double-quotes do not throw off string literal detection in
// prettyPrint
$test = [
'simple' => 'simple test string',
'stringwithjsonchars' => '\"[1,2]',
'complex' => [
'foo' => 'bar',
'far' => 'boo',
'faz' => [
'obj' => $o,
],
],
];
$pretty = Json\Json::encode($test, false, ['prettyPrint' => true]);
$expected = <<assertSame($expected, $pretty);
}
/**
* @group Laminas-11167
*/
public function testEncodeWillUseToArrayMethodWhenAvailable()
{
$o = new TestAsset\Laminas11167ToArrayClass();
$objJson = Json\Json::encode($o);
$arrJson = Json\Json::encode($o->toArray());
$this->assertSame($arrJson, $objJson);
}
/**
* @group Laminas-11167
*/
public function testEncodeWillUseToJsonWhenBothToJsonAndToArrayMethodsAreAvailable()
{
$o = new TestAsset\Laminas11167ToArrayToJsonClass();
$objJson = Json\Json::encode($o);
$this->assertEquals('"bogus"', $objJson);
$arrJson = Json\Json::encode($o->toArray());
$this->assertNotSame($objJson, $arrJson);
}
/**
* @group Laminas-9521
*/
public function testWillEncodeArrayOfObjectsEachWithToJsonMethod()
{
$array = ['one' => new TestAsset\ToJsonClass()];
// @codingStandardsIgnoreStart
$expected = '{"one":{"__className":"LaminasTest\\\\Json\\\\TestAsset\\\\ToJSONClass","firstName":"John","lastName":"Doe","email":"john@doe.com"}}';
// @codingStandardsIgnoreEnd
Json\Json::$useBuiltinEncoderDecoder = true;
$json = Json\Encoder::encode($array);
$this->assertEquals($expected, $json);
}
/**
* @group Laminas-7586
*/
public function testWillDecodeStructureWithEmptyKeyToObjectProperly()
{
Json\Json::$useBuiltinEncoderDecoder = true;
$json = '{"":"test"}';
$object = Json\Json::decode($json, Json\Json::TYPE_OBJECT);
$this->assertAttributeEquals('test', '_empty_', $object);
}
}
PK l}PR9 9 phpunit.xml.distnu W+A
./test
./src
PK l}PzJ^ ^ CHANGELOG.mdnu W+A # Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 3.1.3 - TBD
### Added
- Nothing.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.1.2 - 2019-10-09
### Added
- Nothing.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [zendframework/zend-json#46](https://github.com/zendframework/zend-json/pull/46) changes
curly braces in array and string offset access to square brackets
in order to prevent issues under the upcoming PHP 7.4 release.
- [zendframework/zend-json#37](https://github.com/zendframework/zend-json/pull/37) fixes
output of `\Laminas\Json::prettyPrint` to not remove spaces after
commas in value.
## 3.1.1 - 2019-06-18
### Added
- [zendframework/zend-json#44](https://github.com/zendframework/zend-json/pull/44) adds support for PHP 7.3.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.1.0 - 2018-01-04
### Added
- [zendframework/zend-json#35](https://github.com/zendframework/zend-json/pull/35) and
[zendframework/zend-json#39](https://github.com/zendframework/zend-json/pull/39) add support for PHP
7.1 and PHP 7.2.
### Deprecated
- Nothing.
### Removed
- [zendframework/zend-json#35](https://github.com/zendframework/zend-json/pull/35) removes support for
PHP 5.5.
- [zendframework/zend-json#35](https://github.com/zendframework/zend-json/pull/35) removes support for
HHVM.
### Fixed
- [zendframework/zend-json#38](https://github.com/zendframework/zend-json/pull/38) provides a fix to
`Json::prettyPrint()` to ensure that empty arrays and objects are printed
without newlines.
- [zendframework/zend-json#38](https://github.com/zendframework/zend-json/pull/38) provides a fix to
`Json::prettyPrint()` to remove additional newlines preceding a closing
bracket.
## 3.0.0 - 2016-03-31
### Added
- [zendframework/zend-json#21](https://github.com/zendframework/zend-json/pull/21) adds documentation
and publishes it to https://docs.laminas.dev/laminas-json/
### Deprecated
- Nothing.
### Removed
- [zendframework/zend-json#20](https://github.com/zendframework/zend-json/pull/20) removes the
`Laminas\Json\Server` subcomponent, which has been extracted to
[laminas-json-server](https://docs.laminas.dev/laminas-json-server/).
If you use that functionality, install the new component.
- [zendframework/zend-json#21](https://github.com/zendframework/zend-json/pull/21) removes the
`Laminas\Json\Json::fromXml()` functionality, which has been extracted to
[laminas-xml2json](https://docs.laminas.dev/laminas-xml2json/). If you used
this functionality, you will need to install the new package, and rewrite
calls to `Laminas\Json\Json::fromXml()` to `Laminas\Xml2Json\Xml2Json::fromXml()`.
- [zendframework/zend-json#20](https://github.com/zendframework/zend-json/pull/20) and
[zendframework/zend-json#21](https://github.com/zendframework/zend-json/pull/21) removes dependencies
on laminas/laminas-xml, laminas/laminas-stdlib,
zendframework/zend-server, and zendframework-zend-http, due to the above
listed component extractions.
### Fixed
- Nothing.
## 2.6.1 - 2016-02-04
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [zendframework/zend-json#18](https://github.com/zendframework/zend-json/pull/18) updates dependencies
to allow usage on PHP 7, as well as with laminas-stdlib v3.
## 2.6.0 - 2015-11-18
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- [zendframework/zend-json#5](https://github.com/zendframework/zend-json/pull/5) removes
zendframework/zend-stdlib as a required dependency, marking it instead
optional, as it is only used for the `Server` subcomponent.
### Fixed
- Nothing.
## 2.5.2 - 2015-08-05
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [zendframework/zend-json#3](https://github.com/zendframework/zend-json/pull/3) fixes an array key
name from `intent` to `indent` to ensure indentation works correctly during
pretty printing.
PK l}PYQ@ @
composer.jsonnu W+A {
"name": "laminas/laminas-json",
"description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP",
"license": "BSD-3-Clause",
"keywords": [
"laminas",
"json"
],
"homepage": "https://laminas.dev",
"support": {
"docs": "https://docs.laminas.dev/laminas-json/",
"issues": "https://github.com/laminas/laminas-json/issues",
"source": "https://github.com/laminas/laminas-json",
"rss": "https://github.com/laminas/laminas-json/releases.atom",
"chat": "https://laminas.dev/chat",
"forum": "https://discourse.laminas.dev"
},
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev",
"dev-develop": "3.2.x-dev"
}
},
"require": {
"php": "^5.6 || ^7.0",
"laminas/laminas-zendframework-bridge": "^1.0"
},
"require-dev": {
"laminas/laminas-coding-standard": "~1.0.0",
"laminas/laminas-stdlib": "^2.7.7 || ^3.1",
"phpunit/phpunit": "^5.7.23 || ^6.4.3"
},
"suggest": {
"laminas/laminas-json-server": "For implementing JSON-RPC servers",
"laminas/laminas-xml2json": "For converting XML documents to JSON"
},
"autoload": {
"psr-4": {
"Laminas\\Json\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"LaminasTest\\Json\\": "test/"
}
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"test": "phpunit --colors=always",
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
},
"replace": {
"zendframework/zend-json": "^3.1.2"
}
}
PK l}P`M M docs/book/advanced.mdnu W+A # Advanced Usage
## JSON Objects
When encoding PHP objects as JSON, all public properties of that object will be
encoded in a JSON object.
JSON does not allow object references, so care should be taken not to encode
objects with recursive references. If you have issues with recursion,
`Laminas\Json\Json::encode()` and `Laminas\Json\Encoder::encode()` each allow an
optional second parameter to check for recursion; if an object is serialized
twice, an exception will be thrown.
Decoding JSON objects poses additional difficulty, however, since JavaScript
objects correspond most closely to PHP's associative array. Some suggest that a
class identifier should be passed, and an object instance of that class should
be created and populated with the key/value pairs of the JSON object; others
feel this could pose a substantial security risk.
By default, `Laminas\Json\Json` will decode JSON objects as `stdClass` objects.
However, if you desire an associative array returned, you can request it using
the second argument to `decode()`:
```php
// Decode JSON objects as PHP array
$phpNative = Laminas\Json\Json::decode($encodedValue, Laminas\Json\Json::TYPE_ARRAY);
```
Any objects thus decoded are returned as associative arrays with keys and values
corresponding to the key/value pairs in the JSON notation.
The recommendation of Laminas is that the individual developer should
decide how to decode JSON objects. If an object of a specified type should be
created, it can be created in the developer code and populated with the values
decoded using laminas-json.
## Encoding PHP Objects
If you are encoding PHP objects, the default encoding mechanism can only
access public properties of these objects. When a method `toJson()` is
implemented on an object to encode, `Laminas\Json\Json` calls this method and
expects the object to return a JSON representation of its internal state.
`Laminas\Json\Json` can encode PHP objects recursively but does not do so by
default. This can be enabled by passing `true` as the second argument to
`Laminas\Json\Json::encode()`.
```php
// Encode PHP object recursively
$jsonObject = Laminas\Json\Json::encode($data, true);
```
When doing recursive encoding of objects, as JSON does not support cycles, a
`Laminas\Json\Exception\RecursionException` will be thrown. If you wish, you can
silence these exceptions by passing the `silenceCyclicalExceptions` option:
```php
$jsonObject = Laminas\Json\Json::encode(
$data,
true,
['silenceCyclicalExceptions' => true]
);
```
## Internal Encoder/Decoder
`Laminas\Json` has two different modes depending if ext/json is enabled in your PHP
installation or not. If `ext/json` is installed, laminas-json will use the
`json_encode()` and `json_decode()` functions for encoding and decoding JSON. If
`ext/json` is not installed, a Laminas implementation in PHP code is used
for en/decoding. This is considerably slower than using the PHP extension, but
behaves exactly the same.
Sometimes you might want to use the laminas-json encoder/decoder even if you have
`ext/json` installed. You can achieve this by calling:
```php
Laminas\Json\Json::$useBuiltinEncoderDecoder = true;
```
## JSON Expressions
JavaScript makes heavy use of anonymous function callbacks, which can be saved
within JSON object variables. They only work if not returned inside double
quotes, which laminas-json implements by default. With the Expression support for
laminas-json, you can encode JSON objects with valid JavaScript callbacks.
This works when either `json_encode()` or the internal encoder is used.
A JavaScript callback is represented using the `Laminas\Json\Expr` object. It
implements the value object pattern and is immutable. You can set the JavaScript
expression as the first constructor argument. By default
`Laminas\Json\Json::encode()` does not encode JavaScript callbacks; you have to
pass the option `enableJsonExprFinder` and set it to `TRUE` when calling the
`encode()` method. If enabled, the expression support works for all nested
expressions in large object structures.
As an example:
```php
$data = [
'onClick' => new Laminas\Json\Expr(
'function() {'
. 'alert("I am a valid JavaScript callback created by Laminas\\Json");
. '}'
),
'other' => 'no expression',
];
$jsonObjectWithExpression = Laminas\Json\Json::encode(
$data,
false,
['enableJsonExprFinder' => true]
);
```
PK l}P1ʥ docs/book/migration/v2-to-v3.mdnu W+A # Migrating from v2 to v3
Version 3 is the first significant departure in the laminas-json API. In
particular, it features the removal of two features to new packages.
## laminas-json-server
The `Laminas\Json\Server` subcomponent was extracted to a new component,
[laminas-json-server](https://docs.laminas.dev/laminas-json-server). Install it
using:
```bash
$ composer install laminas/laminas-json-server
```
All classes and functionality remain the same as in previous versions of
laminas-json.
## XML to JSON support
v2 releases of laminas-json provided `Laminas\Json\Json::fromXml()`, which could be
used to convert an XML document to JSON. This functionality has been extracted
to a new component, [laminas-xml2json](https://docs.laminas.dev/laminas-xml2json).
Install it using:
```bash
$ composer install laminas/laminas-xml2json
```
In order to use the functionality, you will need to modify your calls from
`Laminas\Json\Json::fromXml()` to instead use `Laminas\Xml2Json\Xml2Json::fromXml()`.
PK l}P9 docs/book/basics.mdnu W+A # Basic Usage
Usage of laminas-json involves using two public static methods:
`Laminas\Json\Json::encode()` and `Laminas\Json\Json::decode()`.
```php
// Decode a JSON value to PHP:
$phpNative = Laminas\Json\Json::decode($encodedValue);
// Encode a PHP value to JSON:
$json = Laminas\Json\Json::encode($phpNative);
```
> ### ext/json
>
> By default, the above two calls will proxy to the `json_decode()` and
> `json_encode()` functions of `ext/json`, which is bundled in default
> installations of PHP. Using laminas-json, however, ensures that the functionality
> works regardless of whether or not the extension is available. Additionally,
> the component provides some features not found in `ext/json`, such as
> encoding native JSON expressions, communicating class inheritance, and
> customizations around pretty printing.
## Pretty-printing JSON
Sometimes, it may be hard to explore JSON data generated by
`Laminas\Json\Json::encode()`, since it has no spacing or indentation. In order to
make it easier, `Laminas\Json\Json` allows you to pretty-print JSON data in the
human-readable format with `Laminas\Json\Json::prettyPrint()`.
```php
// Encode it to return to the client:
$json = Laminas\Json\Json::encode($phpNative);
if ($debug) {
echo Laminas\Json\Json::prettyPrint($json, array('indent' => ' '));
}
```
The second, optional, argument to `Laminas\Json\Json::prettyPrint()` is an option
array. Option `indent` allows providing an indentation string; by default, it
uses four spaces.
PK l}PSZ docs/book/intro.mdnu W+A # Introduction
laminas-json provides convenience methods for serializing native PHP to JSON and
decoding JSON to native PHP. For more information on JSON, [visit the JSON
project site](http://www.json.org/).
JSON, JavaScript Object Notation, can be used for data interchange between
JavaScript and other languages. Since JSON can be directly evaluated by
JavaScript, it is a more efficient and lightweight format than XML for
exchanging data with JavaScript clients.
PK l}P
LICENSE.mdnu W+A PK l}P%'\ \ COPYRIGHT.mdnu W+A PK l}P- .travis.ymlnu W+A PK l}Px= =
.coveralls.ymlnu W+A PK l}P=
phpcs.xmlnu W+A PK l}PT\ \ README.mdnu W+A PK l}P@ݫ
.gitignorenu W+A PK l}PRR " ^ src/Exception/RuntimeException.phpnu W+A PK l}P)7+o o $ < src/Exception/RecursionException.phpnu W+A PK l}PZ Z $ src/Exception/ExceptionInterface.phpnu W+A PK l}PMk"S ( src/Exception/BadMethodCallException.phpnu W+A PK l}PTa * src/Exception/InvalidArgumentException.phpnu W+A PK l}P:w@D @D src/Decoder.phpnu W+A PK l}Pdg g a src/Expr.phpnu W+A PK l}P4I I g src/Encoder.phpnu W+A PK l}PXĺG6 G6 { src/Json.phpnu W+A PK l}P3f
mkdocs.ymlnu W+A PK l}P;bJ J .github/workflows/docs-build.ymlnu W+A PK l}PRa .gitattributesnu W+A PK l}P`n n ( test/TestAsset/TestIteratorAggregate.phpnu W+A PK l}PA4 test/TestAsset/ToJSONClass.phpnu W+A PK l}P+ + . test/TestAsset/JsonSerializableBuiltinImpl.phpnu W+A PK l}Ps + F test/TestAsset/Laminas11167ToArrayClass.phpnu W+A PK l}PZ . test/TestAsset/JsonSerializableLaminasImpl.phpnu W+A PK l}PgB: : 1 test/TestAsset/Laminas11167ToArrayToJsonClass.phpnu W+A PK l}P 3 test/TestAsset/TestObject.phpnu W+A PK l}P|| test/TestAsset/Item.phpnu W+A PK l}P2<B B ! test/TestAsset/ToJSONWithExpr.phpnu W+A PK l}PH m m test/TestAsset/Bar.phpnu W+A PK l}Pw test/JsonTest.phpnu W+A PK l}PR9 9 ӑ phpunit.xml.distnu W+A PK l}PzJ^ ^ L CHANGELOG.mdnu W+A PK l}PYQ@ @
composer.jsonnu W+A PK l}P`M M c docs/book/advanced.mdnu W+A PK l}P1ʥ docs/book/migration/v2-to-v3.mdnu W+A PK l}P9 6 docs/book/basics.mdnu W+A PK l}PSZ \ docs/book/intro.mdnu W+A PK % % s