Completed
Push — master ( 1cd1d8...dbc4d9 )
by Thierry
02:53
created

CallableDir::generateHash()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * CallableDir.php - Jaxon callable dir plugin
5
 *
6
 * This class registers directories containing user defined callable classes,
7
 * and generates client side javascript code.
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 CallableDir 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 registered callable objects
36
     *
37
     * @var array
38
     */
39
    protected $aCallableDirs = [];
40
41
    /**
42
     * True if the Composer autoload is enabled
43
     *
44
     * @var boolean
45
     */
46
    private $bAutoloadEnabled = true;
47
48
    /**
49
     * The Composer autoloader
50
     *
51
     * @var Autoloader
52
     */
53
    private $xAutoloader = null;
54
55
    /**
56
     * Return the name of this plugin
57
     *
58
     * @return string
59
     */
60
    public function getName()
61
    {
62
        return Jaxon::CALLABLE_DIR;
63
    }
64
65
    /**
66
     * Use the Composer autoloader
67
     *
68
     * @return void
69
     */
70
    public function useComposerAutoloader()
71
    {
72
        $this->bAutoloadEnabled = true;
73
        $this->xAutoloader = require(__DIR__ . '/../../../../autoload.php');
74
    }
75
76
    /**
77
     * Disable the autoloader in the library
78
     *
79
     * The user shall provide an alternative autoload system.
80
     *
81
     * @return void
82
     */
83
    public function disableAutoload()
84
    {
85
        $this->bAutoloadEnabled = false;
86
        $this->xAutoloader = null;
87
    }
88
89
    /**
90
     * Register a callable class
91
     *
92
     * @param string        $sType          The type of request handler being registered
93
     * @param string        $sDirectory     The name of the class being registered
94
     * @param array|string  $aOptions       The associated options
95
     *
96
     * @return boolean
97
     */
98
    public function register($sType, $sDirectory, $aOptions)
99
    {
100
        if($sType != $this->getName())
101
        {
102
            return false;
103
        }
104
105
        if(!is_string($sDirectory) || !is_dir($sDirectory))
106
        {
107
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
108
        }
109
        $sDirectory = trim($sDirectory, DIRECTORY_SEPARATOR);
110
        $this->aCallableDirs[] = $sDirectory;
111
112
        if(is_string($aOptions))
113
        {
114
            $aOptions = ['namespace' => $aOptions];
115
        }
116
        if(!is_array($aOptions))
117
        {
118
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
119
        }
120
121
        if(!is_dir(($sDirectory = trim($sDirectory))))
122
        {
123
            return false;
124
        }
125
126
        $aProtected = key_exists('protected', $aOptions) ? $aOptions['protected'] : [];
127
        if(!is_array($aProtected))
128
        {
129
            throw new \Jaxon\Exception\Error($this->trans('errors.objects.invalid-declaration'));
130
        }
131
132
        $sSeparator = key_exists('separator', $aOptions) ? $aOptions['separator'] : '.';
133
        // Only '.' and '_' are allowed to be used as separator. Any other value is ignored and '.' is used instead.
134
        if(($sSeparator = trim($sSeparator)) != '_')
135
        {
136
            $sSeparator = '.';
137
        }
138
139
        $sNamespace = key_exists('namespace', $aOptions) ? $aOptions['namespace'] : '';
140
        if(!($sNamespace = trim($sNamespace, ' \\')))
141
        {
142
            $sNamespace = '';
143
        }
144
        if(($sNamespace))
145
        {
146
            // If there is an autoloader, register the dir with PSR4 autoloading
147
            if(($this->xAutoloader))
148
            {
149
                $this->xAutoloader->setPsr4($sNamespace . '\\', $sDirectory);
150
            }
151
        }
152
        elseif(($this->xAutoloader))
153
        {
154
            // If there is an autoloader, register the dir with classmap autoloading
155
            $itDir = new RecursiveDirectoryIterator($sDirectory);
156
            $itFile = new RecursiveIteratorIterator($itDir);
157
            // Iterate on dir content
158
            foreach($itFile as $xFile)
159
            {
160
                // skip everything except PHP files
161
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
162
                {
163
                    continue;
164
                }
165
                $this->xAutoloader->addClassMap([$xFile->getBasename('.php') => $xFile->getPathname()]);
166
            }
167
        }
168
169
        $this->aCallableDirs[$sDirectory] = [
170
            'namespace' => $sNamespace,
171
            'separator' => $sSeparator,
172
            'protected' => $aProtected,
173
        ];
174
175
        return true;
176
    }
177
178
    /**
179
     * Register an instance of a given class from a file
180
     *
181
     * @param object            $xFile                  The PHP file containing the class
182
     * @param string            $sDirectory             The path to the directory
183
     * @param string|''         $sNamespace             The associated namespace
0 ignored issues
show
Documentation introduced by
The doc-type string|'' could not be parsed: Unknown type name "''" at position 7. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
184
     * @param string            $sSeparator             The character to use as separator in javascript class names
185
     * @param array             $aProtected             The functions that are not to be exported
186
     * @param array             $aOptions               The options to register the class with
187
     *
188
     * @return void
189
     */
190
    protected function _registerClass($xFile, $sDirectory, $sNamespace = '', $sSeparator = '.',
191
        array $aProtected = [], array $aOptions = [])
