JsonValidator::fromEncodedString()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use JsonSchema\Validator;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr7Middlewares\Middleware;
9
use Psr7Middlewares\Utils\AttributeTrait;
10
use Psr7Middlewares\Utils\CallableTrait;
11
12
class JsonValidator
13
{
14
    const KEY = 'JSON_VALIDATION_ERRORS';
15
16
    use AttributeTrait;
17
    use CallableTrait;
18
19
    /** @var \stdClass */
20
    private $schema;
21
22
    /** @var callable */
23
    private $errorHandler;
24
25
    /**
26
     * JsonSchema constructor.
27
     * Consider using one of the following factories instead of invoking the controller directly:
28
     *  - JsonValidator::fromFile()
29
     *  - JsonValidator::fromEncodedString()
30
     *  - JsonValidator::fromDecodedObject()
31
     *  - JsonValidator::fromArray().
32
     *
33
     * @param \stdClass $schema A JSON-decoded object-representation of the schema
34
     */
35
    public function __construct(\stdClass $schema)
36
    {
37
        $this->schema = $schema;
38
        $this->errorHandler = [$this, 'defaultErrorHandler'];
39
    }
40
41
    /**
42
     * @param \stdClass $schema
43
     *
44
     * @return static|callable
45
     */
46
    public static function fromDecodedObject(\stdClass $schema)
47
    {
48
        return new static($schema);
49
    }
50
51
    /**
52
     * @param \SplFileObject $file
53
     *
54
     * @return static|callable
55
     */
56
    public static function fromFile(\SplFileObject $file)
57
    {
58
        $schema = (object) [
59
            '$ref' => $file->getPathname(),
60
        ];
61
62
        return new static($schema);
63
    }
64
65
    /**
66
     * @param string $json
67
     *
68
     * @return static|callable
69
     */
70
    public static function fromEncodedString($json)
71
    {
72
        return static::fromDecodedObject(json_decode($json, false));
73
    }
74
75
    /**
76
     * @param array $json
77
     *
78
     * @return static|callable
79
     */
80
    public static function fromArray(array $json)
81
    {
82
        return static::fromEncodedString(json_encode($json, JSON_UNESCAPED_SLASHES));
83
    }
84
85
    /**
86
     * Returns the request's JSON validation errors.
87
     *
88
     * @param ServerRequestInterface $request
89
     *
90
     * @return array|null
91
     */
92
    public static function getErrors(ServerRequestInterface $request)
93
    {
94
        return self::getAttribute($request, self::KEY);
95
    }
96
97
    /**
98
     * Execute the middleware.
99
     *
100
     * @param ServerRequestInterface $request
101
     * @param ResponseInterface      $response
102
     * @param callable               $next
103
     *
104
     * @return ResponseInterface
105
     *
106
     * @throws \RuntimeException
107
     * @throws \InvalidArgumentException
108
     * @throws \JsonSchema\Exception\ExceptionInterface
109
     */
110
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
111
    {
112
        $value = $request->getParsedBody();
113
        if (!is_object($value)) {
114
            $request = self::setAttribute($request, self::KEY, [
115
                sprintf('Parsed body must be an object. Type %s is invalid.', gettype($value)),
116
            ]);
117
118
            return $this->executeCallable($this->errorHandler, $request, $response);
119
        }
120
121
        $validator = new Validator();
122
        $validator->check($value, $this->schema);
123
124
        if (!$validator->isValid()) {
125
            $request = self::setAttribute($request, self::KEY, $validator->getErrors());
126
127
            return $this->executeCallable($this->errorHandler, $request, $response);
128
        }
129
130
        return $next($request, $response);
131
    }
132
133
    /**
134
     * @param ServerRequestInterface $request
135
     * @param ResponseInterface      $response
136
     *
137
     * @return ResponseInterface
138
     *
139
     * @throws \RuntimeException
140
     * @throws \InvalidArgumentException
141
     */
142
    private function defaultErrorHandler(
143
        ServerRequestInterface $request,
144
        ResponseInterface $response
145
    ) {
146
        $response = $response->withStatus(422, 'Unprocessable Entity')
147
            ->withHeader('Content-Type', 'application/json');
148
149
        $middlewareAttribute = $request->getAttribute(Middleware::KEY, []);
150
151
        if (isset($middlewareAttribute[self::KEY])) {
152
            /** @var ResponseInterface $response */
153
            $stream = $response->getBody();
154
            $stream->write(json_encode($middlewareAttribute[self::KEY], JSON_UNESCAPED_SLASHES));
155
        }
156
157
        return $response;
158
    }
159
160
    /**
161
     * Has the following method signature:
162
     * function (ServerRequestInterface $request, ResponseInterface $response): ResponseInterface {}.
163
     *
164
     * Validation errors are stored in a middleware attribute:
165
     * $request->getAttribute(Middleware::KEY, [])[JsonValidator::KEY];
166
     *
167
     * @param callable $errorHandler
168
     */
169
    public function errorHandler(callable $errorHandler)
170
    {
171
        $this->errorHandler = $errorHandler;
172
    }
173
}
174