| Current Path : /var/www/clients/client3/web2/web/vendor/laminas/laminas-json/src/ |
| Current File : /var/www/clients/client3/web2/web/vendor/laminas/laminas-json/src/Json.php |
<?php
namespace Laminas\Json;
use Laminas\Json\Exception\RuntimeException;
use SplQueue;
use function array_pop;
use function count;
use function end;
use function function_exists;
use function is_array;
use function is_int;
use function is_object;
use function json_decode;
use function json_encode;
use function json_last_error;
use function method_exists;
use function preg_match;
use function sprintf;
use function str_repeat;
use function str_replace;
use function strlen;
use function trim;
use const JSON_ERROR_CTRL_CHAR;
use const JSON_ERROR_DEPTH;
use const JSON_ERROR_NONE;
use const JSON_ERROR_SYNTAX;
use const JSON_HEX_AMP;
use const JSON_HEX_APOS;
use const JSON_HEX_QUOT;
use const JSON_HEX_TAG;
use const JSON_PRETTY_PRINT;
/**
* Class for encoding to and decoding from JSON.
*/
class Json
{
/**
* How objects should be encoded: as arrays or as stdClass.
*
* TYPE_ARRAY is 1, which also conveniently evaluates to a boolean true
* value, allowing it to be used with ext/json's functions.
*/
public const TYPE_ARRAY = 1;
public const TYPE_OBJECT = 0;
/**
* Whether or not to use the built-in PHP functions.
*
* @var bool
*/
public static $useBuiltinEncoderDecoder = false;
/**
* Decodes the given $encodedValue string from JSON.
*
* Uses json_decode() from ext/json if available.
*
* @param string $encodedValue Encoded in JSON format
* @param int $objectDecodeType Optional; flag indicating how to decode
* objects. See {@link Decoder::decode()} for details.
* @return mixed
* @throws RuntimeException
*/
public static function decode($encodedValue, $objectDecodeType = self::TYPE_OBJECT)
{
$encodedValue = (string) $encodedValue;
if (function_exists('json_decode') && static::$useBuiltinEncoderDecoder !== true) {
return self::decodeViaPhpBuiltIn($encodedValue, $objectDecodeType);
}
return Decoder::decode($encodedValue, $objectDecodeType);
}
/**
* Encode the mixed $valueToEncode into the JSON format
*
* Encodes using ext/json's json_encode() if available.
*
* NOTE: Object should not contain cycles; the JSON format
* does not allow object reference.
*
* NOTE: Only public variables will be encoded
*
* NOTE: Encoding native javascript expressions are possible using Laminas\Json\Expr.
* You can enable this by setting $options['enableJsonExprFinder'] = true
*
* @see Laminas\Json\Expr
*
* @param mixed $valueToEncode
* @param bool $cycleCheck Optional; whether or not to check for object recursion; off by default
* @param array $options Additional options used during encoding
* @return string JSON encoded object
*/
public static function encode($valueToEncode, $cycleCheck = false, array $options = [])
{
if (is_object($valueToEncode)) {
if (method_exists($valueToEncode, 'toJson')) {
return $valueToEncode->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 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 = $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
* @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;
}
}