Scanner   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 15
eloc 46
dl 0
loc 136
c 0
b 0
f 0
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 3
A analyzeMethods() 0 22 4
A assertPBMessage() 0 6 3
A analyzing() 0 15 3
A sources() 0 4 1
A serving() 0 4 1
1
<?php
2
/**
3
 * Service scanner
4
 * User: moyo
5
 * Date: 20/09/2017
6
 * Time: 4:12 PM
7
 */
8
9
namespace Carno\RPC\Service;
10
11
use Carno\RPC\Exception\IllegalServiceMethodDependsException;
12
use Carno\RPC\Exception\IllegalServiceMethodParamsException;
13
use Carno\RPC\Service\SDetectors\API;
14
use Carno\RPC\Service\SDetectors\AST;
15
use Carno\RPC\Service\SDetectors\REF;
16
use ReflectionClass;
17
use ReflectionMethod;
18
19
class Scanner
20
{
21
    /**
22
     * name of "contracts" for interfaces
23
     */
24
    public const CONTRACTS_NAME = 'contracts';
25
26
    /**
27
     * name of "clients" for rpc client
28
     */
29
    public const CLIENTS_NAME = 'clients';
30
31
    /**
32
     * base class of pb message/struct
33
     */
34
    private const PB_STRUCT_BASE = 'Google\\Protobuf\\Internal\\Message';
35
36
    /**
37
     * @var string[]
38
     */
39
    private $services = [];
40
41
    /**
42
     * @var Router
43
     */
44
    private $router = null;
45
46
    /**
47
     * @var API
48
     */
49
    private $detector = null;
50
51
    /**
52
     * @var string[]
53
     */
54
    private $detectors = [AST::class, REF::class];
55
56
    /**
57
     * Scanner constructor.
58
     * @param Router $router
59
     */
60
    public function __construct(Router $router)
61
    {
62
        $this->router = $router;
63
64
        /**
65
         * @var API $api
66
         */
67
        foreach ($this->detectors as $detector) {
68
            $api = new $detector;
69
            if ($api->supported()) {
70
                $this->detector = $api;
71
                break;
72
            }
73
        }
74
    }
75
76
    /**
77
     * @param string ...$services
78
     * @return static
79
     */
80
    public function sources(string ...$services) : self
81
    {
82
        $this->services = array_unique(array_merge($this->services, $services));
83
        return $this;
84
    }
85
86
    /**
87
     */
88
    public function serving() : void
89
    {
90
        array_walk($this->services, function (string $implementer) {
91
            $this->detector->analyzing($this->router, self::CONTRACTS_NAME, $implementer);
92
        });
93
    }
94
95
    /**
96
     */
97
    public function analyzing() : void
98
    {
99
        array_walk($this->services, function (string $implementer) {
100
            $ref = new ReflectionClass($implementer);
101
102
            foreach ($ref->getInterfaces() as $inf) {
103
                $nss = array_map(static function ($p) {
104
                    return lcfirst($p);
105
                }, explode('\\', $inf->getNamespaceName()));
106
107
                if (array_pop($nss) === self::CONTRACTS_NAME) {
108
                    $this->router->add(
109
                        (new Specification(implode('.', $nss), $inf->getShortName()))
110
                            ->setMethods($this->analyzeMethods(...$inf->getMethods(ReflectionMethod::IS_PUBLIC))),
111
                        $implementer
112
                    );
113
                }
114
            }
115
        });
116
    }
117
118
    /**
119
     * @param ReflectionMethod ...$methods
120
     * @return array
121
     */
122
    private function analyzeMethods(ReflectionMethod ...$methods) : array
123
    {
124
        $analyzed = [];
125
126
        foreach ($methods as $method) {
127
            $name = $method->getName();
128
            $params = $method->getParameters();
129
130
            foreach ($params as $param) {
131
                $class = $param->getClass();
132
                if (is_null($class)) {
133
                    throw new IllegalServiceMethodParamsException(
134
                        "#{$param->getPosition()} Expect CLASS but found [{$param->getType()->getName()}]"
135
                    );
136
                } else {
137
                    $this->assertPBMessage($class->getName());
138
                    $analyzed[$name] = ['in' => $class->getName()];
139
                }
140
            }
141
        }
142
143
        return $analyzed;
144
    }
145
146
    /**
147
     * @param string $class
148
     */
149
    private function assertPBMessage(string $class) : void
150
    {
151
        $parent = (new ReflectionClass($class))->getParentClass();
152
        if (empty($parent) || $parent->getName() !== self::PB_STRUCT_BASE) {
153
            throw new IllegalServiceMethodDependsException(
154
                "Provided class is not inherit from pb message"
155
            );
156
        }
157
    }
158
}
159