Passed
Push — master ( 4e00f9...271a5a )
by Thierry
03:37
created

CallableObject::configure()   B

Complexity

Conditions 10
Paths 9

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 23
c 1
b 0
f 0
dl 0
loc 35
rs 7.6666
cc 10
nc 9
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * CallableObject.php
5
 *
6
 * Jaxon callable object
7
 *
8
 * This class stores a reference to an object whose methods can be called from
9
 * the client via a Jaxon request
10
 *
11
 * The Jaxon plugin manager will call <CallableObject->getClientScript> so that
12
 * stub functions can be generated and sent to the browser.
13
 *
14
 * @package jaxon-core
0 ignored issues
show
Coding Style introduced by
Package name "jaxon-core" is not valid; consider "Jaxoncore" instead
Loading history...
15
 * @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...
16
 * @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...
17
 * @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...
18
 * @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...
19
 * @author Thierry Feuzeu <[email protected]>
20
 * @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...
21
 * @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...
22
 * @copyright 2016 Thierry Feuzeu <[email protected]>
23
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
24
 * @link https://github.com/jaxon-php/jaxon-core
25
 */
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...
26
27
namespace Jaxon\Plugin\Request\CallableClass;
28
29
use Jaxon\Di\Container;
30
use Jaxon\Exception\SetupException;
31
use Jaxon\Response\ResponseInterface;
32
33
use ReflectionClass;
34
use ReflectionException;
35
use ReflectionMethod;
36
use ReflectionProperty;
37
38
use function array_filter;
39
use function array_map;
40
use function array_merge;
41
use function call_user_func;
42
use function in_array;
43
use function is_array;
44
use function is_string;
45
use function json_encode;
46
use function str_replace;
47
use function substr;
48
49
class CallableObject
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class CallableObject
Loading history...
50
{
51
    /**
52
     * The DI container
53
     *
54
     * @var Container
55
     */
56
    protected $di;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line(s) before first member var; 0 found
Loading history...
57
58
    /**
59
     * The reflection class of the user registered callable object
60
     *
61
     * @var ReflectionClass
62
     */
63
    private $xReflectionClass;
64
65
    /**
66
     * A list of methods of the user registered callable object the library must not export to javascript
67
     *
68
     * @var array
69
     */
70
    private $aProtectedMethods = [];
71
72
    /**
73
     * A list of methods to call before processing the request
74
     *
75
     * @var array
76
     */
77
    private $aBeforeMethods = [];
78
79
    /**
80
     * A list of methods to call after processing the request
81
     *
82
     * @var array
83
     */
84
    private $aAfterMethods = [];
85
86
    /**
87
     * The callable object options
88
     *
89
     * @var array
90
     */
91
    private $aOptions = [];
92
93
    /**
94
     * The character to use as separator in javascript class names
95
     *
96
     * @var string
97
     */
98
    private $sSeparator = '.';
99
100
    /**
101
     * Check if the js code for this object must be generated
102
     *
103
     * @var bool
0 ignored issues
show
Bug introduced by
Expected "boolean" but found "bool" for @var tag in member variable comment
Loading history...
104
     */
105
    private $bExcluded = false;
106
107
    /**
108
     * The user registered callable object
109
     *
110
     * @var object
111
     */
112
    private $xRegisteredObject = null;
113
114
    /**
115
     * The attributes to inject in the user registered callable object
116
     *
117
     * @var array
118
     */
119
    private $aAttributes = [];
120
121
    /**
122
     * The class constructor
123
     *
124
     * @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...
125
     * @param ReflectionClass $xReflectionClass    The reflection class
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
126
     *
127
     */
128
    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...
129
    {
130
        $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...
131
        $this->xReflectionClass = $xReflectionClass;
132
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
133
134
    /**
135
     * Get callable object options
136
     *
137
     * @return array
138
     */
139
    public function getOptions(): array
140
    {
141
        return $this->aOptions;
142
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
143
144
    /**
145
     * Check if the js code for this object must be generated
146
     *
147
     * @return bool
148
     */
149
    public function excluded(): bool
150
    {
151
        return $this->bExcluded;
152
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
153
154
    /**
155
     * Set callable object options
156
     *
157
     * @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...
158
     *
159
     * @return void
160
     */
161
    public function setOptions(array $aOptions)
162
    {
163
        $this->aOptions = $aOptions;
164
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
165
166
    /**
167
     * Get the name of the corresponding javascript class
168
     *
169
     * @return string
170
     */
171
    public function getJsName(): string
172
    {
173
        return str_replace('\\', $this->sSeparator, $this->xReflectionClass->getName());
174
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
175
176
    /**
177
     * Set hook methods
178
     *
179
     * @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...
180
     * @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...
181
     *
182
     * @return void
183
     */
184
    private function setHookMethods(array &$aHookMethods, $xValue)
185
    {
186
        foreach($xValue as $sCalledMethod => $xMethodToCall)
187
        {
188
            if(is_array($xMethodToCall))
189
            {
190
                $aHookMethods[$sCalledMethod] = $xMethodToCall;
191
            }
192
            elseif(is_string($xMethodToCall))
193
            {
194
                $aHookMethods[$sCalledMethod] = [$xMethodToCall];
195
            }
196
        }
197
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
198
199
    /**
200
     * Set configuration options / call options for each method
201
     *
202
     * @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...
203
     * @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...
204
     *
205
     * @return void
206
     */
207
    public function configure(string $sName, $xValue)
208
    {
209
        switch($sName)
210
        {
211
        // Set the separator
212
        case 'separator':
213
            if($xValue === '_' || $xValue === '.')
214
            {
215
                $this->sSeparator = $xValue;
216
            }
217
            break;
218
        // Set the protected methods
219
        case 'protected':
220
            if(is_array($xValue))
221
            {
222
                $this->aProtectedMethods = array_merge($this->aProtectedMethods, $xValue);
223
            }
224
            break;
225
        // Set the methods to call before processing the request
226
        case '__before':
227
            $this->setHookMethods($this->aBeforeMethods, $xValue);
228
            break;
229
        // Set the methods to call after processing the request
230
        case '__after':
231
            $this->setHookMethods($this->aAfterMethods, $xValue);
232
            break;
233
        // Set the attributes to inject in the callable object
234
        case '__di':
235
            $this->aAttributes = array_merge($this->aAttributes, $xValue);
0 ignored issues
show
Bug introduced by
It seems like $xValue can also be of type string; however, parameter $arrays of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

235
            $this->aAttributes = array_merge($this->aAttributes, /** @scrutinizer ignore-type */ $xValue);
Loading history...
236
            break;
237
        case 'excluded':
238
            $this->bExcluded = (bool)$xValue;
239
            break;
240
        default:
241
            break;
242
        }
243
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
244
245
    /**
246
     * Get the public and protected attributes of the callable object
247
     *
248
     * @return array
249
     */
250
    public function getProperties(): array
251
    {
252
        return array_map(function($xProperty) {
253
            return $xProperty->getName();
254
        }, $this->xReflectionClass->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED));
255
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
256
257
    /**
258
     * Get the public methods of the callable object
259
     *
260
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
261
     *
262
     * @return array
263
     */
264
    public function getPublicMethods(array $aProtectedMethods = []): array
265
    {
266
        $aMethods = array_map(function($xMethod) {
267
            return $xMethod->getShortName();
268
        }, $this->xReflectionClass->getMethods(ReflectionMethod::IS_PUBLIC));
269
270
        return array_filter($aMethods, function($sMethodName) use($aProtectedMethods) {
271
            // Don't take magic __call, __construct, __destruct methods
272
            // Don't take protected methods
273
            return substr($sMethodName, 0, 2) !== '__' &&
274
                !in_array($sMethodName, $aProtectedMethods) &&
275
                !in_array($sMethodName, $this->aProtectedMethods);
276
        });
277
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
278
279
    /**
280
     * Return a list of methods of the callable object to export to javascript
281
     *
282
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
283
     *
284
     * @return array
285
     */
286
    public function getCallableMethods(array $aProtectedMethods): array
287
    {
288
        // Convert an option to a string to be displayed in the js script template.
289
        $fConvertOption = function($xOption) {
290
            return is_array($xOption) ? json_encode($xOption) : $xOption;
291
        };
292
        $aCommonConfig = isset($this->aOptions['*']) ? array_map($fConvertOption, $this->aOptions['*']) : [];
293
294
        return array_map(function($sMethodName) use($fConvertOption, $aCommonConfig) {
295
            // Specific options for this method
296
            $aMethodConfig = isset($this->aOptions[$sMethodName]) ?
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "?"; newline found
Loading history...
297
                array_map($fConvertOption, $this->aOptions[$sMethodName]) : [];
298
            return [
299
                'name' => $sMethodName,
300
                'config' => array_merge($aCommonConfig, $aMethodConfig),
301
            ];
302
        }, $this->getPublicMethods($aProtectedMethods));
303
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
304
305
    /**
306
     * Get the registered callable object
307
     *
308
     * @return null|object
309
     */
310
    public function getRegisteredObject()
311
    {
312
        return $this->di->g($this->xReflectionClass->getName());
313
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
314
315
    /**
316
     * Check if the specified method name is one of the methods of the registered callable object
317
     *
318
     * @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...
319
     *
320
     * @return bool
321
     */
322
    public function hasMethod(string $sMethod): bool
323
    {
324
        return $this->xReflectionClass->hasMethod($sMethod);
325
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
326
327
    /**
328
     * Call the specified method of the registered callable object using the specified array of arguments
329
     *
330
     * @param string $sMethod    The method name
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
331
     * @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...
332
     * @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...
333
     *
334
     * @return mixed
335
     * @throws ReflectionException
336
     */
337
    private function callMethod(string $sMethod, array $aArgs, bool $bAccessible)
338
    {
339
        $reflectionMethod = $this->xReflectionClass->getMethod($sMethod);
340
        $reflectionMethod->setAccessible($bAccessible); // Make it possible to call protected methods
341
        return $reflectionMethod->invokeArgs($this->xRegisteredObject, $aArgs);
342
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
343
344
    /**
345
     * Call the specified method of the registered callable object using the specified array of arguments
346
     *
347
     * @param array $aHookMethods    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...
348
     * @param string $sMethod    The method called by the request
0 ignored issues
show
Coding Style introduced by
Expected 6 spaces after parameter name; 4 found
Loading history...
349
     *
350
     * @return void
351
     * @throws ReflectionException
352
     */
353
    private function callHookMethods(array $aHookMethods, string $sMethod)
354
    {
355
        $aMethods = $aHookMethods[$sMethod] ?? $aHookMethods['*'] ?? [];
356
        foreach($aMethods as $xKey => $xValue)
357
        {
358
            $sMethodName = $xValue;
359
            $aMethodArgs = [];
360
            if(is_string($xKey))
361
            {
362
                $sMethodName = $xKey;
363
                $aMethodArgs = is_array($xValue) ? $xValue : [$xValue];
364
            }
365
            $this->callMethod($sMethodName, $aMethodArgs, true);
366
        }
367
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
368
369
    /**
370
     * Call the specified method of the registered callable object using the specified array of arguments
371
     *
372
     * @param string $sMethod The name of the method to call
373
     * @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; 1 found
Loading history...
374
     *
375
     * @return null|ResponseInterface
376
     * @throws ReflectionException
377
     * @throws SetupException
378
     */
379
    public function call(string $sMethod, array $aArgs): ?ResponseInterface
380
    {
381
        $this->xRegisteredObject = $this->getRegisteredObject();
382
383
        // Set attributes from the DI container
384
        $aAttributes = $this->aAttributes[$sMethod] ?? $this->aAttributes['*'] ?? [];
385
        foreach($aAttributes as $sName => $sClass)
386
        {
387
            // Set the protected attributes of the object
388
            $cSetter = function($c) use($sClass, $sName) {
389
                // Warning: dynamic properties will be deprecated in PHP8.2.
390
                $this->$sName = $c->get($sClass);
391
            };
392
            // Can now access protected attributes
393
            call_user_func($cSetter->bindTo($this->xRegisteredObject, $this->xRegisteredObject), $this->di);
394
        }
395
396
        // Methods to call before processing the request
397
        $this->callHookMethods($this->aBeforeMethods, $sMethod);
398
399
        // Call the request method
400
        $xResponse = $this->callMethod($sMethod, $aArgs, false);
401
402
        // Methods to call after processing the request
403
        $this->callHookMethods($this->aAfterMethods, $sMethod);
404
        return $xResponse;
405
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 0 found
Loading history...
406
}
407