Passed
Push — master ( 382ef3...ddad46 )
by Thierry
03:13 queued 01:02
created

CallableObject   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 36
eloc 79
c 1
b 0
f 0
dl 0
loc 319
rs 9.52

14 Methods

Rating   Name   Duplication   Size   Complexity  
A hasMethod() 0 3 1
A getCallableMethods() 0 17 4
A getOptions() 0 3 1
A setOptions() 0 3 1
A callMethod() 0 5 1
A excluded() 0 3 1
B configure() 0 35 10
A getJsName() 0 3 1
A __construct() 0 4 1
A setHookMethods() 0 11 4
A getRegisteredObject() 0 3 1
A getPublicMethods() 0 11 3
A call() 0 9 1
A callHookMethods() 0 21 6
1
<?php
2
3
/**
4
 * CallableObject.php - Jaxon callable object
5
 *
6
 * This class stores a reference to an object whose methods can be called from
7
 * the client via an Jaxon request
8
 *
9
 * The Jaxon plugin manager will call <CallableObject->getClientScript> so that
10
 * stub functions can be generated and sent to the browser.
11
 *
12
 * @package jaxon-core
0 ignored issues
show
Coding Style introduced by
Package name "jaxon-core" is not valid; consider "Jaxoncore" instead
Loading history...
13
 * @author Jared White
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
14
 * @author J. Max Wilson
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
15
 * @author Joseph Woolley
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
16
 * @author Steffen Konerow
0 ignored issues
show
Coding Style introduced by
Content of the @author tag must be in the form "Display Name <[email protected]>"
Loading history...
17
 * @author Thierry Feuzeu <[email protected]>
18
 * @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
19
 * @copyright Copyright (c) 2008-2010 by Joseph Woolley, Steffen Konerow, Jared White  & J. Max Wilson
0 ignored issues
show
Coding Style introduced by
@copyright tag must contain a year and the name of the copyright holder
Loading history...
20
 * @copyright 2016 Thierry Feuzeu <[email protected]>
21
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
22
 * @link https://github.com/jaxon-php/jaxon-core
23
 */
0 ignored issues
show
Coding Style introduced by
PHP version not specified
Loading history...
Coding Style introduced by
Missing @category tag in file comment
Loading history...
24
25
namespace Jaxon\Plugin\Request\CallableClass;
26
27
use Jaxon\Di\Container;
28
use Jaxon\Response\ResponseInterface;
29
30
use ReflectionClass;
31
use ReflectionException;
32
use ReflectionMethod;
33
34
use function array_filter;
35
use function array_map;
36
use function array_merge;
37
use function in_array;
38
use function is_array;
39
use function is_string;
40
use function json_encode;
41
use function str_replace;
42
use function substr;
43
44
class CallableObject
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class CallableObject
Loading history...
45
{
46
    /**
47
     * The DI container
48
     *
49
     * @var Container
50
     */
51
    protected $di;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line(s) before first member var; 0 found
Loading history...
52
53
    /**
54
     * The reflection class of the user registered callable object
55
     *
56
     * @var ReflectionClass
57
     */
58
    private $xReflectionClass;
59
60
    /**
61
     * A list of methods of the user registered callable object the library must not export to javascript
62
     *
63
     * @var array
64
     */
65
    private $aProtectedMethods = [];
66
67
    /**
68
     * A list of methods to call before processing the request
69
     *
70
     * @var array
71
     */
72
    private $aBeforeMethods = [];
73
74
    /**
75
     * A list of methods to call after processing the request
76
     *
77
     * @var array
78
     */
79
    private $aAfterMethods = [];
80
81
    /**
82
     * The callable object options
83
     *
84
     * @var array
85
     */
86
    private $aOptions = [];
87
88
    /**
89
     * The character to use as separator in javascript class names
90
     *
91
     * @var string
92
     */
93
    private $sSeparator = '.';
94
95
    /**
96
     * Check if the js code for this object must be generated
97
     *
98
     * @var bool
0 ignored issues
show
Bug introduced by
Expected "boolean" but found "bool" for @var tag in member variable comment
Loading history...
99
     */
100
    private $bExcluded = false;
101
102
    /**
103
     * The class constructor
104
     *
105
     * @param Container  $di
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 7 spaces after parameter type; 2 found
Loading history...
106
     * @param ReflectionClass $xReflectionClass    The reflection class
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
107
     *
108
     */
109
    public function __construct(Container $di, ReflectionClass $xReflectionClass)
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines before function; 1 found
Loading history...
110
    {
111
        $this->di = $di;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 15 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
112
        $this->xReflectionClass = $xReflectionClass;
113
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
114
115
    /**
116
     * Get callable object options
117
     *
118
     * @return array
119
     */
120
    public function getOptions(): array
121
    {
122
        return $this->aOptions;
123
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
124
125
    /**
126
     * Check if the js code for this object must be generated
127
     *
128
     * @return bool
129
     */
130
    public function excluded(): bool
131
    {
132
        return $this->bExcluded;
133
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
134
135
    /**
136
     * Set callable object options
137
     *
138
     * @param array  $aOptions
0 ignored issues
show
Coding Style introduced by
Missing parameter comment
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
139
     *
140
     * @return void
141
     */
142
    public function setOptions(array $aOptions)
143
    {
144
        $this->aOptions = $aOptions;
145
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
146
147
    /**
148
     * Get the name of the corresponding javascript class
149
     *
150
     * @return string
151
     */
152
    public function getJsName(): string
153
    {
154
        return str_replace('\\', $this->sSeparator, $this->xReflectionClass->getName());
155
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
156
157
    /**
158
     * Set hook methods
159
     *
160
     * @param array $aHookMethods    The array of hook methods
0 ignored issues
show
Coding Style introduced by
Expected 8 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
161
     * @param string|array $xValue    The value of the configuration option
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 4 found
Loading history...
162
     *
163
     * @return void
164
     */
165
    private function setHookMethods(array &$aHookMethods, $xValue)
166
    {
167
        foreach($xValue as $sCalledMethod => $xMethodToCall)
168
        {
169
            if(is_array($xMethodToCall))
170
            {
171
                $aHookMethods[$sCalledMethod] = $xMethodToCall;
172
            }
173
            elseif(is_string($xMethodToCall))
174
            {
175
                $aHookMethods[$sCalledMethod] = [$xMethodToCall];
176
            }
177
        }
178
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
179
180
    /**
181
     * Set configuration options / call options for each method
182
     *
183
     * @param string $sName    The name of the configuration option
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter name; 4 found
Loading history...
184
     * @param string|array $xValue    The value of the configuration option
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
185
     *
186
     * @return void
187
     */
188
    public function configure(string $sName, $xValue)
189
    {
190
        switch($sName)
191
        {
192
        // Set the separator
193
        case 'separator':
194
            if($xValue === '_' || $xValue === '.')
195
            {
196
                $this->sSeparator = $xValue;
197
            }
198
            break;
199
        // Set the protected methods
200
        case 'protected':
201
            if(is_array($xValue))
202
            {
203
                $this->aProtectedMethods = array_merge($this->aProtectedMethods, $xValue);
204
            }
205
            elseif(is_string($xValue))
0 ignored issues
show
introduced by
The condition is_string($xValue) is always true.
Loading history...
206
            {
207
                $this->aProtectedMethods[] = $xValue;
208
            }
209
            break;
210
        // Set the methods to call before processing the request
211
        case '__before':
212
            $this->setHookMethods($this->aBeforeMethods, $xValue);
213
            break;
214
        // Set the methods to call after processing the request
215
        case '__after':
216
            $this->setHookMethods($this->aAfterMethods, $xValue);
217
            break;
218
        case 'excluded':
219
            $this->bExcluded = (bool)$xValue;
220
            break;
221
        default:
222
            break;
223
        }
224
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
225
226
    /**
227
     * Return a list of methods of the callable object
228
     *
229
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
230
     *
231
     * @return array
232
     */
233
    public function getPublicMethods(array $aProtectedMethods = []): array
234
    {
235
        $aMethods = array_map(function($xMethod) {
236
            return $xMethod->getShortName();
237
        }, $this->xReflectionClass->getMethods(ReflectionMethod::IS_PUBLIC));
238
        return array_filter($aMethods, function($sMethodName) use($aProtectedMethods) {
239
            // Don't take magic __call, __construct, __destruct methods
240
            // Don't take protected methods
241
            return !(substr($sMethodName, 0, 2) === '__' ||
242
                in_array($sMethodName, $aProtectedMethods) ||
243
                in_array($sMethodName, $this->aProtectedMethods));
244
        });
245
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
246
247
    /**
248
     * Return a list of methods of the callable object to export to javascript
249
     *
250
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
251
     *
252
     * @return array
253
     */
254
    public function getCallableMethods(array $aProtectedMethods): array
255
    {
256
        // Convert an option to a string to be displayed in the js script template.
257
        $fConvertOption = function($xOption) {
258
            return is_array($xOption) ? json_encode($xOption) : $xOption;
259
        };
260
        $aCommonConfig = isset($this->aOptions['*']) ? array_map($fConvertOption, $this->aOptions['*']) : [];
261
262
        return array_map(function($sMethodName) use($fConvertOption, $aCommonConfig) {
263
            // Specific options for this method
264
            $aMethodConfig = isset($this->aOptions[$sMethodName]) ?
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "?"; newline found
Loading history...
265
                array_map($fConvertOption, $this->aOptions[$sMethodName]) : [];
266
            return [
267
                'name' => $sMethodName,
268
                'config' => array_merge($aCommonConfig, $aMethodConfig),
269
            ];
270
        }, $this->getPublicMethods($aProtectedMethods));
271
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
272
273
    /**
274
     * Get the registered callable object
275
     *
276
     * @return null|object
277
     */
278
    public function getRegisteredObject()
279
    {
280
        return $this->di->g($this->xReflectionClass->getName());
281
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
282
283
    /**
284
     * Check if the specified method name is one of the methods of the registered callable object
285
     *
286
     * @param string $sMethod    The name of the method to check
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
287
     *
288
     * @return bool
289
     */
290
    public function hasMethod(string $sMethod): bool
291
    {
292
        return $this->xReflectionClass->hasMethod($sMethod);
293
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
294
295
    /**
296
     * Call the specified method of the registered callable object using the specified array of arguments
297
     *
298
     * @param string $sMethod    The method name
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
299
     * @param array $aArgs    The method arguments
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 7 spaces after parameter name; 4 found
Loading history...
300
     * @param bool $bAccessible    If false, only calls to public method are allowed
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
301
     *
302
     * @return mixed
303
     * @throws ReflectionException
304
     */
305
    private function callMethod(string $sMethod, array $aArgs, bool $bAccessible)
306
    {
307
        $reflectionMethod = $this->xReflectionClass->getMethod($sMethod);
308
        $reflectionMethod->setAccessible($bAccessible); // Make it possible to call protected methods
309
        return $reflectionMethod->invokeArgs($this->getRegisteredObject(), $aArgs);
310
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
311
312
    /**
313
     * Call the specified method of the registered callable object using the specified array of arguments
314
     *
315
     * @param array $aClassMethods    The method config options
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
316
     * @param string $sMethod    The method called by the request
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 4 found
Loading history...
317
     *
318
     * @return void
319
     * @throws ReflectionException
320
     */
321
    private function callHookMethods(array $aClassMethods, string $sMethod)
322
    {
323
        $aMethods = [];
324
        if(isset($aClassMethods[$sMethod]))
325
        {
326
            $aMethods = $aClassMethods[$sMethod];
327
        }
328
        elseif(isset($aClassMethods['*']))
329
        {
330
            $aMethods = $aClassMethods['*'];
331
        }
332
        foreach($aMethods as $xKey => $xValue)
333
        {
334
            $sMethodName = $xValue;
335
            $aMethodArgs = [];
336
            if(is_string($xKey))
337
            {
338
                $sMethodName = $xKey;
339
                $aMethodArgs = is_array($xValue) ? $xValue : [$xValue];
340
            }
341
            $this->callMethod($sMethodName, $aMethodArgs, true);
342
        }
343
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
344
345
    /**
346
     * Call the specified method of the registered callable object using the specified array of arguments
347
     *
348
     * @param string $sMethod    The name of the method to call
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
349
     * @param array $aArgs    The arguments to pass to the method
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter name; 4 found
Loading history...
350
     *
351
     * @return null|ResponseInterface
352
     * @throws ReflectionException
353
     */
354
    public function call(string $sMethod, array $aArgs): ?ResponseInterface
355
    {
356
        // Methods to call before processing the request
357
        $this->callHookMethods($this->aBeforeMethods, $sMethod);
358
        // Call the request method
359
        $xResponse = $this->callMethod($sMethod, $aArgs, false);
360
        // Methods to call after processing the request
361
        $this->callHookMethods($this->aAfterMethods, $sMethod);
362
        return $xResponse;
363
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 0 found
Loading history...
364
}
365