Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: "Lint PHP files"
run: |
hasErrors=0
for f in $(find src/ tests/ -type f -name '*.php' ! -path '*/vendor/*' ! -path '*/Fixtures/*')
for f in bin/jsonlint $(find src/ tests/ -type f -name '*.php' ! -path '*/vendor/*' ! -path '*/Fixtures/*')
do
{ error="$(php -derror_reporting=-1 -ddisplay_errors=1 -l -f $f 2>&1 1>&3 3>&-)"; } 3>&1;
if [ "$error" != "" ]; then
Expand Down
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ You can also pass additional flags to `JsonParser::lint/parse` that tweak the fu
- `JsonParser::PARSE_TO_ASSOC` parses to associative arrays instead of stdClass objects.
- `JsonParser::ALLOW_COMMENTS` parses while allowing (and ignoring) inline `//` and multiline `/* */` comments in the JSON document.
- `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).
- `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.

Example:

Expand All @@ -49,6 +50,28 @@ try {
}
```

Validating UTF-8 encoding
-------------------------

The UTF-8 validation behind the `VALIDATE_UTF8_ENCODING` flag is also available as a
standalone, reusable utility through the `Seld\JsonLint\Utf8Validator` class, should you
need to validate UTF-8 encoding outside of JSON parsing:

```php
use Seld\JsonLint\Utf8Validator;
use Seld\JsonLint\InvalidEncodingException;

try {
// returns void on success, throws on the first invalid byte
Utf8Validator::validate($string);
} catch (InvalidEncodingException $e) {
// getMessage() describes the offending byte and its position
echo $e->getMessage();
// getDetails() returns that position information as an array
$details = $e->getDetails();
}
```

> **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()`.
>
> 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):
Expand Down
27 changes: 14 additions & 13 deletions bin/jsonlint
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,7 @@ if (isset($_SERVER['argc']) && $_SERVER['argc'] > 1) {
}
}

$flags = getFlags(
$allowComments,
$detectKeyConflicts,
$validateUTF8Encoding,
);
$flags = getFlags($allowComments, $detectKeyConflicts, $validateUTF8Encoding);

if (!empty($files)) {
// file linting
Expand All @@ -80,15 +76,19 @@ if (!empty($files)) {
}
}

function getFlags(
$allowComments = false,
$detectKeyConflicts = false,
$validateUTF8Encoding = false,
){
function getFlags($allowComments = false, $detectKeyConflicts = false, $validateUTF8Encoding = false)
{
$flags = 0;
if($allowComments){ $flags |= JsonParser::ALLOW_COMMENTS; }
if($detectKeyConflicts){ $flags |= JsonParser::DETECT_KEY_CONFLICTS; }
if($validateUTF8Encoding){ $flags |= JsonParser::VALIDATE_UTF8_ENCODING; }
if ($allowComments) {
$flags |= JsonParser::ALLOW_COMMENTS;
}
if ($detectKeyConflicts) {
$flags |= JsonParser::DETECT_KEY_CONFLICTS;
}
if ($validateUTF8Encoding) {
$flags |= JsonParser::VALIDATE_UTF8_ENCODING;
}

return $flags;
}

Expand Down Expand Up @@ -129,6 +129,7 @@ function lintFile($file, $quiet = false, $flags = 0)
if (!$quiet) {
echo 'Valid JSON (' . $file . ')' . PHP_EOL;
}

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ parameters:
- tests/

ignoreErrors:
- '/Method JsonParserTest::[a-zA-Z0-9]+\(\) has no return type/'
- '/Method [a-zA-Z0-9]+Test::[a-zA-Z0-9]+\(\) has no return type/'
6 changes: 3 additions & 3 deletions src/Seld/JsonLint/InvalidEncodingException.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
class InvalidEncodingException extends ParsingException
{
/**
* @var array{key: string, line: int}
* @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}
*/
protected $details;

/**
* @param string $message
* @param string $key
* @phpstan-param array{line: int} $details
* @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
*/
public function __construct($message, $key, array $details)
{
Expand All @@ -41,7 +41,7 @@ public function getKey()
}

/**
* @phpstan-return array{key: string, line: int}
* @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}
*/
public function getDetails()
{
Expand Down
Loading