├── .meta-storm.xml
├── .phpunit-watcher.yml
├── .styleci.yml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── composer.json
├── infection.json.dist
├── psalm.xml
├── rector.php
└── src
└── Json.php
/.meta-storm.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.phpunit-watcher.yml:
--------------------------------------------------------------------------------
1 | watch:
2 | directories:
3 | - src
4 | - tests
5 | fileMask: '*.php'
6 | notifications:
7 | passingTests: false
8 | failingTests: false
9 | phpunit:
10 | binaryPath: vendor/bin/phpunit
11 | timeout: 180
12 |
--------------------------------------------------------------------------------
/.styleci.yml:
--------------------------------------------------------------------------------
1 | preset: psr12
2 | risky: true
3 |
4 | version: 8.1
5 |
6 | finder:
7 | exclude:
8 | - docs
9 | - vendor
10 |
11 | enabled:
12 | - alpha_ordered_traits
13 | - array_indentation
14 | - array_push
15 | - combine_consecutive_issets
16 | - combine_consecutive_unsets
17 | - combine_nested_dirname
18 | - declare_strict_types
19 | - dir_constant
20 | - fully_qualified_strict_types
21 | - function_to_constant
22 | - hash_to_slash_comment
23 | - is_null
24 | - logical_operators
25 | - magic_constant_casing
26 | - magic_method_casing
27 | - method_separation
28 | - modernize_types_casting
29 | - native_function_casing
30 | - native_function_type_declaration_casing
31 | - no_alias_functions
32 | - no_empty_comment
33 | - no_empty_phpdoc
34 | - no_empty_statement
35 | - no_extra_block_blank_lines
36 | - no_short_bool_cast
37 | - no_superfluous_elseif
38 | - no_unneeded_control_parentheses
39 | - no_unneeded_curly_braces
40 | - no_unneeded_final_method
41 | - no_unset_cast
42 | - no_unused_imports
43 | - no_unused_lambda_imports
44 | - no_useless_else
45 | - no_useless_return
46 | - normalize_index_brace
47 | - php_unit_dedicate_assert
48 | - php_unit_dedicate_assert_internal_type
49 | - php_unit_expectation
50 | - php_unit_mock
51 | - php_unit_mock_short_will_return
52 | - php_unit_namespaced
53 | - php_unit_no_expectation_annotation
54 | - phpdoc_no_empty_return
55 | - phpdoc_no_useless_inheritdoc
56 | - phpdoc_order
57 | - phpdoc_property
58 | - phpdoc_scalar
59 | - phpdoc_singular_inheritdoc
60 | - phpdoc_trim
61 | - phpdoc_trim_consecutive_blank_line_separation
62 | - phpdoc_type_to_var
63 | - phpdoc_types
64 | - phpdoc_types_order
65 | - print_to_echo
66 | - regular_callable_call
67 | - return_assignment
68 | - self_accessor
69 | - self_static_accessor
70 | - set_type_to_cast
71 | - short_array_syntax
72 | - short_list_syntax
73 | - simplified_if_return
74 | - single_quote
75 | - standardize_not_equals
76 | - ternary_to_null_coalescing
77 | - trailing_comma_in_multiline_array
78 | - unalign_double_arrow
79 | - unalign_equals
80 | - empty_loop_body_braces
81 | - integer_literal_case
82 | - union_type_without_spaces
83 |
84 | disabled:
85 | - function_declaration
86 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Yii JSON Change Log
2 |
3 | ## 1.0.1 under development
4 |
5 | - Bug #41: Fix bug with instances of extended `DateTime` class (@Tigrov)
6 | - Enh #41: Improve performance of `Json::encode()` method by 10-20% (@Tigrov)
7 | - Chg #56: Change PHP constraint in `composer.json` to `~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0` (@vjik)
8 |
9 | ## 1.0.0 August 26, 2020
10 |
11 | - Initial release.
12 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright © 2008 by Yii Software ()
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 | * Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in
12 | the documentation and/or other materials provided with the
13 | distribution.
14 | * Neither the name of Yii Software nor the names of its
15 | contributors may be used to endorse or promote products derived
16 | from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 | POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Yii JSON
6 |
7 |
8 |
9 | [](https://packagist.org/packages/yiisoft/json)
10 | [](https://packagist.org/packages/yiisoft/json)
11 | [](https://github.com/yiisoft/json/actions/workflows/build.yml)
12 | [](https://codecov.io/gh/yiisoft/json)
13 | [](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/json/master)
14 | [](https://github.com/yiisoft/json/actions/workflows/static.yml?query=branch%3Amaster)
15 | [](https://shepherd.dev/github/yiisoft/json)
16 |
17 | The package provides methods to encode and decode JSON.
18 |
19 | - It always throws `\JsonException` instead of returning false on error.
20 | - It has sensible defaults, so you don't have to specify flags all the time.
21 | - It has handy method to encode for HTML safely.
22 | - It handles `\JsonSerializable`, `\DateTimeInterface`, and `\SimpleXMLElement` well.
23 |
24 | ## Requirements
25 |
26 | - PHP 7.4 or higher.
27 | - `JSON` PHP extension.
28 | - `SimpleXML` PHP extension.
29 |
30 | ## Installation
31 |
32 | The package could be installed with [Composer](https://getcomposer.org):
33 |
34 | ```shell
35 | composer require yiisoft/json
36 | ```
37 |
38 | ## General usage
39 |
40 | Encoding:
41 |
42 | ```php
43 | use \Yiisoft\Json\Json;
44 |
45 | $data = ['name' => 'Alex', 'team' => 'Yii'];
46 | $json = Json::encode($data);
47 | ```
48 |
49 | Encoding for HTML:
50 |
51 | ```php
52 | use \Yiisoft\Json\Json;
53 |
54 | $data = ['name' => 'Alex', 'team' => 'Yii'];
55 | $json = Json::htmlEncode($data);
56 | ```
57 |
58 | Decoding:
59 |
60 | ```php
61 | use \Yiisoft\Json\Json;
62 |
63 | $json = '{"name":"Alex","team":"Yii"}';
64 | $data = Json::decode($json);
65 | ```
66 |
67 | ## Documentation
68 |
69 | - [Internals](docs/internals.md)
70 |
71 | If you need help or have a question, the [Yii Forum](https://forum.yiiframework.com/c/yii-3-0/63) is a good place for that.
72 | You may also check out other [Yii Community Resources](https://www.yiiframework.com/community).
73 |
74 | ## License
75 |
76 | The Yii JSON is free software. It is released under the terms of the BSD License.
77 | Please see [`LICENSE`](./LICENSE.md) for more information.
78 |
79 | Maintained by [Yii Software](https://www.yiiframework.com/).
80 |
81 | ## Support the project
82 |
83 | [](https://opencollective.com/yiisoft)
84 |
85 | ## Follow updates
86 |
87 | [](https://www.yiiframework.com/)
88 | [](https://twitter.com/yiiframework)
89 | [](https://t.me/yii3en)
90 | [](https://www.facebook.com/groups/yiitalk)
91 | [](https://yiiframework.com/go/slack)
92 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "yiisoft/json",
3 | "type": "library",
4 | "description": "Yii JSON encoding and decoding",
5 | "keywords": [
6 | "json"
7 | ],
8 | "homepage": "https://www.yiiframework.com/",
9 | "license": "BSD-3-Clause",
10 | "support": {
11 | "issues": "https://github.com/yiisoft/json/issues?state=open",
12 | "source": "https://github.com/yiisoft/json",
13 | "forum": "https://www.yiiframework.com/forum/",
14 | "wiki": "https://www.yiiframework.com/wiki/",
15 | "irc": "ircs://irc.libera.chat:6697/yii",
16 | "chat": "https://t.me/yii3en"
17 | },
18 | "funding": [
19 | {
20 | "type": "opencollective",
21 | "url": "https://opencollective.com/yiisoft"
22 | },
23 | {
24 | "type": "github",
25 | "url": "https://github.com/sponsors/yiisoft"
26 | }
27 | ],
28 | "require": {
29 | "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
30 | "ext-json": "*",
31 | "ext-simplexml": "*"
32 | },
33 | "require-dev": {
34 | "maglnet/composer-require-checker": "^3.8 || ^4.2",
35 | "phpunit/phpunit": "^9.6.22",
36 | "rector/rector": "^2.0.8",
37 | "roave/infection-static-analysis-plugin": "^1.18",
38 | "spatie/phpunit-watcher": "^1.23.6",
39 | "vimeo/psalm": "^4.30 || ^5.26.1 || ^6.5"
40 | },
41 | "autoload": {
42 | "psr-4": {
43 | "Yiisoft\\Json\\": "src"
44 | }
45 | },
46 | "autoload-dev": {
47 | "psr-4": {
48 | "Yiisoft\\Json\\Tests\\": "tests"
49 | }
50 | },
51 | "config": {
52 | "sort-packages": true,
53 | "bump-after-update": "dev",
54 | "allow-plugins": {
55 | "infection/extension-installer": true,
56 | "composer/package-versions-deprecated": true
57 | }
58 | },
59 | "prefer-stable": true,
60 | "scripts": {
61 | "test": "phpunit --testdox --no-interaction",
62 | "test-watch": "phpunit-watcher watch"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/infection.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "directories": [
4 | "src"
5 | ]
6 | },
7 | "logs": {
8 | "text": "php:\/\/stderr",
9 | "stryker": {
10 | "report": "master"
11 | }
12 | },
13 | "mutators": {
14 | "@default": true
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
12 | __DIR__ . '/src',
13 | __DIR__ . '/tests',
14 | ]);
15 |
16 | // register a single rule
17 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
18 |
19 | // define sets of rules
20 | $rectorConfig->sets([
21 | LevelSetList::UP_TO_PHP_74,
22 | ]);
23 |
24 | $rectorConfig->skip([
25 | ClosureToArrowFunctionRector::class,
26 | ]);
27 | };
28 |
--------------------------------------------------------------------------------
/src/Json.php:
--------------------------------------------------------------------------------
1 | $depth
41 | *
42 | * @throws JsonException if there is any encoding error.
43 | *
44 | * @return string The encoding result.
45 | */
46 | public static function encode(
47 | $value,
48 | int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR,
49 | int $depth = 512
50 | ): string {
51 | if (is_array($value)) {
52 | $value = self::processArray($value);
53 | } elseif (is_object($value)) {
54 | $value = self::processObject($value);
55 | }
56 |
57 | /**
58 | * @var string We use flag `JSON_THROW_ON_ERROR`, so `json_encode` never returns `false`.
59 | */
60 | return json_encode($value, JSON_THROW_ON_ERROR | $options, $depth);
61 | }
62 |
63 | /**
64 | * Encodes the given value into a JSON string HTML-escaping entities so it is safe to be embedded in HTML code.
65 | *
66 | * Note that data encoded as JSON must be UTF-8 encoded according to the JSON specification.
67 | * You must ensure strings passed to this method have proper encoding before passing them.
68 | *
69 | * @param mixed $value The data to be encoded.
70 | *
71 | * @throws JsonException If there is any encoding error.
72 | *
73 | * @return string The encoding result.
74 | */
75 | public static function htmlEncode($value): string
76 | {
77 | return self::encode(
78 | $value,
79 | JSON_UNESCAPED_UNICODE | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_THROW_ON_ERROR
80 | );
81 | }
82 |
83 | /**
84 | * Decodes the given JSON string into a PHP data structure.
85 | *
86 | * @param string $json The JSON string to be decoded.
87 | * @param bool $asArray Whether to return objects in terms of associative arrays.
88 | * @param int $depth The recursion depth.
89 | * @param int $options The decode options.
90 | *
91 | * @psalm-param int<1, 2147483647> $depth
92 | *
93 | * @throws JsonException If there is any decoding error.
94 | *
95 | * @return mixed The PHP data.
96 | */
97 | public static function decode(
98 | string $json,
99 | bool $asArray = true,
100 | int $depth = 512,
101 | int $options = JSON_THROW_ON_ERROR
102 | ) {
103 | if ($json === '') {
104 | return null;
105 | }
106 | return json_decode($json, $asArray, $depth, JSON_THROW_ON_ERROR | $options);
107 | }
108 |
109 | /**
110 | * Pre-processes the array before sending it to `json_encode()`.
111 | *
112 | * @param array $data The array to be processed.
113 | *
114 | * @return array The processed array.
115 | */
116 | private static function processArray(array $data): array
117 | {
118 | foreach ($data as $key => $value) {
119 | if (is_array($value)) {
120 | $data[$key] = self::processArray($value);
121 | } elseif (is_object($value)) {
122 | $data[$key] = self::processObject($value);
123 | }
124 | }
125 |
126 | return $data;
127 | }
128 |
129 | /**
130 | * Pre-processes the object before sending it to `json_encode()`.
131 | *
132 | * @param object $data The object to be processed.
133 | *
134 | * @return mixed The processed data.
135 | */
136 | private static function processObject(object $data)
137 | {
138 | if ($data instanceof JsonSerializable) {
139 | $data = $data->jsonSerialize();
140 |
141 | if (is_array($data)) {
142 | return self::processArray($data);
143 | }
144 |
145 | if (is_object($data)) {
146 | return self::processObject($data);
147 | }
148 |
149 | return $data;
150 | }
151 |
152 | if ($data instanceof DateTimeInterface) {
153 | return $data;
154 | }
155 |
156 | if ($data instanceof SimpleXMLElement) {
157 | return (array)$data ?: new stdClass();
158 | }
159 |
160 | if ($data instanceof Traversable) {
161 | return self::processArray(iterator_to_array($data)) ?: new stdClass();
162 | }
163 |
164 | return self::processArray(get_object_vars($data)) ?: new stdClass();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------