Passed
Push — master ( 818b3e...c1bcf5 )
by Thierry
02:23
created

CallableObject::getJsName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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