Completed
Push — master ( dbc4d9...4d3a24 )
by Thierry
01:44
created

CallableClass::getScript()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 35

Duplication

Lines 11
Ratio 31.43 %

Importance

Changes 0
Metric Value
cc 7
nc 10
nop 0
dl 11
loc 35
rs 8.4266
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * CallableClass.php - Jaxon callable class plugin
5
 *
6
 * This class registers user defined callable classes, generates client side javascript code,
7
 * and calls their methods on user request
8
 *
9
 * @package jaxon-core
10
 * @author Jared White
11
 * @author J. Max Wilson
12
 * @author Joseph Woolley
13
 * @author Steffen Konerow
14
 * @author Thierry Feuzeu <[email protected]>
15
 * @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson
16
 * @copyright Copyright (c) 2008-2010 by Joseph Woolley, Steffen Konerow, Jared White  & J. Max Wilson
17
 * @copyright 2016 Thierry Feuzeu <[email protected]>
18
 * @license https://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
19
 * @link https://github.com/jaxon-php/jaxon-core
20
 */
21
22
namespace Jaxon\Request\Plugin;
23
24
use Jaxon\Jaxon;
25
use Jaxon\Plugin\Request as RequestPlugin;
26
27
class CallableClass extends RequestPlugin
28
{
29
    use \Jaxon\Utils\Traits\Config;
30
    use \Jaxon\Utils\Traits\Manager;
31
    use \Jaxon\Utils\Traits\Validator;
32
    use \Jaxon\Utils\Traits\Translator;
33
34
    /**
35
     * The classes of the registered callable objects
36
     *
37
     * @var array
38
     */
39
    protected $aClassOptions = [];
40
41
    /**
42
     * The registered callable objects
43
     *
44
     * @var array
45
     */
46
    protected $aCallableObjects = [];
47
48
    /**
49
     * The value of the class parameter of the incoming Jaxon request
50
     *
51
     * @var string
52
     */
53
    protected $sRequestedClass = null;
54
55
    /**
56
     * The value of the method parameter of the incoming Jaxon request
57
     *
58
     * @var string
59
     */
60
    protected $sRequestedMethod = null;
61
62 View Code Duplication
    public function __construct()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
63
    {
64
        if(!empty($_GET['jxncls']))
65
        {
66
            $this->sRequestedClass = $_GET['jxncls'];
67
        }
68
        if(!empty($_GET['jxnmthd']))
69
        {
70
            $this->sRequestedMethod = $_GET['jxnmthd'];
71
        }
72
        if(!empty($_POST['jxncls']))
73
        {
74
            $this->sRequestedClass = $_POST['jxncls'];
75
        }
76
        if(!empty($_POST['jxnmthd']))
77
        {
78
            $this->sRequestedMethod = $_POST['jxnmthd'];
79
        }
80
    }
81
82
    /**
83
     * Return the name of this plugin
84
     *
85
     * @return string
86
     */
87
    public function getName()
88
    {
89
        return Jaxon::CALLABLE_CLASS;
90
    }
91
92
    /**
93
     * Register a callable class
94
     *
95
     * @param string        $sType          The type of request handler being registered
96
     * @param string        $sClassName     The name of the class being registered
97
     * @param array|string  $aOptions       The associated options
98
     *
99
     * @return boolean
100
     */
101
    public function register($sType, $sClassName, $aOptions)
102
    {
103
        if($sType != $this->getName())
104
        {
105
            return false;
106
        }
107
108
        if(!is_string($sClassName))
109
        {
110
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
111
        }
112
        if(!is_array($aOptions))
113
        {
114
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
115
        }
116
117
        $sClassName = trim($sClassName, '\\');
118
        $this->aClassOptions[$sClassName] = $aOptions;
119
120
        return true;
121
    }
122
123
    /**
124
     * Find a callable object by class name
125
     *
126
     * @param string        $sClassName            The class name of the callable object
127
     *
128
     * @return object
129
     */
130
    public function getCallableObject($sClassName)
131
    {
132
        // Replace all separators ('.' and '_') with antislashes, and remove the antislashes
133
        // at the beginning and the end of the class name.
134
        $sClassName = trim(str_replace(['.', '_'], ['\\', '\\'], (string)$sClassName), '\\');
135
136
        if(!key_exists($sClassName, $this->aClassOptions))
137
        {
138
            return null; // Class not registered
139
        }
140
141
        if(key_exists($sClassName, $this->aCallableObjects))
142
        {
143
            return $this->aCallableObjects[$sClassName];
144
        }
145
146
        // Create the callable object
147
        $xCallableObject = new \Jaxon\Request\Support\CallableObject($sClassName);
148
        foreach($aOptions as $sMethod => $aValue)
0 ignored issues
show
Bug introduced by
The variable $aOptions does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
149
        {
150
            foreach($aValue as $sName => $sValue)
151
            {
152
                $xCallableObject->configure($sMethod, $sName, $sValue);
153
            }
154
        }
155
156
        // Make sure the registered class exists
157
        // We need to check this after the callable object is configured
158
        // to take the 'include' option into account.
159
        if(!class_exists($sClassName))
160
        {
161
            return null;
162
        }
163
164
        $this->aCallableObjects[$sClassName] = $xCallableObject;
165
        // Register the request factory for this callable object
166
        jaxon()->di()->set($sClassName . '\Factory\Rq', function ($di) use ($sClassName) {
0 ignored issues
show
Unused Code introduced by
The parameter $di is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
167
            $xCallableObject = $this->aCallableObjects[$sClassName];
168
            return new \Jaxon\Factory\Request\Portable($xCallableObject);
169
        });
170
        // Register the paginator factory for this callable object
171
        jaxon()->di()->set($sClassName . '\Factory\Pg', function ($di) use ($sClassName) {
0 ignored issues
show
Unused Code introduced by
The parameter $di is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
172
            $xCallableObject = $this->aCallableObjects[$sClassName];
173
            return new \Jaxon\Factory\Request\Paginator($xCallableObject);
174
        });
175
176
        return $xCallableObject;
177
    }
178
179
    /**
180
     * Find a user registered callable object by class name
181
     *
182
     * @param string        $sClassName            The class name of the callable object
183
     *
184
     * @return object
185
     */
186
    public function getRegisteredObject($sClassName)
187
    {
188
        // Get the corresponding callable object
189
        $xCallableObject = $this->getCallableObject($sClassName);
190
        return ($xCallableObject) ? $xCallableObject->getRegisteredObject() : null;
191
    }
192
193
    /**
194
     * Create callable objects for all registered namespaces
195
     *
196
     * @return void
197
     */
198
    private function createCallableObjects()
199
    {
200
        foreach(array_keys($this->aClassOptions) as $sClassName)
201
        {
202
            $this->getCallableObject($sClassName);
203
        }
204
    }
205
206
    /**
207
     * Generate a hash for the registered callable objects
208
     *
209
     * @return string
210
     */
211
    public function generateHash()
212
    {
213
        $this->createCallableObjects();
214
215
        $sHash = '';
216
        foreach($this->aCallableObjects as $sClassName => $xCallableObject)
217
        {
218
            $sHash .= $sClassName . implode('|', $xCallableObject->getMethods());
219
        }
220
221
        return md5($sHash);
222
    }
223
224
    /**
225
     * Generate client side javascript code for the registered callable objects
226
     *
227
     * @return string
228
     */
229
    public function getScript()
230
    {
231
        $this->createCallableObjects();
232
233
        // Generate code for javascript objects declaration
234
        $sJaxonPrefix = $this->getOption('core.prefix.class');
235
        $aJsClasses = [];
236
        $sCode = '';
237
        foreach($this->aClassOptions as $sClassName => $aOptions)
238
        {
239
            if(key_exists('separator', $aOptions) && $aOptions['separator'] != '.')
240
            {
241
                continue;
242
            }
243
            $offset = 0;
244
            $sJsClasses = str_replace('\\', '.', $sClassName);
245 View Code Duplication
            while(($dotPosition = strpos($sJsClasses, '.', $offset)) !== false)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
246
            {
247
                $sJsClass = substr($sJsClasses, 0, $dotPosition);
248
                // Generate code for this object
249
                if(!key_exists($sJsClass, $aJsClasses))
250
                {
251
                    $sCode .= "$sJaxonPrefix$sJsClass = {};\n";
252
                    $aJsClasses[$sJsClass] = $sJsClass;
253
                }
254
                $offset = $dotPosition + 1;
255
            }
256
        }
257
        foreach($this->aCallableObjects as $xCallableObject)
258
        {
259
            $sCode .= $xCallableObject->getScript();
260
        }
261
262
        return $sCode;
263
    }
264
265
    /**
266
     * Check if this plugin can process the incoming Jaxon request
267
     *
268
     * @return boolean
269
     */
270
    public function canProcessRequest()
271
    {
272
        // Check the validity of the class name
273 View Code Duplication
        if(($this->sRequestedClass) && !$this->validateClass($this->sRequestedClass))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274
        {
275
            $this->sRequestedClass = null;
276
            $this->sRequestedMethod = null;
277
        }
278
        // Check the validity of the method name
279 View Code Duplication
        if(($this->sRequestedMethod) && !$this->validateMethod($this->sRequestedMethod))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
280
        {
281
            $this->sRequestedClass = null;
282
            $this->sRequestedMethod = null;
283
        }
284
        return ($this->sRequestedClass != null && $this->sRequestedMethod != null &&
285
            key_exists($this->sRequestedClass, $this->aCallableObjects));
286
    }
287
288
    /**
289
     * Process the incoming Jaxon request
290
     *
291
     * @return boolean
292
     */
293 View Code Duplication
    public function processRequest()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
    {
295
        if(!$this->canProcessRequest())
296
        {
297
            return false;
298
        }
299
300
        $aArgs = $this->getRequestManager()->process();
301
302
        // Find the requested method
303
        $xCallableObject = $this->getCallableObject($this->sRequestedClass);
304
        if(!$xCallableObject || !$xCallableObject->hasMethod($this->sRequestedMethod))
305
        {
306
            // Unable to find the requested object or method
307
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid',
308
                ['class' => $this->sRequestedClass, 'method' => $this->sRequestedMethod]));
309
        }
310
311
        // Call the requested method
312
        $xCallableObject->call($this->sRequestedMethod, $aArgs);
313
        return true;
314
    }
315
}
316