1 | <?php |
||
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 | * @param string $schemaDir |
||
36 | * @param string $validateDir |
||
37 | * |
||
38 | * @Named("schemaDir=json_schema_dir,validateDir=json_validate_dir") |
||
39 | */ |
||
40 | 7 | public function __construct($schemaDir, $validateDir) |
|
45 | |||
46 | /** |
||
47 | * {@inheritdoc} |
||
48 | */ |
||
49 | 7 | public function invoke(MethodInvocation $invocation) |
|
50 | { |
||
51 | 7 | $jsonSchema = $invocation->getMethod()->getAnnotation(JsonSchema::class); |
|
|
|||
52 | 7 | if (! $jsonSchema instanceof JsonSchema) { |
|
53 | throw new JsonSchemaException($invocation->getMethod()->name); |
||
54 | } |
||
55 | 7 | if ($jsonSchema->params) { |
|
56 | 3 | $arguments = $this->getNamedArguments($invocation); |
|
57 | 3 | $this->validateRequest($jsonSchema, $arguments); |
|
58 | } |
||
59 | 6 | $ro = $invocation->proceed(); |
|
60 | 6 | if (! $ro instanceof ResourceObject) { |
|
61 | throw new JsonSchemaException($invocation->getMethod()->name); |
||
62 | } |
||
63 | 6 | if ($ro->code === 200 || $ro->code == 201) { |
|
64 | 5 | $this->validateResponse($jsonSchema, $ro); |
|
65 | } |
||
66 | |||
67 | 3 | return $ro; |
|
68 | } |
||
69 | |||
70 | 3 | private function validateRequest(JsonSchema $jsonSchema, array $arguments) |
|
76 | |||
77 | 5 | private function validateResponse(JsonSchema $jsonSchema, ResourceObject $ro) |
|
83 | |||
84 | 5 | private function validate($scanObject, $schemaFile) |
|
85 | { |
||
86 | 5 | $validator = new Validator; |
|
87 | 5 | $schema = (object) ['$ref' => 'file://' . $schemaFile]; |
|
88 | 5 | $validator->validate($scanObject, $schema, Constraint::CHECK_MODE_TYPE_CAST); |
|
89 | 5 | $isValid = $validator->isValid(); |
|
90 | 5 | if ($isValid) { |
|
91 | 3 | return; |
|
92 | } |
||
93 | 3 | $e = null; |
|
94 | 3 | foreach ($validator->getErrors() as $error) { |
|
95 | 3 | $msg = sprintf('[%s] %s', $error['property'], $error['message']); |
|
96 | 3 | $e = $e ? new JsonSchemaErrorException($msg, 0, $e) : new JsonSchemaErrorException($msg); |
|
97 | } |
||
98 | 3 | throw new JsonSchemaException($schemaFile, Code::ERROR, $e); |
|
99 | } |
||
100 | |||
101 | 5 | private function getSchemaFile(JsonSchema $jsonSchema, ResourceObject $ro) : string |
|
117 | |||
118 | 5 | private function validateFileExists(string $schemaFile) |
|
124 | |||
125 | 3 | private function getNamedArguments(MethodInvocation $invocation) |
|
136 | } |
||
137 |
Let’s take a look at an example:
In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.
Available Fixes
Change the type-hint for the parameter:
Add an additional type-check:
Add the method to the parent class: