Skip to content

Commit 015f8dd

Browse files
authored
Extract Utf8Validator class (#97)
* CS fixes, php5.3 support fixes * Deduplicate exception throwing and fix InvalidEncodingException data types * Extract Utf8Validator into its own class
1 parent 703485b commit 015f8dd

10 files changed

Lines changed: 559 additions & 526 deletions

File tree

.github/workflows/lint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: "Lint PHP files"
3737
run: |
3838
hasErrors=0
39-
for f in $(find src/ tests/ -type f -name '*.php' ! -path '*/vendor/*' ! -path '*/Fixtures/*')
39+
for f in bin/jsonlint $(find src/ tests/ -type f -name '*.php' ! -path '*/vendor/*' ! -path '*/Fixtures/*')
4040
do
4141
{ error="$(php -derror_reporting=-1 -ddisplay_errors=1 -l -f $f 2>&1 1>&3 3>&-)"; } 3>&1;
4242
if [ "$error" != "" ]; then

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ You can also pass additional flags to `JsonParser::lint/parse` that tweak the fu
3636
- `JsonParser::PARSE_TO_ASSOC` parses to associative arrays instead of stdClass objects.
3737
- `JsonParser::ALLOW_COMMENTS` parses while allowing (and ignoring) inline `//` and multiline `/* */` comments in the JSON document.
3838
- `JsonParser::ALLOW_DUPLICATE_KEYS_TO_ARRAY` collects duplicate keys. e.g. if you have two `foo` keys the `foo` key will become an object (or array in assoc mode) with all `foo` values accessible as an array in `$result->foo->__duplicates__` (or `$result['foo']['__duplicates__']` in assoc mode).
39+
- `JsonParser::VALIDATE_UTF8_ENCODING` validates that the whole input is well-formed UTF-8 (as defined by [RFC 3629](https://www.rfc-editor.org/rfc/rfc3629)) before parsing, throwing an `InvalidEncodingException` that points at the first invalid byte otherwise.
3940

4041
Example:
4142

@@ -49,6 +50,28 @@ try {
4950
}
5051
```
5152

53+
Validating UTF-8 encoding
54+
-------------------------
55+
56+
The UTF-8 validation behind the `VALIDATE_UTF8_ENCODING` flag is also available as a
57+
standalone, reusable utility through the `Seld\JsonLint\Utf8Validator` class, should you
58+
need to validate UTF-8 encoding outside of JSON parsing:
59+
60+
```php
61+
use Seld\JsonLint\Utf8Validator;
62+
use Seld\JsonLint\InvalidEncodingException;
63+
64+
try {
65+
// returns void on success, throws on the first invalid byte
66+
Utf8Validator::validate($string);
67+
} catch (InvalidEncodingException $e) {
68+
// getMessage() describes the offending byte and its position
69+
echo $e->getMessage();
70+
// getDetails() returns that position information as an array
71+
$details = $e->getDetails();
72+
}
73+
```
74+
5275
> **Note:** This library is meant to parse JSON while providing good error messages on failure. There is no way it can be as fast as php native `json_decode()`.
5376
>
5477
> It is recommended to parse with `json_decode`, and when it fails parse again with seld/jsonlint to get a proper error message back to the user. See for example [how Composer uses this library](https://github.com/composer/composer/blob/56edd53046fd697d32b2fd2fbaf45af5d7951671/src/Composer/Json/JsonFile.php#L283-L318):

bin/jsonlint

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,7 @@ if (isset($_SERVER['argc']) && $_SERVER['argc'] > 1) {
5454
}
5555
}
5656

57-
$flags = getFlags(
58-
$allowComments,
59-
$detectKeyConflicts,
60-
$validateUTF8Encoding,
61-
);
57+
$flags = getFlags($allowComments, $detectKeyConflicts, $validateUTF8Encoding);
6258

6359
if (!empty($files)) {
6460
// file linting
@@ -80,15 +76,19 @@ if (!empty($files)) {
8076
}
8177
}
8278

83-
function getFlags(
84-
$allowComments = false,
85-
$detectKeyConflicts = false,
86-
$validateUTF8Encoding = false,
87-
){
79+
function getFlags($allowComments = false, $detectKeyConflicts = false, $validateUTF8Encoding = false)
80+
{
8881
$flags = 0;
89-
if($allowComments){ $flags |= JsonParser::ALLOW_COMMENTS; }
90-
if($detectKeyConflicts){ $flags |= JsonParser::DETECT_KEY_CONFLICTS; }
91-
if($validateUTF8Encoding){ $flags |= JsonParser::VALIDATE_UTF8_ENCODING; }
82+
if ($allowComments) {
83+
$flags |= JsonParser::ALLOW_COMMENTS;
84+
}
85+
if ($detectKeyConflicts) {
86+
$flags |= JsonParser::DETECT_KEY_CONFLICTS;
87+
}
88+
if ($validateUTF8Encoding) {
89+
$flags |= JsonParser::VALIDATE_UTF8_ENCODING;
90+
}
91+
9292
return $flags;
9393
}
9494

@@ -129,6 +129,7 @@ function lintFile($file, $quiet = false, $flags = 0)
129129
if (!$quiet) {
130130
echo 'Valid JSON (' . $file . ')' . PHP_EOL;
131131
}
132+
132133
return true;
133134
}
134135

phpstan.neon.dist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ parameters:
1212
- tests/
1313

1414
ignoreErrors:
15-
- '/Method JsonParserTest::[a-zA-Z0-9]+\(\) has no return type/'
15+
- '/Method [a-zA-Z0-9]+Test::[a-zA-Z0-9]+\(\) has no return type/'

src/Seld/JsonLint/InvalidEncodingException.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
class InvalidEncodingException extends ParsingException
1818
{
1919
/**
20-
* @var array{key: string, line: int}
20+
* @var array{key: string, current_octet: int|null, continuation_octet_needed: int, offset_in_octets_from_string_start: int, offset_in_characters_from_string_start: int, character_start_position_from_string_start: int, line: int, offset_in_octets_from_line_start: int, offset_in_characters_from_line_start: int, character_start_position_from_line_start: int, current_continuation_octet_minimum: int, current_continuation_octet_maximum: int}
2121
*/
2222
protected $details;
2323

2424
/**
2525
* @param string $message
2626
* @param string $key
27-
* @phpstan-param array{line: int} $details
27+
* @phpstan-param array{current_octet: int|null, continuation_octet_needed: int, offset_in_octets_from_string_start: int, offset_in_characters_from_string_start: int, character_start_position_from_string_start: int, line: int, offset_in_octets_from_line_start: int, offset_in_characters_from_line_start: int, character_start_position_from_line_start: int, current_continuation_octet_minimum: int, current_continuation_octet_maximum: int} $details
2828
*/
2929
public function __construct($message, $key, array $details)
3030
{
@@ -41,7 +41,7 @@ public function getKey()
4141
}
4242

4343
/**
44-
* @phpstan-return array{key: string, line: int}
44+
* @phpstan-return array{key: string, current_octet: int|null, continuation_octet_needed: int, offset_in_octets_from_string_start: int, offset_in_characters_from_string_start: int, character_start_position_from_string_start: int, line: int, offset_in_octets_from_line_start: int, offset_in_characters_from_line_start: int, character_start_position_from_line_start: int, current_continuation_octet_minimum: int, current_continuation_octet_maximum: int}
4545
*/
4646
public function getDetails()
4747
{

0 commit comments

Comments
 (0)