You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
9.5 KiB
302 lines
9.5 KiB
<?php
|
|
|
|
/**
|
|
*
|
|
* @package Duplicator
|
|
* @copyright (c) 2022, Snap Creek LLC
|
|
*/
|
|
|
|
namespace Duplicator\Libs\Snap;
|
|
|
|
// phpcs:disable
|
|
require_once(__DIR__ . '/JsonSerializable.php');
|
|
// phpcs:enable
|
|
|
|
class SnapJson
|
|
{
|
|
/**
|
|
* Encode a variable into JSON, with some sanity checks.
|
|
*
|
|
* @since 4.1.0
|
|
*
|
|
* @param mixed $data Variable (usually an array or object) to encode as JSON.
|
|
* @param int $options Optional. Options to be passed to json_encode(). Default 0.
|
|
* @param int $depth Optional. Maximum depth to walk through $data. Must be
|
|
* greater than 0. Default 512.
|
|
*
|
|
* @return string|false The JSON encoded string, or false if it cannot be encoded.
|
|
*/
|
|
public static function jsonEncode($data, $options = 0, $depth = 512)
|
|
{
|
|
if (function_exists('wp_json_encode')) {
|
|
return wp_json_encode($data, $options, $depth);
|
|
}
|
|
|
|
/*
|
|
* json_encode() has had extra params added over the years.
|
|
* $options was added in 5.3, and $depth in 5.5.
|
|
* We need to make sure we call it with the correct arguments.
|
|
*/
|
|
if (version_compare(PHP_VERSION, '5.5', '>=')) {
|
|
$args = array($data, $options, $depth);
|
|
} elseif (version_compare(PHP_VERSION, '5.3', '>=')) {
|
|
$args = array($data, $options);
|
|
} else {
|
|
$args = array($data);
|
|
}
|
|
|
|
$preparedData = self::jsonPrepareData($data);
|
|
// Prepare the data for JSON serialization.
|
|
$args[0] = $preparedData;
|
|
|
|
$json = @call_user_func_array('json_encode', $args);
|
|
|
|
// If json_encode() was successful, no need to do more sanity checking.
|
|
// ... unless we're in an old version of PHP, and json_encode() returned
|
|
// a string containing 'null'. Then we need to do more sanity checking.
|
|
if (false !== $json && ( version_compare(PHP_VERSION, '5.5', '>=') || false === strpos($json, 'null') )) {
|
|
return $json;
|
|
}
|
|
|
|
try {
|
|
$args[0] = self::jsonSanityCheck($preparedData, $depth);
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
|
|
return call_user_func_array('json_encode', $args);
|
|
}
|
|
|
|
/**
|
|
* wp_json_encode with pretty print if define exists
|
|
*
|
|
* @param mixed $data Variable (usually an array or object) to encode as JSON.
|
|
* @param int $options Optional. Options to be passed to json_encode(). Default 0.
|
|
* @param int $depth Optional. Maximum depth to walk through $data. Must be
|
|
* greater than 0. Default 512.
|
|
*
|
|
* @return string|false The JSON encoded string, or false if it cannot be encoded.
|
|
*/
|
|
public static function jsonEncodePPrint($data, $options = 0, $depth = 512)
|
|
{
|
|
if (defined('JSON_PRETTY_PRINT')) {
|
|
// phpcs:ignore PHPCompatibility.Constants.NewConstants.json_pretty_printFound
|
|
return self::jsonEncode($data, JSON_PRETTY_PRINT | $options, $depth);
|
|
} else {
|
|
return self::jsonEncode($data, $options, $depth);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepares response data to be serialized to JSON.
|
|
*
|
|
* This supports the JsonSerializable interface for PHP 5.2-5.3 as well.
|
|
*
|
|
* @param mixed $data Native representation.
|
|
*
|
|
* @return bool|int|float|null|string|mixed[] Data ready for `json_encode()`.
|
|
*/
|
|
private static function jsonPrepareData($data)
|
|
{
|
|
if (
|
|
!defined('WP_JSON_SERIALIZE_COMPATIBLE') ||
|
|
WP_JSON_SERIALIZE_COMPATIBLE === false
|
|
) {
|
|
return $data;
|
|
}
|
|
|
|
switch (gettype($data)) {
|
|
case 'boolean':
|
|
case 'integer':
|
|
case 'double':
|
|
case 'string':
|
|
case 'NULL':
|
|
// These values can be passed through.
|
|
return $data;
|
|
|
|
case 'array':
|
|
// Arrays must be mapped in case they also return objects.
|
|
return array_map(array(__CLASS__, 'jsonPrepareData'), $data);
|
|
|
|
case 'object':
|
|
// If this is an incomplete object (__PHP_Incomplete_Class), bail.
|
|
if (!is_object($data)) {
|
|
return null;
|
|
}
|
|
|
|
if ($data instanceof \JsonSerializable) {
|
|
$data = $data->jsonSerialize();
|
|
} else {
|
|
$data = get_object_vars($data);
|
|
}
|
|
|
|
// Now, pass the array (or whatever was returned from jsonSerialize through).
|
|
return self::jsonPrepareData($data);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform sanity checks on data that shall be encoded to JSON.
|
|
*
|
|
* @ignore
|
|
* @since 4.1.0
|
|
* @access private
|
|
*
|
|
* @see wp_json_encode()
|
|
*
|
|
* @param mixed $data Variable (usually an array or object) to encode as JSON.
|
|
* @param int $depth Maximum depth to walk through $data. Must be greater than 0.
|
|
*
|
|
* @return mixed The sanitized data that shall be encoded to JSON.
|
|
*/
|
|
private static function jsonSanityCheck($data, $depth)
|
|
{
|
|
if ($depth < 0) {
|
|
throw new \Exception('Reached depth limit');
|
|
}
|
|
|
|
if ($data instanceof \JsonSerializable) {
|
|
$data = $data->jsonSerialize();
|
|
}
|
|
|
|
if (is_array($data)) {
|
|
$output = array();
|
|
foreach ($data as $id => $el) {
|
|
// Don't forget to sanitize the ID!
|
|
if (is_string($id)) {
|
|
$clean_id = self::jsonConvertString($id);
|
|
} else {
|
|
$clean_id = $id;
|
|
}
|
|
|
|
// Check the element type, so that we're only recursing if we really have to.
|
|
if (is_array($el) || is_object($el)) {
|
|
$output[$clean_id] = self::jsonSanityCheck($el, $depth - 1);
|
|
} elseif (is_string($el)) {
|
|
$output[$clean_id] = self::jsonConvertString($el);
|
|
} else {
|
|
$output[$clean_id] = $el;
|
|
}
|
|
}
|
|
} elseif (is_object($data)) {
|
|
$output = new \stdClass();
|
|
foreach ($data as $id => $el) {
|
|
if (is_string($id)) {
|
|
$clean_id = self::jsonConvertString($id);
|
|
} else {
|
|
$clean_id = $id;
|
|
}
|
|
|
|
if (is_array($el) || is_object($el)) {
|
|
$output->$clean_id = self::jsonSanityCheck($el, $depth - 1);
|
|
} elseif (is_string($el)) {
|
|
$output->$clean_id = self::jsonConvertString($el);
|
|
} else {
|
|
$output->$clean_id = $el;
|
|
}
|
|
}
|
|
} elseif (is_string($data)) {
|
|
return self::jsonConvertString($data);
|
|
} else {
|
|
return $data;
|
|
}
|
|
|
|
return $output;
|
|
}
|
|
|
|
/**
|
|
* Return json string
|
|
*
|
|
* @param string $string data
|
|
*
|
|
* @return string
|
|
*/
|
|
private static function jsonConvertString($string)
|
|
{
|
|
static $use_mb = null;
|
|
if (is_null($use_mb)) {
|
|
$use_mb = function_exists('mb_convert_encoding');
|
|
}
|
|
|
|
if ($use_mb) {
|
|
$encoding = mb_detect_encoding($string, mb_detect_order(), true);
|
|
if ($encoding) {
|
|
return mb_convert_encoding($string, 'UTF-8', $encoding);
|
|
} else {
|
|
return mb_convert_encoding($string, 'UTF-8', 'UTF-8');
|
|
}
|
|
} else {
|
|
return self::checkInvalidUTF8($string, true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for invalid UTF8 in a string.
|
|
*
|
|
* @param string $string The text which is to be checked.
|
|
* @param bool $strip Optional. Whether to attempt to strip out invalid UTF8. Default is false.
|
|
*
|
|
* @return string The checked text.
|
|
*/
|
|
public static function checkInvalidUTF8($string, $strip = false)
|
|
{
|
|
$string = (string) $string;
|
|
|
|
if (0 === strlen($string)) {
|
|
return '';
|
|
}
|
|
|
|
// Check for support for utf8 in the installed PCRE library once and store the result in a static
|
|
static $utf8_pcre = null;
|
|
if (!isset($utf8_pcre)) {
|
|
$utf8_pcre = @preg_match('/^./u', 'a');
|
|
}
|
|
// We can't demand utf8 in the PCRE installation, so just return the string in those cases
|
|
if (!$utf8_pcre) {
|
|
return $string;
|
|
}
|
|
|
|
// preg_match fails when it encounters invalid UTF8 in $string
|
|
if (1 === @preg_match('/^./us', $string)) {
|
|
return $string;
|
|
}
|
|
|
|
// Attempt to strip the bad chars if requested (not recommended)
|
|
if ($strip && function_exists('iconv')) {
|
|
return iconv('utf-8', 'utf-8', $string);
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
/**
|
|
* todo remove esc_attr wp function
|
|
*
|
|
* @param mixed $val object to be encoded
|
|
*
|
|
* @return string escaped json string
|
|
*/
|
|
public static function jsonEncodeEscAttr($val)
|
|
{
|
|
return esc_attr(json_encode($val));
|
|
}
|
|
|
|
/**
|
|
* this function return a json encoded string without quotes at the beginning and the end
|
|
*
|
|
* @param string $string json string
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getJsonWithoutQuotes($string)
|
|
{
|
|
if (!is_string($string)) {
|
|
throw new \Exception('the function getJsonStringWithoutQuotes take only strings');
|
|
}
|
|
|
|
return substr(self::jsonEncode($string), 1, -1);
|
|
}
|
|
}
|
|
|