Passed
Push — master ( 7c83a0...cd350b )
by Thierry
02:08
created

CallableObject::call()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 9
rs 10
c 0
b 0
f 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\Request\Plugin\CallableClass;
26
27
use Jaxon\Container\Container;
28
use Jaxon\Response\Response;
29
30
use ReflectionClass;
31
use ReflectionException;
32
33
use function array_map;
34
use function array_filter;
35
use function array_merge;
36
use function in_array;
37
use function is_array;
38
use function is_string;
39
use function substr;
40
41
class CallableObject
0 ignored issues
show
Coding Style introduced by
Missing doc comment for class CallableObject
Loading history...
42
{
43
    /**
44
     * The DI container
45
     *
46
     * @var Container
47
     */
48
    protected $di;
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line(s) before first member var; 0 found
Loading history...
49
50
    /**
51
     * The reflection class of the user registered callable object
52
     *
53
     * @var ReflectionClass
54
     */
55
    private $xReflectionClass;
56
57
    /**
58
     * A list of methods of the user registered callable object the library must not export to javascript
59
     *
60
     * @var array
61
     */
62
    private $aProtectedMethods = [];
63
64
    /**
65
     * A list of methods to call before processing the request
66
     *
67
     * @var array
68
     */
69
    private $aBeforeMethods = [];
70
71
    /**
72
     * A list of methods to call after processing the request
73
     *
74
     * @var array
75
     */
76
    private $aAfterMethods = [];
77
78
    /**
79
     * The callable object options
80
     *
81
     * @var array
82
     */
83
    private $aOptions = [];
84
85
    /**
86
     * The character to use as separator in javascript class names
87
     *
88
     * @var string
89
     */
90
    private $sSeparator = '.';
91
92
    /**
93
     * The class constructor
94
     *
95
     * @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...
96
     * @param ReflectionClass $xReflectionClass    The reflection class
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
97
     *
98
     */
99
    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...
100
    {
101
        $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...
102
        $this->xReflectionClass = $xReflectionClass;
103
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
104
105
    /**
106
     * Set callable object options
107
     *
108
     * @param array  $aOptions
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter type; 2 found
Loading history...
Coding Style introduced by
Missing parameter comment
Loading history...
109
     *
110
     * @return void
111
     */
112
    public function setOptions(array $aOptions)
113
    {
114
        $this->aOptions = $aOptions;
115
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
116
117
    /**
118
     * Get the name of the corresponding javascript class
119
     *
120
     * @return string
121
     */
122
    public function getJsName(): string
123
    {
124
        return str_replace('\\', $this->sSeparator, $this->xReflectionClass->getName());
125
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
126
127
    /**
128
     * Set hook methods
129
     *
130
     * @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...
131
     * @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...
132
     *
133
     * @return void
134
     */
135
    private function setHookMethods(array &$aHookMethods, $xValue)
136
    {
137
        foreach($xValue as $sCalledMethod => $xMethodToCall)
138
        {
139
            if(is_array($xMethodToCall))
140
            {
141
                $aHookMethods[$sCalledMethod] = $xMethodToCall;
142
            }
143
            elseif(is_string($xMethodToCall))
144
            {
145
                $aHookMethods[$sCalledMethod] = [$xMethodToCall];
146
            }
147
        }
148
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
149
150
    /**
151
     * Set configuration options / call options for each method
152
     *
153
     * @param string $sName    The name of the configuration option
0 ignored issues
show
Coding Style introduced by
Expected 2 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 7 spaces after parameter type; 1 found
Loading history...
154
     * @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...
155
     *
156
     * @return void
157
     */
158
    public function configure(string $sName, $xValue)
159
    {
160
        switch($sName)
161
        {
162
        // Set the separator
163
        case 'separator':
164
            if($xValue === '_' || $xValue === '.')
165
            {
166
                $this->sSeparator = $xValue;
167
            }
168
            break;
169
        // Set the protected methods
170
        case 'protected':
171
            if(is_array($xValue))
172
            {
173
                $this->aProtectedMethods = array_merge($this->aProtectedMethods, $xValue);
174
            }
175
            elseif(is_string($xValue))
0 ignored issues
show
introduced by
The condition is_string($xValue) is always true.
Loading history...
176
            {
177
                $this->aProtectedMethods[] = $xValue;
178
            }
179
            break;
180
        // Set the methods to call before processing the request
181
        case '__before':
182
            $this->setHookMethods($this->aBeforeMethods, $xValue);
183
            break;
184
        // Set the methods to call after processing the request
185
        case '__after':
186
            $this->setHookMethods($this->aAfterMethods, $xValue);
187
            break;
188
        default:
189
            break;
190
        }
191
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
192
193
    /**
194
     * Return a list of methods of the callable object
195
     *
196
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
197
     *
198
     * @return array
199
     */
200
    private function _getMethods(array $aProtectedMethods): array
201
    {
202
        $aMethods = array_map(function($xMethod) {
203
            return $xMethod->getShortName();
204
        }, $this->xReflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC));
205
        return array_filter($aMethods, function($sMethodName) use($aProtectedMethods) {
206
            // Don't take magic __call, __construct, __destruct methods
207
            // Don't take protected methods
208
            return !(substr($sMethodName, 0, 2) === '__' ||
209
                in_array($sMethodName, $aProtectedMethods) ||
210
                in_array($sMethodName, $this->aProtectedMethods));
211
        });
212
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
213
214
    /**
215
     * Return a list of methods of the callable object to export to javascript
216
     *
217
     * @param array $aProtectedMethods    The protected methods
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
218
     *
219
     * @return array
220
     */
221
    public function getMethods(array $aProtectedMethods): array
222
    {
223
        // Convert an option to a string to be displayed in the js script template.
224
        $fConvertOption = function($xOption) {
225
            return is_array($xOption) ? json_encode($xOption) : $xOption;
226
        };
227
        $aCommonConfig = isset($this->aOptions['*']) ? array_map($fConvertOption, $this->aOptions['*']) : [];
228
229
        return array_map(function($sMethodName) use($fConvertOption, $aCommonConfig) {
230
            // Specific options for this method
231
            $aMethodConfig = isset($this->aOptions[$sMethodName]) ?
0 ignored issues
show
Coding Style introduced by
Expected 1 space after "?"; newline found
Loading history...
232
                array_map($fConvertOption, $this->aOptions[$sMethodName]) : [];
233
            return [
234
                'name' => $sMethodName,
235
                'config' => array_merge($aCommonConfig, $aMethodConfig),
236
            ];
237
        }, $this->_getMethods($aProtectedMethods));
238
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
239
240
    /**
241
     * Get the registered callable object
242
     *
243
     * @return null|object
244
     */
245
    public function getRegisteredObject()
246
    {
247
        return $this->di->get($this->xReflectionClass->getName());
248
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
249
250
    /**
251
     * Check if the specified method name is one of the methods of the registered callable object
252
     *
253
     * @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...
254
     *
255
     * @return bool
256
     */
257
    public function hasMethod(string $sMethod): bool
258
    {
259
        return $this->xReflectionClass->hasMethod($sMethod);
260
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
261
262
    /**
263
     * Call the specified method of the registered callable object using the specified array of arguments
264
     *
265
     * @param string $sMethod    The method name
0 ignored issues
show
Coding Style introduced by
Expected 5 spaces after parameter name; 4 found
Loading history...
266
     * @param array $aArgs    The method arguments
0 ignored issues
show
Coding Style introduced by
Expected 7 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
267
     * @param bool $bAccessible    If false, only calls to public method are allowed
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 3 spaces after parameter type; 1 found
Loading history...
268
     *
269
     * @return mixed
270
     * @throws ReflectionException
271
     */
272
    private function callMethod(string $sMethod, array $aArgs, bool $bAccessible)
273
    {
274
        $reflectionMethod = $this->xReflectionClass->getMethod($sMethod);
275
        $reflectionMethod->setAccessible($bAccessible); // Make it possible to call protected methods
276
        return $reflectionMethod->invokeArgs($this->getRegisteredObject(), $aArgs);
277
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 1 found
Loading history...
278
279
    /**
280
     * Call the specified method of the registered callable object using the specified array of arguments
281
     *
282
     * @param array $aClassMethods    The method config options
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
283
     * @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...
284
     *
285
     * @return void
286
     * @throws ReflectionException
287
     */
288
    private function callHookMethods(array $aClassMethods, string $sMethod)
289
    {
290
        $aMethods = [];
291
        if(isset($aClassMethods[$sMethod]))
292
        {
293
            $aMethods = $aClassMethods[$sMethod];
294
        }
295
        elseif(isset($aClassMethods['*']))
296
        {
297
            $aMethods = $aClassMethods['*'];
298
        }
299
        foreach($aMethods as $xKey => $xValue)
300
        {
301
            $sMethodName = $xValue;
302
            $aMethodArgs = [];
303
            if(is_string($xKey))
304
            {
305
                $sMethodName = $xKey;
306
                $aMethodArgs = is_array($xValue) ? $xValue : [$xValue];
307
            }
308
            $this->callMethod($sMethodName, $aMethodArgs, true);
309
        }
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 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...
316
     * @param array $aArgs    The arguments to pass to the method
0 ignored issues
show
Coding Style introduced by
Expected 3 spaces after parameter name; 4 found
Loading history...
Coding Style introduced by
Expected 2 spaces after parameter type; 1 found
Loading history...
317
     *
318
     * @return null|Response
319
     * @throws ReflectionException
320
     */
321
    public function call(string $sMethod, array $aArgs): ?Response
322
    {
323
        // Methods to call before processing the request
324
        $this->callHookMethods($this->aBeforeMethods, $sMethod);
325
        // Call the request method
326
        $xResponse = $this->callMethod($sMethod, $aArgs, false);
327
        // Methods to call after processing the request
328
        $this->callHookMethods($this->aAfterMethods, $sMethod);
329
        return $xResponse;
330
    }
0 ignored issues
show
Coding Style introduced by
Expected 2 blank lines after function; 0 found
Loading history...
331
}
332