Completed
Push — master ( 89341c...462ecc )
by Lawrence
01:28
created

Server::info()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 5
nop 0
dl 0
loc 23
ccs 0
cts 19
cp 0
crap 30
rs 8.5906
c 0
b 0
f 0
1
<?php
2
/*
3
 +------------------------------------------------------------------------+
4
 | Plinker-RPC PHP                                                        |
5
 +------------------------------------------------------------------------+
6
 | Copyright (c)2017-2018 (https://github.com/plinker-rpc/core)           |
7
 +------------------------------------------------------------------------+
8
 | This source file is subject to MIT License                             |
9
 | that is bundled with this package in the file LICENSE.                 |
10
 |                                                                        |
11
 | If you did not receive a copy of the license and are unable to         |
12
 | obtain it through the world-wide-web, please send an email             |
13
 | to [email protected] so we can send you a copy immediately.        |
14
 +------------------------------------------------------------------------+
15
 | Authors: Lawrence Cherone <[email protected]>                     |
16
 +------------------------------------------------------------------------+
17
 */
18
19
namespace Plinker\Core;
20
21
/**
22
 * Plinker\Core\Server
23
 */
24
final class Server
25
{
26
    /**
27
     * @var
28
     */
29
    protected $post;
30
31
    /**
32
     * @var
33
     */
34
    protected $config;
35
36
    /**
37
     * @var
38
     */
39
    protected $signer;
40
    
41
    /**
42
     * @var
43
     */
44
    protected $response;
45
    
46
47
    /**
48
     * @const - error strings
49
     */
50
    const ERROR_IP            = "IP not in allowed list (%s)";
51
    const ERROR_TOKEN         = "Plinker token mismatch";
52
    const ERROR_DECODE        = "Failed to decode payload, check secret";
53
    const ERROR_USR_COMPONENT = "User component class (%s) not found";
54
    const ERROR_EXT_COMPONENT = "Component (%s) not implemented";
55
    const ERROR_ACTION        = "Component action (%s) not implemented in: %s";
56
57
    /**
58
     * Class construct
59
     *
60
     * @param  array $config - config array which holds object configuration
61
     * @return void
62
     */
63
    public function __construct($config = [])
64
    {
65
        $this->config = array_merge([
66
            "debug"       => false,
67
            "secret"      => null,
68
            "allowed_ips" => []
69
        ], $config);
70
71
        // check and set client timeout
72
        if (!isset($this->config["timeout"]) || !is_numeric($this->config["timeout"])) {
73
            $this->config["timeout"] = 10;
74
        }
75
    }
76
77
    /**
78
     * Listen method
79
     *
80
     * <code>
81
     *  $server->listen();
82
     * </code>
83
     *
84
     * @return void
85
     */
86
    public function listen()
87
    {
88
        $this->post = file_get_contents("php://input");
89
        $this->post = gzinflate($this->post);
90
        $this->post = json_decode($this->post, true);
91
92
        // check allowed ips
93
        if (!empty($this->config["allowed_ips"]) &&
94
           !in_array($_SERVER["REMOTE_ADDR"], $this->config["allowed_ips"])) {
95
            $this->response = serialize([
96
                "error" => sprintf(Server::ERROR_IP, $_SERVER["REMOTE_ADDR"]),
97
                "code" => 403
98
            ]);
99
            return;
100
        }
101
102
        // check header token matches data token
103
        if ($_SERVER["HTTP_PLINKER"] != $this->post["token"]) {
104
            $this->response = serialize([
105
                "error" => Server::ERROR_TOKEN,
106
                "code" => 422
107
            ]);
108
            return;
109
        }
110
111
        // load signer
112
        if (!$this->signer) {
113
            $this->signer = new Lib\Signer($this->config);
114
        }
115
116
        // decode post payload
117
        $this->post = $this->signer->decode($this->post);
118
119
        // could not decode payload
120
        if ($this->post === null) {
121
            $this->response = serialize([
122
                "error" => Server::ERROR_DECODE,
123
                "code" => 422
124
            ]);
125
            return;
126
        }
127
128
        //
129
        $response = null;
130
        $ns = null;
131
        $action = $this->post["action"];
132
        $this->config = array_merge(
133
            $this->config,
134
            $this->post
135
        );
136
137
        // component is in classes config
138
        if (array_key_exists($this->post["component"], $this->config["classes"])) {
139
            //
140
            if (!empty($this->config["classes"][$this->post["component"]][0])) {
141
                $ns = $this->config["classes"][$this->post["component"]][0];
142
            }
143
144
            //
145
            if (!empty($this->config["classes"][$this->post["component"]][1])) {
146
                $this->config = array_merge(
147
                    $this->config,
148
                    $this->config["classes"][$this->post["component"]][1]
149
                );
150
            }
151
152
            //
153
            if (!empty($ns) && !file_exists($ns)) {
154
                $this->response = serialize([
155
                    "error" => sprintf(Server::ERROR_USR_COMPONENT, $this->post["component"]),
156
                    "code"  => 422
157
                ]);
158
                return;
159
            }
160
161
            //
162
            require($ns);
163
164
            //
165
            if (!class_exists($this->post["component"])) {
166
                $this->response = serialize([
167
                    "error" => sprintf(Server::ERROR_USR_COMPONENT, $this->post["component"]),
168
                    "code"  => 422
169
                ]);
170
                return;
171
            }
172
173
            //
174
            $response = $this->execute($this->post["component"], $action);
175
176
            $this->response = serialize($response);
177
            return;
178
        }
179
180
        // component is plinker endpoint
181
        $ns = "\\Plinker\\Core\\Endpoint\\".ucfirst($this->post["component"]);
182
183
        if (class_exists($ns)) {
184
            //
185
            $response = $this->execute($ns, $action);
186
        } else {
187
            if (empty($this->post["component"]) && $action === "info") {
188
                $response = $this->info();
189
            } else {
190
                $response = sprintf(Server::ERROR_EXT_COMPONENT, $this->post["component"]);
191
            }
192
        }
193
194
        $this->response = serialize($response);
195
    }
196
197
    /**
198
     * Return info about available classes
199
     *
200
     * <code>
201
     *  $client->info();
202
     * </code>
203
     *
204
     * @return array
205
     */
206
    private function info()
207
    {
208
        $response = [
209
            "class" => []
210
        ];
211
        foreach ($this->config["classes"] as $key => $val) {
212
            //
213
            require($val[0]);
214
215
            $reflection = new \ReflectionClass($key);
216
217
            foreach ($reflection->getMethods() as $method) {
218
                if (!in_array($method->getName(), ["__construct"])) {
219
                    $param = [];
220
                    foreach ($method->getParameters() as $parameter) {
221
                        $param[] = $parameter->getName();
222
                    }
223
                    $response["class"][$key]["methods"][$method->getName()] = $param;
224
                }
225
            }
226
        }
227
228
        return $response;
229
    }
230
231
    /**
232
     * Execute component
0 ignored issues
show
Bug introduced by
The type Plinker\Core\component was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
233
     *
234
     * @param  $ns      component class namespace
235
     * @param  $action  component action
236
     * @return string
237
     */
238
    private function execute($ns, $action)
239
    {
240
        $response  = null;
241
        $component = new $ns($this->config);
242
243
        if (method_exists($component, $action)) {
244
            $response = call_user_func_array(
245
                [
246
                    $component,
247
                    $action
248
                ],
249
                $this->post["params"]
250
            );
251
        } else {
252
            $response = sprintf(Server::ERROR_ACTION, $action, $ns);
253
        }
254
255
        return $response;
256
    }
257
    
258
    /**
259
     * 
260
     */
261
    private function __destruct()
262
    {
263
        header("Content-Type: text/plain; charset=utf-8");
264
        echo $this->response;
265
    }
266
}
267