Completed
Push — spike ( 7b340c...46b582 )
by Akihito
06:25 queued 04:57
created

JsonSchemaInterceptor::getNamedArguments()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.9
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource\Interceptor;
6
7
use BEAR\Resource\Annotation\JsonSchema;
8
use BEAR\Resource\Code;
9
use BEAR\Resource\Exception\JsonSchemaErrorException;
10
use BEAR\Resource\Exception\JsonSchemaException;
11
use BEAR\Resource\Exception\JsonSchemaNotFoundException;
12
use BEAR\Resource\ResourceObject;
13
use function is_string;
14
use JsonSchema\Constraints\Constraint;
15
use JsonSchema\Validator;
16
use Ray\Aop\MethodInterceptor;
17
use Ray\Aop\MethodInvocation;
18
use Ray\Aop\ReflectionMethod;
19
use Ray\Aop\WeavedInterface;
20
use Ray\Di\Di\Named;
21
22
final class JsonSchemaInterceptor implements MethodInterceptor
23
{
24
    /**
25
     * @var string
26
     */
27
    private $schemaDir;
28
29
    /**
30
     * @var string
31
     */
32
    private $validateDir;
33
34
    /**
35
     * @var null|string
36
     */
37
    private $schemaHost;
38
39
    /**
40
     * @Named("schemaDir=json_schema_dir,validateDir=json_validate_dir,schemaHost=json_schema_host")
41
     */
42 12
    public function __construct(string $schemaDir, string $validateDir, string $schemaHost = null)
43
    {
44 12
        $this->schemaDir = $schemaDir;
45 12
        $this->validateDir = $validateDir;
46 12
        $this->schemaHost = $schemaHost;
47 12
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52 12
    public function invoke(MethodInvocation $invocation)
53
    {
54
        /** @var ReflectionMethod $method */
55 12
        $method = $invocation->getMethod();
56
        /** @var JsonSchema $jsonSchema */
57 12
        $jsonSchema = $method->getAnnotation(JsonSchema::class);
58 12
        if ($jsonSchema->params) {
59 6
            $arguments = $this->getNamedArguments($invocation);
60 6
            $this->validateRequest($jsonSchema, $arguments);
61
        }
62
        /** @var ResourceObject $ro */
63 10
        $ro = $invocation->proceed();
64 10
        if ($ro->code === 200 || $ro->code == 201) {
65 8
            $this->validateResponse($jsonSchema, $ro);
66
        }
67 6
        if (is_string($this->schemaHost)) {
68
            $ro->headers['Link'] = sprintf('<%s%s>; rel="describedby"', $this->schemaHost, $jsonSchema->schema);
69
        }
70
71 6
        return $ro;
72
    }
73
74 6
    private function validateRequest(JsonSchema $jsonSchema, array $arguments)
75
    {
76 6
        $schemaFile = $this->validateDir . '/' . $jsonSchema->params;
77 6
        $this->validateFileExists($schemaFile);
78 6
        $this->validate($arguments, $schemaFile);
79 4
    }
80
81 8
    private function validateResponse(JsonSchema $jsonSchema, ResourceObject $ro)
82
    {
83 8
        $schemaFile = $this->getSchemaFile($jsonSchema, $ro);
84 6
        $body = isset($ro->body[$jsonSchema->key]) ? $ro->body[$jsonSchema->key] : $ro->body;
85 6
        $this->validate($body, $schemaFile);
86 4
    }
87
88 8
    private function validate($scanObject, $schemaFile)
89
    {
90 8
        $validator = new Validator;
91 8
        $schema = (object) ['$ref' => 'file://' . $schemaFile];
92 8
        $validator->validate($scanObject, $schema, Constraint::CHECK_MODE_TYPE_CAST);
93 8
        $isValid = $validator->isValid();
94 8
        if ($isValid) {
95 6
            return;
96
        }
97 4
        $e = null;
98 4
        foreach ($validator->getErrors() as $error) {
99 4
            $msg = sprintf('[%s] %s', $error['property'], $error['message']);
100 4
            $e = $e ? new JsonSchemaErrorException($msg, 0, $e) : new JsonSchemaErrorException($msg);
101
        }
102
103 4
        throw new JsonSchemaException($schemaFile, Code::ERROR, $e);
104
    }
105
106 8
    private function getSchemaFile(JsonSchema $jsonSchema, ResourceObject $ro) : string
107
    {
108 8
        if (! $jsonSchema->schema) {
109
            // for BC only
110
            $ref = new \ReflectionClass($ro);
111
            if (! $ref instanceof ReflectionClass) {
0 ignored issues
show
Bug introduced by
The class BEAR\Resource\Interceptor\ReflectionClass does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
112
                throw new \ReflectionException(get_class($ro));
113
            }
114
            $roFileName = $ro instanceof WeavedInterface ? $roFileName = $ref->getParentClass()->getFileName() : $ref->getFileName();
115
            $bcFile = str_replace('.php', '.json', $roFileName);
116
            if (file_exists($bcFile)) {
117
                return $bcFile;
118
            }
119
        }
120 8
        $schemaFile = $this->schemaDir . '/' . $jsonSchema->schema;
121 8
        $this->validateFileExists($schemaFile);
122
123 6
        return $schemaFile;
124
    }
125
126 10
    private function validateFileExists(string $schemaFile)
127
    {
128 10
        if (! file_exists($schemaFile) || is_dir($schemaFile)) {
129 2
            throw new JsonSchemaNotFoundException($schemaFile);
130
        }
131 8
    }
132
133 6
    private function getNamedArguments(MethodInvocation $invocation)
134
    {
135 6
        $parameters = $invocation->getMethod()->getParameters();
136 6
        $values = $invocation->getArguments();
137 6
        $arguments = [];
138 6
        foreach ($parameters as $index => $parameter) {
139 6
            $arguments[$parameter->name] = $values[$index] ?? $parameter->getDefaultValue();
140
        }
141
142 6
        return $arguments;
143
    }
144
}
145