192
    {
193
        $sDS = DIRECTORY_SEPARATOR;
194
        // Get the corresponding class path and name
195
        $sClassPath = substr($xFile->getPath(), strlen($sDirectory));
196
        $sClassPath = str_replace($sDS, '\\', trim($sClassPath, $sDS));
197
        $sClassName = $xFile->getBasename('.php');
198
        if(($sNamespace))
199
        {
200
            $sClassPath = ($sClassPath) ? $sNamespace . '\\' . $sClassPath : $sNamespace;
201
            $sClassName = '\\' . $sClassPath . '\\' . $sClassName;
202
        }
203
        // Require the file only if autoload is enabled but there is no autoloader
204
        if(($this->bAutoloadEnabled) && !($this->xAutoloader))
205
        {
206
            require_once($xFile->getPathname());
207
        }
208
        // Create and register an instance of the class
209
        if(!array_key_exists('*', $aOptions) || !is_array($aOptions['*']))
210
        {
211
            $aOptions['*'] = [];
212
        }
213
        $aOptions['*']['separator'] = $sSeparator;
214
        if(($sNamespace))
215
        {
216
            $aOptions['*']['namespace'] = $sNamespace;
217
        }
218
        if(($sClassPath))
219
        {
220
            $aOptions['*']['classpath'] = $sClassPath;
221
        }
222
        // Filter excluded methods
223
        $aProtected = array_filter($aProtected, function ($sName) {return is_string($sName);});
224
        if(count($aProtected) > 0)
225
        {
226
            $aOptions['*']['protected'] = $aProtected;
227
        }
228
229
        $this->callableClassPlugin->register(Jaxon::CALLABLE_CLASS, $sClassName, $aOptions);
0 ignored issues
show
Bug introduced by
The property callableClassPlugin does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
230
    }
231
232
    /**
233
     * Register callable objects from all class directories
234
     *
235
     * @param array             $aOptions               The options to register the classes with
236
     *
237
     * @return void
238
     */
239
    public function registerClasses(array $aOptions = [])
240
    {
241
        // Get the callable class plugin
242
        $this->callableClassPlugin = $this->getPluginManager()->getRequestPlugin(Jaxon::CALLABLE_CLASS);
243
244
        $sDS = DIRECTORY_SEPARATOR;
245
        // Change the keys in $aOptions to have "\" as separator
246
        $aNewOptions = [];
247
        foreach($aOptions as $key => $aOption)
248
        {
249
            $key = trim(str_replace(['.', '_'], ['\\', '\\'], $key), ' \\');
250
            $aNewOptions[$key] = $aOption;
251
        }
252
253
        foreach($this->aCallableDirs as $sDirectory => $aDirOptions)
254
        {
255
            // Get the namespace
256
            $sNamespace = $aDirOptions['namespace'];
257
258
            $itDir = new RecursiveDirectoryIterator($sDirectory);
259
            $itFile = new RecursiveIteratorIterator($itDir);
260
            // Iterate on dir content
261
            foreach($itFile as $xFile)
262
            {
263
                // skip everything except PHP files
264
                if(!$xFile->isFile() || $xFile->getExtension() != 'php')
265
                {
266
                    continue;
267
                }
268
269
                // Get the class name
270
                $sClassPath = substr($xFile->getPath(), strlen($sDirectory));
271
                $sClassPath = trim(str_replace($sDS, '\\', $sClassPath), '\\');
272
                $sClassName = $xFile->getBasename('.php');
273
                if(($sClassPath))
274
                {
275
                    $sClassName = $sClassPath . '\\' . $sClassName;
276
                }
277
                if(($sNamespace))
278
                {
279
                    $sClassName = $sNamespace . '\\' . $sClassName;
280
                }
281
                // Get the class options
282
                $aClassOptions = [];
283
                if(array_key_exists($sClassName, $aNewOptions))
284
                {
285
                    $aClassOptions = $aNewOptions[$sClassName];
286
                }
287
288
                $this->_registerClass($xFile, $sDirectory, $sNamespace,
289
                    $aDirOptions['separator'], $aDirOptions['protected'], $aClassOptions);
290
            }
291
        }
292
    }
293
294
    /**
295
     * Generate a hash for the registered callable objects
296
     *
297
     * @return string
298
     */
299
    public function generateHash()
300
    {
301
        if(count($this->aCallableDirs) == 0)
302
        {
303
            return '';
304
        }
305
        $sHash = '';
306
        foreach($this->aCallableDirs as $sDirectory => $aDirOptions)
307
        {
308
            $sHash .= $sDirectory . $aDirOptions['namespace'] . $aDirOptions['separator'];
309
        }
310
        return md5($sHash);
311
    }
312
313
    /**
314
     * Generate client side javascript code for the registered callable objects
315
     *
316
     * @return string
317
     */
318
    public function getScript()
319
    {
320
        $code = '';
321
        return $code;
322
    }
323
324
    /**
325
     * Check if this plugin can process the incoming Jaxon request
326
     *
327
     * @return boolean
328
     */
329
    public function canProcessRequest()
330
    {
331
        // This plugin never processes any request
332
        return false;
333
    }
334
335
    /**
336
     * Process the incoming Jaxon request
337
     *
338
     * @return boolean
339
     */
340
    public function processRequest()
341
    {
342
        return false;
343
    }
344
}
345