Passed
Pull Request — master (#370)
by Valentin
04:34
created

PipelineInterceptor::cleanOriginalPipeline()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 6
nop 1
dl 0
loc 18
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Domain;
6
7
use Doctrine\Common\Annotations\AnnotationReader;
8
use Psr\Container\ContainerInterface;
9
use Spiral\Core\CoreInterceptorInterface;
10
use Spiral\Core\CoreInterface;
11
use Spiral\Core\InterceptableCore;
12
use Spiral\Core\InterceptorPipeline;
13
use Spiral\Domain\Annotation\Pipeline;
14
15
class PipelineInterceptor implements CoreInterceptorInterface
16
{
17
    /** @var array  */
18
    private $cache = [];
19
20
    /** @var AnnotationReader  */
21
    private $reader;
22
23
    /** @var ContainerInterface  */
24
    private $container;
25
26
    /**
27
     * @param AnnotationReader   $reader
28
     * @param ContainerInterface $container
29
     */
30
    public function __construct(AnnotationReader $reader, ContainerInterface $container)
31
    {
32
        $this->reader = $reader;
33
        $this->container = $container;
34
    }
35
36
    /**
37
     * @param string        $controller
38
     * @param string        $action
39
     * @param array         $parameters
40
     * @param CoreInterface $core
41
     * @return mixed
42
     * @throws \Throwable
43
     */
44
    public function process(string $controller, string $action, array $parameters, CoreInterface $core)
45
    {
46
        $annotation = $this->readAnnotation($controller, $action);
47
        if ($core instanceof InterceptorPipeline && $annotation->skipNext) {
48
            $this->cleanOriginalPipeline($core);
49
        }
50
51
        $pipeline = $this->getCachedPipeline($controller, $action, $annotation);
52
        if (!empty($pipeline)) {
53
            $core = $core instanceof InterceptorPipeline ? $core : new InterceptableCore($core);
54
            foreach ($pipeline as $interceptor) {
55
                $core->addInterceptor($interceptor);
56
            }
57
        }
58
59
        return $core->callAction($controller, $action, $parameters);
60
    }
61
62
    /**
63
     * @param string $controller
64
     * @param string $action
65
     * @return Pipeline
66
     */
67
    private function readAnnotation(string $controller, string $action): Pipeline
68
    {
69
        try {
70
            $method = new \ReflectionMethod($controller, $action);
71
        } catch (\ReflectionException $e) {
72
            return new Pipeline();
73
        }
74
75
        /** @var Pipeline $annotation */
76
        $annotation = $this->reader->getMethodAnnotation($method, Pipeline::class);
77
        return $annotation ?? new Pipeline();
78
    }
79
80
    /**
81
     * @param InterceptorPipeline $pipeline
82
     */
83
    private function cleanOriginalPipeline(InterceptorPipeline $pipeline): void
84
    {
85
        $pipelineReflection = new \ReflectionProperty(InterceptorPipeline::class, 'interceptors');
86
        $pipelineReflection->setAccessible(true);
87
88
        $oldInterceptors = $pipelineReflection->getValue($pipeline);
89
        $newInterceptors = [];
90
        foreach ($oldInterceptors as $interceptor) {
91
            $newInterceptors[] = $interceptor;
92
            if ($interceptor instanceof self) {
93
                break;
94
            }
95
        }
96
97
        if (count($newInterceptors) !== count($oldInterceptors)) {
98
            $pipelineReflection->setValue($pipeline, $newInterceptors);
99
        }
100
        $pipelineReflection->setAccessible(false);
101
    }
102
103
    /**
104
     * @param string   $controller
105
     * @param string   $action
106
     * @param Pipeline $annotation
107
     * @return array
108
     */
109
    private function getCachedPipeline(string $controller, string $action, Pipeline $annotation): array
110
    {
111
        $key = "{$controller}:{$action}";
112
        if (!array_key_exists($key, $this->cache)) {
113
            $this->cache[$key] = $this->extractAnnotationPipeline($annotation);
114
        }
115
116
        return $this->cache[$key];
117
    }
118
119
    /**
120
     * @param Pipeline $annotation
121
     * @return array
122
     */
123
    private function extractAnnotationPipeline(Pipeline $annotation): array
124
    {
125
        $interceptors = [];
126
        foreach ($annotation->pipeline as $interceptor) {
127
            try {
128
                $interceptors[] = $this->container->get($interceptor);
129
            } catch (\Throwable $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
130
            }
131
        }
132
133
        return $interceptors;
134
    }
135
}
136