Completed
Push — master ( 4500a3...cfb9f9 )
by Thierry
01:39
created

CallableObject::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 16
rs 9.4285
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
13
 * @author Jared White
14
 * @author J. Max Wilson
15
 * @author Joseph Woolley
16
 * @author Steffen Konerow
17
 * @author Thierry Feuzeu <[email protected]>
18
 * @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson
19
 * @copyright Copyright (c) 2008-2010 by Joseph Woolley, Steffen Konerow, Jared White  & J. Max Wilson
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
 */
24
25
namespace Jaxon\Request\Support;
26
27
use Jaxon\Request\Request;
28
29
class CallableObject
30
{
31
    use \Jaxon\Utils\Traits\Config;
32
    use \Jaxon\Utils\Traits\Manager;
33
    use \Jaxon\Utils\Traits\Template;
34
35
    /**
36
     * A reference to the callable object the user has registered
37
     *
38
     * @var object
39
     */
40
    private $callableObject;
41
42
    /**
43
     * The reflection class of the user registered callable object
44
     *
45
     * @var \ReflectionClass
46
     */
47
    private $reflectionClass;
48
    
49
    /**
50
     * A list of methods of the user registered callable object the library must not export to javascript
51
     *
52
     * @var array
53
     */
54
    private $aProtectedMethods;
55
    
56
    /**
57
     * The namespace where the callable object class is defined
58
     *
59
     * @var string
60
     */
61
    private $namespace = '';
62
    
63
    /**
64
     * The path to the directory where the callable object class is defined, starting from the namespace root
65
     *
66
     * @var string
67
     */
68
    private $classpath = '';
69
    
70
    /**
71
     * The character to use as separator in javascript class names
72
     *
73
     * @var string
74
     */
75
    private $separator = '.';
76
    
77
    /**
78
     * An associative array that will contain configuration options for zero or more of the objects methods
79
     *
80
     * These configuration options will define the call options for each request.
81
     * The call options will be passed to the client browser when the function stubs are generated.
82
     *
83
     * @var array
84
     */
85
    private $aConfiguration;
86
    
87
    /**
88
     * The class constructor
89
     *
90
     * @param object|string            $xCallable               The callable object instance or class name
91
     *
92
     */
93
    public function __construct($xCallable)
94
    {
95
        if(is_string($xCallable)) // Received a class name
96
        {
97
            $this->reflectionClass = new \ReflectionClass($xCallable);
98
            $this->callableObject = null;
99
        }
100
        else // if(is_object($xCallable)) // Received a class instance
101
        {
102
            $this->reflectionClass = new \ReflectionClass(get_class($xCallable));
103
            $this->setCallable($xCallable);
104
        }
105
        $this->aConfiguration = array();
106
        // By default, no method is "protected"
107
        $this->aProtectedMethods = array();
108
    }
109
110
    /**
111
     * Set a user registered callable object.
112
     *
113
     * If the input parameter is null, the callable is first created with its reflection object.
114
     *
115
     * @param object|null           $xCallable          The callable object instance or null
116
     *
117
     * @return void
118
     */
119
    private function setCallable($xCallable = null)
120
    {
121
        if($xCallable == null)
122
        {
123
            $xCallable = $this->reflectionClass->newInstance();
124
        }
125
        // Save the Jaxon callable object into the user callable object
126
        if($this->reflectionClass->hasMethod('setJaxonCallable'))
127
        {
128
            $xCallable->setJaxonCallable($this);
129
        }
130
        $this->callableObject = $xCallable;
131
    }
132
133
    /**
134
     * Return the registered callable object
135
     *
136
     * @return object
137
     */
138
    public function getRegisteredObject()
139
    {
140
        if($this->callableObject == null)
141
        {
142
            $this->setCallable();
143
        }
144
        return $this->callableObject;
145
    }
146
147
    /**
148
     * Return the class name of this callable object, without the namespace if any
149
     *
150
     * @return string
151
     */
152
    public function getClassName()
153
    {
154
        // Get the class name without the namespace.
155
        return $this->reflectionClass->getShortName();
156
    }
157
158
    /**
159
     * Return the name of this callable object
160
     *
161
     * @return string
162
     */
163
    public function getName()
164
    {
165
        // The class name without the namespace.
166
        $name = $this->reflectionClass->getShortName();
167
        // Append the classpath to the name
168
        if(($this->classpath))
169
        {
170
            $name = $this->classpath . '\\' . $name;
171
        }
172
        return $name;
173
    }
174
175
    /**
176
     * Return the javascript name of this callable object
177
     *
178
     * @return string
179
     */
180
    public function getJsName()
181
    {
182
        return str_replace('\\', $this->separator, $this->getName());
183
    }
184
185
    /**
186
     * Return the namespace of this callable object
187
     *
188
     * @return string
189
     */
190
    public function getNamespace()
191
    {
192
        // The namespace the class was registered with.
193
        return $this->namespace;
194
    }
195
196
    /**
197
     * Return the class path of this callable object
198
     *
199
     * @return string
200
     */
201
    public function getPath()
202
    {
203
        // The class path without the trailing separator.
204
        return $this->classpath;
205
    }
206
207
    /**
208
     * Return a list of methods of the callable object to export to javascript
209
     *
210
     * @return array
211
     */
212
    public function getMethods()
213
    {
214
        $aReturn = array();
215
        foreach($this->reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $xMethod)
216
        {
217
            $sMethodName = $xMethod->getShortName();
218
            // Don't take magic __call, __construct, __destruct methods
219
            if(strlen($sMethodName) > 2 && substr($sMethodName, 0, 2) == '__')
220
            {
221
                continue;
222
            }
223
            // Don't take excluded methods
224
            if(in_array($sMethodName, $this->aProtectedMethods))
225
            {
226
                continue;
227
            }
228
            $aReturn[] = $sMethodName;
229
        }
230
        return $aReturn;
231
    }
232
233
    /**
234
     * Set configuration options / call options for each method
235
     *
236
     * @param string        $sMethod            The name of the method
237
     * @param string        $sName                The name of the configuration option
238
     * @param string        $sValue                The value of the configuration option
239
     *
240
     * @return void
241
     */
242
    public function configure($sMethod, $sName, $sValue)
243
    {
244
        // Set the namespace
245
        if($sName == 'namespace')
246
        {
247
            if($sValue != '')
248
                $this->namespace = $sValue;
249
            return;
250
        }
251
        // Set the classpath
252
        if($sName == 'classpath')
253
        {
254
            if($sValue != '')
255
                $this->classpath = trim($sValue, '\\');
256
            return;
257
        }
258
        // Set the separator
259
        if($sName == 'separator')
260
        {
261
            if($sValue == '_' || $sValue == '.')
262
                $this->separator = $sValue;
263
            return;
264
        }
265
        // Set the excluded methods
266
        if($sName == 'protected')
267
        {
268
            if(is_array($sValue))
269
                $this->aProtectedMethods = array_merge($this->aProtectedMethods, $sValue);
270
            elseif(is_string($sValue))
271
                $this->aProtectedMethods[] = $sValue;
272
            return;
273
        }
274
        
275
        if(!isset($this->aConfiguration[$sMethod]))
276
        {
277
            $this->aConfiguration[$sMethod] = array();
278
        }
279
        $this->aConfiguration[$sMethod][$sName] = $sValue;
280
    }
281
    
282
    /**
283
     * Generate client side javascript code for calls to all methods exposed by this callable object
284
     *
285
     * @return string
286
     */
287
    public function getScript()
288
    {
289
        $sJaxonPrefix = $this->getOption('core.prefix.class');
290
        // "\" are replaced with the configured separator in the generated javascript code.
291
        $sClass = str_replace('\\', $this->separator, $this->getName());
292
        $aMethods = array();
293
294
        // Common options to be set on all methods
295
        $aCommonConfig = array_key_exists('*', $this->aConfiguration) ? $this->aConfiguration['*'] : array();
296
        foreach($this->reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $xMethod)
297
        {
298
            $sMethodName = $xMethod->getShortName();
299
            // Don't export magic __call, __construct, __destruct methods
300
            if(strlen($sMethodName) > 0 && substr($sMethodName, 0, 2) == '__')
301
            {
302
                continue;
303
            }
304
            // Don't export "protected" methods
305
            if(in_array($sMethodName, $this->aProtectedMethods))
306
            {
307
                continue;
308
            }
309
            // Specific options for this method
310
            $aMethodConfig = array_key_exists($sMethodName, $this->aConfiguration) ?
311
                array_merge($aCommonConfig, $this->aConfiguration[$sMethodName]) : $aCommonConfig;
312
            $aMethod = array('name' => $sMethodName, 'config' => $aMethodConfig);
313
            $aMethods[] = $aMethod;
314
        }
315
316
        return $this->render('jaxon::support/object.js', array(
317
            'sPrefix' => $sJaxonPrefix,
318
            'sClass' => $sClass,
319
            'aMethods' => $aMethods,
320
        ));
321
    }
322
    
323
    /**
324
     * Check if the specified class name matches the class name of the registered callable object
325
     *
326
     * @return boolean
327
     */
328
    public function isClass($sClass)
329
    {
330
        return ($this->reflectionClass->getName() === $sClass);
331
    }
332
    
333
    /**
334
     * Check if the specified method name is one of the methods of the registered callable object
335
     *
336
     * @param string        $sMethod            The name of the method to check
337
     *
338
     * @return boolean
339
     */
340
    public function hasMethod($sMethod)
341
    {
342
        return $this->reflectionClass->hasMethod($sMethod) || $this->reflectionClass->hasMethod('__call');
343
    }
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
349
     * @param array         $aArgs              The arguments to pass to the method
350
     *
351
     * @return void
352
     */
353
    public function call($sMethod, $aArgs)
354
    {
355
        if(!$this->hasMethod($sMethod))
356
            return;
357
        $reflectionMethod = $this->reflectionClass->getMethod($sMethod);
358
        $callableObject = $this->getRegisteredObject();
359
        $this->getResponseManager()->append($reflectionMethod->invokeArgs($callableObject, $aArgs));
360
    }
361
}
362