1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Manager.php - Jaxon plugin manager |
5
|
|
|
* |
6
|
|
|
* Register Jaxon plugins, generate corresponding code, handle request |
7
|
|
|
* and redirect them to the right plugin. |
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\Plugin; |
23
|
|
|
|
24
|
|
|
use Jaxon\Jaxon; |
25
|
|
|
use Jaxon\Plugin\Package; |
26
|
|
|
use Jaxon\Config\Config; |
27
|
|
|
|
28
|
|
|
use Closure; |
29
|
|
|
|
30
|
|
|
class Manager |
31
|
|
|
{ |
32
|
|
|
use \Jaxon\Utils\Traits\Manager; |
33
|
|
|
use \Jaxon\Utils\Traits\Config; |
34
|
|
|
use \Jaxon\Utils\Traits\Cache; |
35
|
|
|
use \Jaxon\Utils\Traits\Event; |
36
|
|
|
use \Jaxon\Utils\Traits\Translator; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* All plugins, indexed by priority |
40
|
|
|
* |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
private $aPlugins = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Request plugins, indexed by name |
47
|
|
|
* |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
private $aRequestPlugins = []; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Response plugins, indexed by name |
54
|
|
|
* |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
private $aResponsePlugins = []; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* An array of package names |
61
|
|
|
* |
62
|
|
|
* @var array |
63
|
|
|
*/ |
64
|
|
|
private $aPackages = []; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* Get the request plugins |
68
|
|
|
* |
69
|
|
|
* @return array |
70
|
|
|
*/ |
71
|
|
|
public function getRequestPlugins() |
72
|
|
|
{ |
73
|
|
|
return $this->aRequestPlugins; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Get the response plugins |
78
|
|
|
* |
79
|
|
|
* @return array |
80
|
|
|
*/ |
81
|
|
|
public function getResponsePlugins() |
82
|
|
|
{ |
83
|
|
|
return $this->aResponsePlugins; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Get the package plugins |
88
|
|
|
* |
89
|
|
|
* @return array |
90
|
|
|
*/ |
91
|
|
|
public function getPackages() |
92
|
|
|
{ |
93
|
|
|
return $this->aPackages; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Inserts an entry into an array given the specified priority number |
98
|
|
|
* |
99
|
|
|
* If a plugin already exists with the given priority, the priority is automatically incremented until a free spot is found. |
100
|
|
|
* The plugin is then inserted into the empty spot in the array. |
101
|
|
|
* |
102
|
|
|
* @param Plugin $xPlugin An instance of a plugin |
103
|
|
|
* @param integer $nPriority The desired priority, used to order the plugins |
104
|
|
|
* |
105
|
|
|
* @return void |
106
|
|
|
*/ |
107
|
|
|
private function setPluginPriority(Plugin $xPlugin, $nPriority) |
108
|
|
|
{ |
109
|
|
|
while (isset($this->aPlugins[$nPriority])) |
110
|
|
|
{ |
111
|
|
|
$nPriority++; |
112
|
|
|
} |
113
|
|
|
$this->aPlugins[$nPriority] = $xPlugin; |
114
|
|
|
// Sort the array by ascending keys |
115
|
|
|
ksort($this->aPlugins); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* Register a plugin |
120
|
|
|
* |
121
|
|
|
* Below is a table for priorities and their description: |
122
|
|
|
* - 0 thru 999: Plugins that are part of or extensions to the jaxon core |
123
|
|
|
* - 1000 thru 8999: User created plugins, typically, these plugins don't care about order |
124
|
|
|
* - 9000 thru 9999: Plugins that generally need to be last or near the end of the plugin list |
125
|
|
|
* |
126
|
|
|
* @param Plugin $xPlugin An instance of a plugin |
127
|
|
|
* @param integer $nPriority The plugin priority, used to order the plugins |
128
|
|
|
* |
129
|
|
|
* @return void |
130
|
|
|
*/ |
131
|
|
|
public function registerPlugin(Plugin $xPlugin, $nPriority = 1000) |
132
|
|
|
{ |
133
|
|
|
$bIsAlert = ($xPlugin instanceof Dialogs\Interfaces\Alert); |
|
|
|
|
134
|
|
|
$bIsConfirm = ($xPlugin instanceof Dialogs\Interfaces\Confirm); |
|
|
|
|
135
|
|
|
if($xPlugin instanceof Request) |
136
|
|
|
{ |
137
|
|
|
// The name of a request plugin is used as key in the plugin table |
138
|
|
|
$this->aRequestPlugins[$xPlugin->getName()] = $xPlugin; |
139
|
|
|
} |
140
|
|
|
elseif($xPlugin instanceof Response) |
141
|
|
|
{ |
142
|
|
|
// The name of a response plugin is used as key in the plugin table |
143
|
|
|
$this->aResponsePlugins[$xPlugin->getName()] = $xPlugin; |
144
|
|
|
} |
145
|
|
|
elseif(!$bIsConfirm && !$bIsAlert) |
146
|
|
|
{ |
147
|
|
|
throw new \Jaxon\Exception\Error($this->trans('errors.register.invalid', array('name' => get_class($xPlugin)))); |
148
|
|
|
} |
149
|
|
|
// This plugin implements the Alert interface |
150
|
|
|
if($bIsAlert) |
151
|
|
|
{ |
152
|
|
|
$this->setAlert($xPlugin); |
|
|
|
|
153
|
|
|
} |
154
|
|
|
// This plugin implements the Confirm interface |
155
|
|
|
if($bIsConfirm) |
156
|
|
|
{ |
157
|
|
|
$this->setConfirm($xPlugin); |
|
|
|
|
158
|
|
|
} |
159
|
|
|
// Register the plugin as an event listener |
160
|
|
|
if($xPlugin instanceof \Jaxon\Utils\Interfaces\EventListener) |
161
|
|
|
{ |
162
|
|
|
$this->addEventListener($xPlugin); |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$this->setPluginPriority($xPlugin, $nPriority); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Register a package |
170
|
|
|
* |
171
|
|
|
* @param string $sPackageClass The package class name |
172
|
|
|
* @param Closure $xClosure A closure to create package instance |
173
|
|
|
* |
174
|
|
|
* @return void |
175
|
|
|
*/ |
176
|
|
|
public function registerPackage(string $sPackageClass, Closure $xClosure) |
177
|
|
|
{ |
178
|
|
|
$this->aPackages[] = $sPackageClass; |
179
|
|
|
jaxon()->di()->set($sPackageClass, $xClosure); |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Register a function, event or callable object |
184
|
|
|
* |
185
|
|
|
* Call the request plugin with the $sType defined as name. |
186
|
|
|
* |
187
|
|
|
* @param string $sType The type of request handler being registered |
188
|
|
|
* @param string $sCallable The callable entity being registered |
189
|
|
|
* @param array|string $aOptions The associated options |
190
|
|
|
* |
191
|
|
|
* @return mixed |
192
|
|
|
*/ |
193
|
|
|
public function register($sType, $sCallable, $aOptions = []) |
194
|
|
|
{ |
195
|
|
|
if(!key_exists($sType, $this->aRequestPlugins)) |
196
|
|
|
{ |
197
|
|
|
throw new \Jaxon\Exception\Error($this->trans('errors.register.plugin', ['name' => $sType])); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
$xPlugin = $this->aRequestPlugins[$sType]; |
201
|
|
|
return $xPlugin->register($sType, $sCallable, $aOptions); |
202
|
|
|
// foreach($this->aRequestPlugins as $xPlugin) |
203
|
|
|
// { |
204
|
|
|
// if($mResult instanceof \Jaxon\Request\Request || is_array($mResult) || $mResult === true) |
205
|
|
|
// { |
206
|
|
|
// return $mResult; |
207
|
|
|
// } |
208
|
|
|
// } |
209
|
|
|
// throw new \Jaxon\Exception\Error($this->trans('errors.register.method', ['args' => print_r($aArgs, true)])); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
/** |
213
|
|
|
* Read and set Jaxon options from a JSON config file |
214
|
|
|
* |
215
|
|
|
* @param Config $xAppConfig The config options |
216
|
|
|
* |
217
|
|
|
* @return void |
218
|
|
|
*/ |
219
|
|
|
public function registerFromConfig($xAppConfig) |
220
|
|
|
{ |
221
|
|
|
// Register user functions |
222
|
|
|
$aFunctionsConfig = $xAppConfig->getOption('functions', []); |
223
|
|
|
foreach($aFunctionsConfig as $xKey => $xValue) |
224
|
|
|
{ |
225
|
|
|
if(is_integer($xKey) && is_string($xValue)) |
226
|
|
|
{ |
227
|
|
|
$sFunction = $xValue; |
228
|
|
|
// Register a function without options |
229
|
|
|
$this->register(Jaxon::USER_FUNCTION, $sFunction); |
230
|
|
|
} |
231
|
|
View Code Duplication |
elseif(is_string($xKey) && is_array($xValue)) |
|
|
|
|
232
|
|
|
{ |
233
|
|
|
$sFunction = $xKey; |
234
|
|
|
$aOptions = $xValue; |
235
|
|
|
// Register a function with options |
236
|
|
|
$this->register(Jaxon::USER_FUNCTION, $sFunction, $aOptions); |
237
|
|
|
} |
238
|
|
|
else |
239
|
|
|
{ |
240
|
|
|
continue; |
241
|
|
|
// Todo: throw an exception |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
// Register classes and directories |
246
|
|
|
$aClassesConfig = $xAppConfig->getOption('classes', []); |
247
|
|
|
foreach($aClassesConfig as $xKey => $xValue) |
248
|
|
|
{ |
249
|
|
|
if(is_integer($xKey) && is_string($xValue)) |
250
|
|
|
{ |
251
|
|
|
$sClass = $xValue; |
252
|
|
|
// Register a class without options |
253
|
|
|
$this->register(Jaxon::CALLABLE_CLASS, $sClass); |
254
|
|
|
} |
255
|
|
View Code Duplication |
elseif(is_string($xKey) && is_array($xValue)) |
|
|
|
|
256
|
|
|
{ |
257
|
|
|
$sClass = $xKey; |
258
|
|
|
$aOptions = $xValue; |
259
|
|
|
// Register a class with options |
260
|
|
|
$this->register(Jaxon::CALLABLE_CLASS, $sClass, $aOptions); |
261
|
|
|
} |
262
|
|
|
elseif(is_integer($xKey) && is_array($xValue)) |
263
|
|
|
{ |
264
|
|
|
// The directory path is required |
265
|
|
|
if(!key_exists('directory', $xValue)) |
266
|
|
|
{ |
267
|
|
|
continue; |
268
|
|
|
// Todo: throw an exception |
269
|
|
|
} |
270
|
|
|
// Registering a directory |
271
|
|
|
$sDirectory = $xValue['directory']; |
272
|
|
|
$aOptions = []; |
273
|
|
|
if(key_exists('options', $xValue) && |
274
|
|
|
is_array($xValue['options']) || is_string($xValue['options'])) |
275
|
|
|
{ |
276
|
|
|
$aOptions = $xValue['options']; |
277
|
|
|
} |
278
|
|
|
// Setup directory options |
279
|
|
|
if(key_exists('namespace', $xValue)) |
280
|
|
|
{ |
281
|
|
|
$aOptions['namespace'] = $xValue['namespace']; |
282
|
|
|
} |
283
|
|
|
if(key_exists('separator', $xValue)) |
284
|
|
|
{ |
285
|
|
|
$aOptions['separator'] = $xValue['separator']; |
286
|
|
|
} |
287
|
|
|
// Register a class without options |
288
|
|
|
$this->register(Jaxon::CALLABLE_DIR, $sDirectory, $aOptions); |
289
|
|
|
} |
290
|
|
|
else |
291
|
|
|
{ |
292
|
|
|
continue; |
293
|
|
|
// Todo: throw an exception |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Find the specified response plugin by name and return a reference to it if one exists |
301
|
|
|
* |
302
|
|
|
* @param string $sName The name of the plugin |
303
|
|
|
* |
304
|
|
|
* @return \Jaxon\Plugin\Response |
305
|
|
|
*/ |
306
|
|
|
public function getResponsePlugin($sName) |
307
|
|
|
{ |
308
|
|
|
if(array_key_exists($sName, $this->aResponsePlugins)) |
309
|
|
|
{ |
310
|
|
|
return $this->aResponsePlugins[$sName]; |
311
|
|
|
} |
312
|
|
|
return null; |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Find the specified request plugin by name and return a reference to it if one exists |
317
|
|
|
* |
318
|
|
|
* @param string $sName The name of the plugin |
319
|
|
|
* |
320
|
|
|
* @return \Jaxon\Plugin\Request |
321
|
|
|
*/ |
322
|
|
|
public function getRequestPlugin($sName) |
323
|
|
|
{ |
324
|
|
|
if(array_key_exists($sName, $this->aRequestPlugins)) |
325
|
|
|
{ |
326
|
|
|
return $this->aRequestPlugins[$sName]; |
327
|
|
|
} |
328
|
|
|
return null; |
329
|
|
|
} |
330
|
|
|
} |
331
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.