Completed
Push — master ( fb95ee...b3e448 )
by Oscar
9s
created

JsonSchema   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 5
dl 0
loc 117
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B __invoke() 0 34 5
A invalidateResponse() 0 15 3
A getSchema() 0 17 3
A normalizeFilePath() 0 9 2
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
9
class JsonSchema
10
{
11
    /** @var string[] */
12
    private $schemas;
13
14
    /**
15
     * JsonSchema constructor.
16
     * @param string[] $schemas [uri => file] An associative array of HTTP URI to validation schema.
17
     */
18
    public function __construct(array $schemas)
19
    {
20
        $this->schemas = $schemas;
21
    }
22
23
    /**
24
     * Execute the middleware.
25
     *
26
     * @param ServerRequestInterface $request
27
     * @param ResponseInterface      $response
28
     * @param callable               $next
29
     *
30
     * @return ResponseInterface
31
     */
32
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
33
    {
34
        $schema = $this->getSchema($request);
35
36
        if (is_object($schema)) {
37
            $value = $request->getParsedBody();
38
            if (!is_object($value)) {
39
                return $this->invalidateResponse(
40
                    $response,
41
                    sprintf('Parsed body must be an object. Type %s is invalid.', gettype($value))
42
                );
43
            }
44
45
            $validator = new Validator();
46
            $validator->check($value, $schema);
47
48
            if (!$validator->isValid()) {
49
                return $this->invalidateResponse(
50
                    $response,
51
                    'Unprocessable Entity',
52
                    [
53
                        'Content-Type' => 'application/json',
54
                    ],
55
                    json_encode($validator->getErrors(), JSON_UNESCAPED_SLASHES)
56
                );
57
            }
58
        }
59
60
        if ($next) {
61
            return $next($request, $response);
62
        }
63
64
        return $response;
65
    }
66
67
    /**
68
     * @param ResponseInterface $response
69
     * @param string $reason
70
     * @param string[] $headers
71
     * @param string|null $body
72
     * @return ResponseInterface
73
     */
74
    private function invalidateResponse(ResponseInterface $response, $reason, array $headers = [], $body = null)
75
    {
76
        $response = $response->withStatus(422, $reason);
77
78
        foreach ($headers as $name => $value) {
79
            $response = $response->withHeader($name, $value);
80
        }
81
82
        if ($body !== null) {
83
            $stream = $response->getBody();
84
            $stream->write($body);
85
        }
86
87
        return $response;
88
    }
89
90
    /**
91
     * @param ServerRequestInterface $request
92
     * @return object|null
93
     */
94
    private function getSchema(ServerRequestInterface $request)
95
    {
96
        foreach ($this->schemas as $pattern => $file) {
97
            $uri = $request->getUri();
98
            $path = $uri->getPath();
99
100
            if (stripos($path, $pattern) === 0) {
101
                $file = $this->normalizeFilePath($file);
102
103
                return (object) [
104
                    '$ref' => $file,
105
                ];
106
            }
107
        }
108
109
        return null;
110
    }
111
112
    /**
113
     * @param string
114
     * @return string
115
     */
116
    private function normalizeFilePath($path)
117
    {
118
        if (parse_url($path, PHP_URL_SCHEME)) {
119
            // The schema file already has a scheme, e.g. `file://` or `vfs://`.
120
            return $path;
121
        }
122
123
        return 'file://' . $path;
124
    }
125
}
126