ValidateInterceptor::findOnMethods()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 11
nc 2
nop 2
1
<?php
2
/**
3
 * This file is part of the Ray.ValidateModule package
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
namespace Ray\Validation;
8
9
use Doctrine\Common\Annotations\Reader;
10
use Ray\Aop\MethodInterceptor;
11
use Ray\Aop\MethodInvocation;
12
use Ray\Aop\WeavedInterface;
13
use Ray\Validation\Annotation\OnFailure;
14
use Ray\Validation\Annotation\OnValidate;
15
use Ray\Validation\Annotation\Valid;
16
use Ray\Validation\Exception\InvalidArgumentException;
17
use Ray\Validation\Exception\ValidateMethodNotFound;
18
19
class ValidateInterceptor implements MethodInterceptor
20
{
21
    /**
22
     * @var Reader
23
     */
24
    private $reader;
25
26
    public function __construct(Reader $reader)
27
    {
28
        $this->reader = $reader;
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     *
34
     * @throws InvalidArgumentException
35
     */
36
    public function invoke(MethodInvocation $invocation)
37
    {
38
        list($onValidate, $onFailure) = $this->getOnValidate($invocation->getMethod());
39
        list($isValid, $failure) = $this->validate($invocation, $onValidate, $onFailure);
40
        if ($isValid === true) {
41
            // validation success
42
            return $invocation->proceed();
43
        }
44
45
        // onFailure
46
        return $failure;
47
    }
48
49
    /**
50
     * Return Validate and OnFailure method
51
     *
52
     * @param \ReflectionMethod $method
53
     *
54
     * @return \ReflectionMethod[] [$onValidateMethod, $onFailureMethod]
55
     */
56
    private function getOnValidate(\ReflectionMethod $method)
57
    {
58
        /** @var $valid Valid */
59
        $valid = $this->reader->getMethodAnnotation($method, Valid::class);
60
        $class = $method->getDeclaringClass();
61
        $onMethods = $this->findOnMethods($class, $valid);
62
        if ($onMethods[0] && $onMethods[0] instanceof \ReflectionMethod) {
63
            return $onMethods;
64
        }
65
        throw new ValidateMethodNotFound($method->getShortName());
66
    }
67
68
    /**
69
     * Validate with Validate method
70
     *
71
     * @param MethodInvocation  $invocation
72
     * @param \ReflectionMethod $onValidate
73
     * @param \ReflectionMethod $onFailure
74
     *
75
     * @return bool|mixed|InvalidArgumentException
76
     *
77
     * @throws \Exception
78
     */
79
    private function validate(MethodInvocation $invocation, \ReflectionMethod $onValidate, \ReflectionMethod $onFailure = null)
80
    {
81
        $validation = $onValidate->invokeArgs($invocation->getThis(), (array) $invocation->getArguments());
82
        if ($validation instanceof Validation && $validation->getMessages()) {
83
            /* @var $validation Validation */
84
            $validation->setInvocation($invocation);
85
            $failure = $this->getFailure($invocation, $validation, $onFailure);
86
            if ($failure instanceof \Exception) {
87
                throw $failure;
88
            }
89
90
            return [false, $failure];
91
        }
92
93
        return [true, null];
94
    }
95
96
    /**
97
     * Return result OnFailure
98
     *
99
     * @param MethodInvocation  $invocation
100
     * @param FailureInterface  $failure
101
     * @param \ReflectionMethod $onFailure
102
     *
103
     * @return mixed|InvalidArgumentException
104
     */
105
    private function getFailure(MethodInvocation $invocation, FailureInterface $failure, \ReflectionMethod $onFailure = null)
106
    {
107
        if ($onFailure) {
108
            return $onFailure->invoke($invocation->getThis(), $failure);
109
        }
110
111
        return $this->failureException($invocation, $failure);
112
    }
113
114
    /**
115
     * Return InvalidArgumentException exception
116
     *
117
     * @param MethodInvocation $invocation
118
     * @param FailureInterface $failure
119
     *
120
     * @return InvalidArgumentException
121
     */
122
    private function failureException(MethodInvocation $invocation, FailureInterface $failure)
123
    {
124
        $class = new \ReflectionClass($invocation->getThis());
125
        $className = $class->implementsInterface(WeavedInterface::class) ? $class->getParentClass()->name : $class->name;
126
        $errors = json_encode($failure->getMessages());
127
        $msg = sprintf("%s::%s() %s", $className, $invocation->getMethod()->name, $errors);
128
129
        return new InvalidArgumentException($msg, 400);
130
    }
131
132
    /**
133
     * @param \ReflectionClass $class
134
     * @param Valid            $valid
135
     *
136
     * @return \ReflectionMethod[]
137
     */
138
    private function findOnMethods(\ReflectionClass $class, Valid $valid)
139
    {
140
        $onValidateMethod = $onFailureMethod = null;
141
        foreach ($class->getMethods() as $method) {
142
            $annotations = $this->reader->getMethodAnnotations($method);
143
            list($onValidateMethod, $onFailureMethod) = $this->scanAnnotation(
144
                $valid,
145
                $annotations,
146
                $method,
147
                $onValidateMethod,
148
                $onFailureMethod
149
            );
150
        }
151
152
        return [$onValidateMethod, $onFailureMethod];
153
    }
154
155
    /**
156
     * @param Valid             $valid
157
     * @param array             $annotations
158
     * @param \ReflectionMethod $method
159
     * @param \ReflectionMethod $onValidateMethod
160
     * @param \ReflectionMethod $onFailureMethod
161
     *
162
     * @return array
163
     */
164
    private function scanAnnotation(
165
        Valid $valid,
166
        array $annotations,
167
        \ReflectionMethod $method,
168
        \ReflectionMethod $onValidateMethod = null,
169
        \ReflectionMethod $onFailureMethod = null
170
    ) {
171
        foreach ($annotations as $annotation) {
172
            if ($this->isOnValidateFound($annotation, $valid, $onValidateMethod)) {
173
                $onValidateMethod = $method;
174
            }
175
            if ($this->isOnFailureFound($annotation, $valid, $onFailureMethod)) {
176
                $onFailureMethod = $method;
177
            }
178
        }
179
180
        return [$onValidateMethod ,$onFailureMethod];
181
    }
182
183
    /**
184
     * @param object            $annotation
185
     * @param Valid             $valid
186
     * @param \ReflectionMethod $onValidateMethod
187
     *
188
     * @return bool
189
     */
190
    private function isOnValidateFound($annotation, Valid $valid, \ReflectionMethod $onValidateMethod = null)
191
    {
192
        return (is_null($onValidateMethod) && $annotation instanceof OnValidate && $annotation->value === $valid->value) ? true : false;
193
    }
194
195
    /**
196
     * @param object            $annotation
197
     * @param Valid             $valid
198
     * @param \ReflectionMethod $onFailureMethod
199
     *
200
     * @return bool
201
     */
202
    private function isOnFailureFound($annotation, Valid $valid, \ReflectionMethod $onFailureMethod = null)
203
    {
204
        return (is_null($onFailureMethod) && $annotation instanceof OnFailure && $annotation->value === $valid->value) ? true : false;
205
    }
206
}
207