Passed
Pull Request — master (#77)
by Théo
02:35
created

Json::decodeFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
eloc 2
nc 1
nop 2
cc 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Json;
16
17
use JsonSchema\Validator;
18
use Seld\JsonLint\JsonParser;
19
use Seld\JsonLint\ParsingException;
20
use stdClass;
21
use function KevinGH\Box\FileSystem\file_contents;
22
23
final class Json
24
{
25
    private $linter;
26
27
    public function __construct()
28
    {
29
        $this->linter = new JsonParser();
30
    }
31
32
    /**
33
     * @param string $json
34
     * @param bool   $assoc
35
     *
36
     * @throws ParsingException
37
     *
38
     * @return array|stdClass
39
     */
40
    public function decode(string $json, bool $assoc = false)
41
    {
42
        $data = json_decode($json, $assoc);
43
44
        if (JSON_ERROR_NONE !== ($error = json_last_error())) {
45
            if (JSON_ERROR_UTF8 === $error) {
46
                throw JsonValidationException::createDecodeException($error);
47
            }
48
49
            $this->lint($json);
50
51
            if (($result = $this->linter->lint($json)) instanceof ParsingException) {
52
                throw $result;
53
            }
54
        }
55
56
        return false === $assoc ? (object) $data : $data;   // If JSON is an empty JSON json_decode returns an empty
57
                                                            // array instead of an stdClass instance
58
    }
59
60
    public function decodeFile(string $file, bool $assoc = false): stdClass
61
    {
62
        $json = file_contents($file);
63
64
        return $this->decode($json, $assoc);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->decode($json, $assoc) could return the type array which is incompatible with the type-hinted return stdClass. Consider adding an additional type-check to rule them out.
Loading history...
65
    }
66
67
    public function lint(string $json): void
68
    {
69
        $result = $this->linter->lint($json);
70
71
        if ($result instanceof ParsingException) {
72
            throw $result;
73
        }
74
    }
75
76
    /**
77
     * Validates the decoded JSON data.
78
     *
79
     * @param string   $file   The JSON file
80
     * @param stdClass $json   The decoded JSON data
81
     * @param stdClass $schema The JSON schema
82
     *
83
     * @throws JsonValidationException If the JSON data failed validation
84
     */
85
    public function validate(string $file, stdClass $json, stdClass $schema): void
86
    {
87
        $validator = new Validator();
88
        $validator->check($json, $schema);
89
90
        if (!$validator->isValid()) {
91
            $errors = [];
92
93
            foreach ((array) $validator->getErrors() as $error) {
94
                $errors[] = ($error['property'] ? $error['property'].' : ' : '').$error['message'];
95
            }
96
97
            $message = [] !== $errors
98
                ? "\"$file\" does not match the expected JSON schema:\n  - ".implode("\n  - ", $errors)
99
                : "\"$file\" does not match the expected JSON schema."
100
            ;
101
102
            throw new JsonValidationException($message, $file, $errors);
103
        }
104
    }
105
}
106