1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* Joomla! component. |
4
|
|
|
* |
5
|
|
|
* @copyright Copyright (C) 2017 Roberto Segura López, Inc. All rights reserved. |
6
|
|
|
* @license See COPYING.txt |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace Phproberto\Joomla\Component; |
10
|
|
|
|
11
|
|
|
use Joomla\Registry\Registry; |
12
|
|
|
use Phproberto\Joomla\Client\Client; |
13
|
|
|
use Phproberto\Joomla\Client\Site; |
14
|
|
|
use Phproberto\Joomla\Client\Administrator; |
15
|
|
|
use Phproberto\Joomla\Client\ClientInterface; |
16
|
|
|
use Phproberto\Joomla\Traits; |
17
|
|
|
|
18
|
1 |
|
defined('JPATH_PLATFORM') || die; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Table finder. |
22
|
|
|
* |
23
|
|
|
* @since __DEPLOY_VERSION__ |
24
|
|
|
*/ |
25
|
|
|
class Component |
26
|
|
|
{ |
27
|
|
|
use Traits\HasExtension, Traits\HasParams; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Component option. Example: com_content |
31
|
|
|
* |
32
|
|
|
* @var string |
33
|
|
|
*/ |
34
|
|
|
protected $option; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Component prefix for classes, etc. |
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $prefix; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Active client. |
45
|
|
|
* |
46
|
|
|
* @var ClientInterface |
47
|
|
|
*/ |
48
|
|
|
protected $client; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Cached instances. |
52
|
|
|
* |
53
|
|
|
* @var array |
54
|
|
|
*/ |
55
|
|
|
protected static $instances = array(); |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Constructor |
59
|
|
|
* |
60
|
|
|
* @param string $option Component option |
61
|
|
|
* @param ClientInterface $client Client |
62
|
|
|
* |
63
|
|
|
* @throws \InvalidArgumentException |
64
|
|
|
*/ |
65
|
24 |
|
public function __construct($option, ClientInterface $client = null) |
66
|
|
|
{ |
67
|
24 |
|
$option = trim(strtolower($option)); |
68
|
|
|
|
69
|
24 |
|
if (empty($option)) |
70
|
|
|
{ |
71
|
1 |
|
throw new \InvalidArgumentException(__CLASS__ . ': Empty component option.'); |
72
|
|
|
} |
73
|
|
|
|
74
|
23 |
|
$this->client = $client ?: Client::active(); |
75
|
23 |
|
$this->option = $option; |
76
|
23 |
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Switch to admin client. |
80
|
|
|
* |
81
|
|
|
* @return self |
82
|
|
|
*/ |
83
|
5 |
|
public function admin() |
84
|
|
|
{ |
85
|
5 |
|
$this->client = new Administrator; |
86
|
|
|
|
87
|
5 |
|
return $this; |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Clear a singleton instance. |
92
|
|
|
* |
93
|
|
|
* @param string $option Component option |
94
|
|
|
* |
95
|
|
|
* @return void |
96
|
|
|
*/ |
97
|
4 |
|
public static function clear($option) |
98
|
|
|
{ |
99
|
4 |
|
unset(static::$instances[get_called_class()][$option]); |
100
|
4 |
|
} |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Clears all the statically cached instances. |
104
|
|
|
* |
105
|
|
|
* @return void |
106
|
|
|
*/ |
107
|
24 |
|
public static function clearAll() |
108
|
|
|
{ |
109
|
24 |
|
static::$instances = array(); |
110
|
24 |
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Get a singleton instance. |
114
|
|
|
* |
115
|
|
|
* @param string $option Component option |
116
|
|
|
* |
117
|
|
|
* @return $this |
118
|
|
|
*/ |
119
|
23 |
|
public static function get($option) |
120
|
|
|
{ |
121
|
23 |
|
$option = trim(strtolower($option)); |
122
|
|
|
|
123
|
23 |
|
$class = get_called_class(); |
124
|
|
|
|
125
|
23 |
|
if (empty(static::$instances[$class][$option])) |
126
|
|
|
{ |
127
|
23 |
|
static::$instances[$class][$option] = new static($option); |
128
|
|
|
} |
129
|
|
|
|
130
|
23 |
|
return static::$instances[$class][$option]; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Get the active component. |
135
|
|
|
* |
136
|
|
|
* @return $this |
137
|
|
|
* |
138
|
|
|
* @throws \InvalidArgumentException |
139
|
|
|
*/ |
140
|
1 |
|
public static function getActive() |
141
|
|
|
{ |
142
|
1 |
|
return static::get(static::getActiveOption()); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Get the active component option. Isolated for testing purposes. |
147
|
|
|
* |
148
|
|
|
* @return string |
149
|
|
|
* |
150
|
|
|
* @codeCoverageIgnore |
151
|
|
|
*/ |
152
|
|
|
protected static function getActiveOption() |
153
|
|
|
{ |
154
|
|
|
return \JApplicationHelper::getComponentName(); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Get the active client. |
159
|
|
|
* |
160
|
|
|
* @return ClientInterface |
161
|
|
|
*/ |
162
|
5 |
|
public function getClient() |
163
|
|
|
{ |
164
|
5 |
|
return $this->client; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* Ensure that we retrieve a non-statically-cached instance. |
169
|
|
|
* |
170
|
|
|
* @param string $option Component option |
171
|
|
|
* |
172
|
|
|
* @return $this |
173
|
|
|
*/ |
174
|
2 |
|
public static function getFresh($option) |
175
|
|
|
{ |
176
|
2 |
|
static::clear($option); |
177
|
|
|
|
178
|
2 |
|
return static::get($option); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Get a model of this component. |
183
|
|
|
* |
184
|
|
|
* @param string $name Name of the model. |
185
|
|
|
* @param array $config Optional array of configuration for the model |
186
|
|
|
* |
187
|
|
|
* @return \JModelLegacy |
188
|
|
|
* |
189
|
|
|
* @throws \InvalidArgumentException If not found |
190
|
|
|
*/ |
191
|
5 |
|
public function model($name, array $config = array('ignore_request' => true)) |
192
|
|
|
{ |
193
|
5 |
|
$prefix = $this->getPrefix() . 'Model'; |
194
|
|
|
|
195
|
5 |
|
\JModelLegacy::addIncludePath($this->getModelsFolder(), $prefix); |
196
|
|
|
|
197
|
|
|
try |
198
|
|
|
{ |
199
|
4 |
|
\JTable::addIncludePath($this->getTablesFolder()); |
200
|
|
|
} |
201
|
4 |
|
catch (\Exception $e) |
202
|
|
|
{ |
203
|
|
|
// There are models with no associated tables |
204
|
|
|
} |
205
|
|
|
|
206
|
4 |
|
$model = \JModelLegacy::getInstance($name, $prefix, $config); |
|
|
|
|
207
|
|
|
|
208
|
4 |
|
if (!$model instanceof \JModel && !$model instanceof \JModelLegacy) |
|
|
|
|
209
|
|
|
{ |
210
|
1 |
|
throw new \InvalidArgumentException( |
211
|
1 |
|
sprintf("Cannot find the model `%s` in `%s` component's %s folder.", $name, $this->option, $this->client->getName()) |
212
|
|
|
); |
213
|
|
|
} |
214
|
|
|
|
215
|
3 |
|
return $model; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Get the folder where the models are. |
220
|
|
|
* |
221
|
|
|
* @return string |
222
|
|
|
* |
223
|
|
|
* @throws \RuntimeException If not found |
224
|
|
|
*/ |
225
|
5 |
View Code Duplication |
protected function getModelsFolder() |
|
|
|
|
226
|
|
|
{ |
227
|
5 |
|
$folder = $this->client->getFolder() . '/components/' . $this->option . '/models'; |
228
|
|
|
|
229
|
5 |
|
if (is_dir($folder)) |
230
|
|
|
{ |
231
|
4 |
|
return $folder; |
232
|
|
|
} |
233
|
|
|
|
234
|
1 |
|
throw new \RuntimeException( |
235
|
1 |
|
sprintf("Cannot find the models folder for `%s` component in `%s` folder.", $this->option, $this->client->getName()) |
236
|
|
|
); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* Get the folder where the tables are. |
241
|
|
|
* |
242
|
|
|
* @return string |
243
|
|
|
* |
244
|
|
|
* @throws \RuntimeException If not found |
245
|
|
|
*/ |
246
|
7 |
View Code Duplication |
protected function getTablesFolder() |
|
|
|
|
247
|
|
|
{ |
248
|
7 |
|
$folder = $this->client->getFolder() . '/components/' . $this->option . '/tables'; |
249
|
|
|
|
250
|
7 |
|
if (is_dir($folder)) |
251
|
|
|
{ |
252
|
3 |
|
return $folder; |
253
|
|
|
} |
254
|
|
|
|
255
|
4 |
|
throw new \RuntimeException( |
256
|
4 |
|
sprintf("Cannot find the tables folder for `%s` component in `%s` folder.", $this->option, $this->client->getName()) |
257
|
|
|
); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Get the component prefix. |
262
|
|
|
* |
263
|
|
|
* @return string |
264
|
|
|
*/ |
265
|
11 |
|
public function getPrefix() |
266
|
|
|
{ |
267
|
11 |
|
if (null === $this->prefix) |
268
|
|
|
{ |
269
|
11 |
|
$parts = array_map( |
270
|
11 |
|
function ($part) |
271
|
|
|
{ |
272
|
11 |
|
return ucfirst(strtolower($part)); |
273
|
11 |
|
}, |
274
|
11 |
|
explode('_', substr($this->option, 4)) |
275
|
|
|
); |
276
|
|
|
|
277
|
11 |
|
$this->prefix = implode('_', $parts); |
278
|
|
|
} |
279
|
|
|
|
280
|
11 |
|
return $this->prefix; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Get a component table. |
285
|
|
|
* |
286
|
|
|
* @param string $name Name of the table to load. Example: Article |
287
|
|
|
* @param array $config Optional array of configuration for the table |
288
|
|
|
* |
289
|
|
|
* @return \JTable |
290
|
|
|
* |
291
|
|
|
* @throws \InvalidArgumentException If not found |
292
|
|
|
*/ |
293
|
3 |
|
public function table($name, array $config = array()) |
294
|
|
|
{ |
295
|
3 |
|
$prefix = $this->getPrefix() . 'Table'; |
296
|
|
|
|
297
|
3 |
|
\JTable::addIncludePath($this->getTablesFolder()); |
298
|
|
|
|
299
|
3 |
|
$table = \JTable::getInstance($name, $prefix, $config); |
300
|
|
|
|
301
|
3 |
|
if (!$table instanceof \JTable) |
|
|
|
|
302
|
|
|
{ |
303
|
1 |
|
throw new \InvalidArgumentException( |
304
|
1 |
|
sprintf("Cannot find the table `%s` in `%s` component's `%s` folder.", $prefix . $name, $this->option, $this->client->getName()) |
305
|
|
|
); |
306
|
|
|
} |
307
|
|
|
|
308
|
2 |
|
return $table; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Load extension from DB. |
313
|
|
|
* |
314
|
|
|
* @return \stdClass |
315
|
|
|
*/ |
316
|
3 |
|
protected function loadExtension() |
317
|
|
|
{ |
318
|
3 |
|
$db = \JFactory::getDbo(); |
319
|
3 |
|
$query = $db->getQuery(true) |
320
|
3 |
|
->select('*') |
321
|
3 |
|
->from('#__extensions') |
322
|
3 |
|
->where('type = ' . $db->quote('component')) |
323
|
3 |
|
->where('element = ' . $db->q($this->option)); |
324
|
|
|
|
325
|
3 |
|
$db->setQuery($query); |
326
|
|
|
|
327
|
3 |
|
return $db->loadObject() ?: new \stdClass; |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
/** |
331
|
|
|
* Load parameters from database. |
332
|
|
|
* |
333
|
|
|
* @return Registry |
334
|
|
|
*/ |
335
|
2 |
|
protected function loadParams() |
336
|
|
|
{ |
337
|
2 |
|
return new Registry($this->getExtensionProperty('params', array())); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Save parameters to database. |
342
|
|
|
* |
343
|
|
|
* @return boolean |
344
|
|
|
*/ |
345
|
1 |
|
public function saveParams() |
346
|
|
|
{ |
347
|
1 |
|
$db = \JFactory::getDbo(); |
348
|
|
|
|
349
|
1 |
|
$query = $db->getQuery(true) |
350
|
1 |
|
->update('#__extensions') |
351
|
1 |
|
->set('params = ' . $db->q($this->getParams()->toString())) |
352
|
1 |
|
->where('type = ' . $db->quote('component')) |
353
|
1 |
|
->where('element = ' . $db->q($this->option)); |
354
|
|
|
|
355
|
1 |
|
$db->setQuery($query); |
356
|
|
|
|
357
|
1 |
|
return $db->execute() ? true : false; |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Switch to site client. |
362
|
|
|
* |
363
|
|
|
* @return self |
364
|
|
|
*/ |
365
|
3 |
|
public function site() |
366
|
|
|
{ |
367
|
3 |
|
$this->client = new Site; |
368
|
|
|
|
369
|
3 |
|
return $this; |
370
|
|
|
} |
371
|
|
|
} |
372
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.