|
1
|
|
|
<?php |
|
2
|
|
|
namespace samson\core; |
|
3
|
|
|
|
|
4
|
|
|
// TODO: Разобраться почему с вызовом m()->render() во вьюхе, и почему не передаются параметры |
|
5
|
|
|
use samsonframework\core\ResourcesInterface; |
|
6
|
|
|
use samsonframework\core\SystemInterface; |
|
7
|
|
|
|
|
8
|
|
|
/** |
|
9
|
|
|
* Модуль системы |
|
10
|
|
|
* |
|
11
|
|
|
* @author Vitaly Iegorov <[email protected]> |
|
12
|
|
|
* @version 1.0 |
|
13
|
|
|
*/ |
|
14
|
|
|
class Module implements iModule, \ArrayAccess |
|
15
|
|
|
{ |
|
16
|
|
|
/** Static module instances collection */ |
|
17
|
|
|
public static $instances = array(); |
|
18
|
|
|
|
|
19
|
|
|
/** Uniquer identifier to check pointers */ |
|
20
|
|
|
public $uid; |
|
21
|
|
|
|
|
22
|
|
|
/** @var ResourcesInterface Pointer to module resource map */ |
|
23
|
|
|
public $resourceMap; |
|
24
|
|
|
|
|
25
|
|
|
public $composerParameters = array(); |
|
26
|
|
|
|
|
27
|
|
|
/** Collection for cachable callable controllers of module */ |
|
28
|
|
|
protected $cacheControllers = array(); |
|
29
|
|
|
|
|
30
|
|
|
/** Collection for callable controllers of module */ |
|
31
|
|
|
protected $controllers = array(); |
|
32
|
|
|
|
|
33
|
|
|
/** Module views collection */ |
|
34
|
|
|
protected $views = array(); |
|
35
|
|
|
|
|
36
|
|
|
/** Module location */ |
|
37
|
|
|
protected $path = ''; |
|
38
|
|
|
|
|
39
|
|
|
/** Unique module identifier */ |
|
40
|
|
|
protected $id = ''; |
|
41
|
|
|
|
|
42
|
|
|
/** Default module version */ |
|
43
|
|
|
protected $version = '0.0.1'; |
|
44
|
|
|
|
|
45
|
|
|
/** Path to view for rendering */ |
|
46
|
|
|
protected $view_path = self::VD_POINTER_DEF; |
|
47
|
|
|
|
|
48
|
|
|
/** Pointer to view data entry */ |
|
49
|
|
|
protected $data = array(self::VD_POINTER_DEF => array(self::VD_HTML => '')); |
|
50
|
|
|
|
|
51
|
|
|
/** Collection of data for view rendering, filled with default pointer */ |
|
52
|
|
|
protected $view_data = array(self::VD_POINTER_DEF => array(self::VD_HTML => '')); |
|
53
|
|
|
|
|
54
|
|
|
/** Name of current view context entry */ |
|
55
|
|
|
protected $view_context = self::VD_POINTER_DEF; |
|
56
|
|
|
|
|
57
|
|
|
/** Unique module cache path in local web-application */ |
|
58
|
|
|
protected $cache_path; |
|
59
|
|
|
|
|
60
|
|
|
/** @var SystemInterface Instance for interaction with framework */ |
|
61
|
|
|
protected $system; |
|
62
|
|
|
|
|
63
|
|
|
/** |
|
64
|
|
|
* Perform module view context switching |
|
65
|
|
|
* @param string $view_path New view context name |
|
66
|
7 |
|
*/ |
|
67
|
|
|
protected function viewContext($view_path) |
|
68
|
|
|
{ |
|
69
|
7 |
|
// Pointer to NEW view data context |
|
70
|
|
|
$new = &$this->view_data[$view_path]; |
|
71
|
|
|
|
|
72
|
7 |
|
// Pointer to OLD view data context |
|
73
|
|
|
$old = &$this->view_data[$this->view_context]; |
|
74
|
7 |
|
|
|
75
|
|
|
// If we are trying to switch to NEW view context |
|
76
|
|
|
if ($this->view_context !== $view_path) { |
|
77
|
7 |
|
//elapsed( $this->id.' - Switching view context from '.$this->view_context.' to '.$view_path ); |
|
78
|
|
|
|
|
79
|
|
|
// Create new entry in view data collection if it does not exists |
|
80
|
7 |
|
if (!isset($this->view_data[$view_path])) { |
|
81
|
|
|
// Create new view data record |
|
82
|
|
|
$new = array(); |
|
83
|
7 |
|
|
|
84
|
|
|
// If current view data context has view data |
|
85
|
|
|
if (isset($old)) { |
|
86
|
7 |
|
//elapsed($old); |
|
87
|
|
|
//elapsed( $this->id.' - Copying previous view context view data '.$this->view_context.' to new view context '.$view_path.'('.sizeof($old).')'); |
|
88
|
|
|
|
|
89
|
7 |
|
// Copy default view context view data to new view context |
|
90
|
|
|
$new = array_merge($new, $old); |
|
91
|
|
|
} |
|
92
|
7 |
|
|
|
93
|
|
|
// Clear plain HTML for new view context |
|
94
|
|
|
$new[self::VD_HTML] = ''; |
|
95
|
7 |
|
} |
|
96
|
|
|
|
|
97
|
|
|
// Change view data pointer to appropriate view data entry |
|
98
|
|
|
$this->data = &$new; |
|
99
|
7 |
|
|
|
100
|
|
|
// Save current context name |
|
101
|
|
|
$this->view_context = $view_path; |
|
102
|
7 |
|
} |
|
103
|
|
|
//else elapsed( $this->id.' - NO need to switch view context from '.$this->view_context.' to '.$view_path ); |
|
104
|
|
|
} |
|
105
|
7 |
|
|
|
106
|
|
|
/** Sort array by string length */ |
|
107
|
|
|
protected function sortStrings($a, $b) |
|
108
|
7 |
|
{ |
|
109
|
|
|
return strlen($b) - strlen($a); |
|
110
|
|
|
} |
|
111
|
7 |
|
|
|
112
|
7 |
|
/** |
|
113
|
|
|
* Find view file by its part in module view resources and return full path to it |
|
114
|
7 |
|
* @param string $viewPath Part of path to module view file |
|
115
|
|
|
* @return string Full path to view file |
|
116
|
1 |
|
*/ |
|
117
|
|
|
public function findView($viewPath) |
|
118
|
|
|
{ |
|
119
|
|
|
// Remove file extension for correct array searching |
|
120
|
|
|
$viewPath = str_replace(array('.php', '.vphp'), '', $viewPath); |
|
121
|
|
|
|
|
122
|
|
|
// Try to find passed view_path in resources views collection |
|
123
|
|
|
if (sizeof($view = preg_grep('/' . addcslashes($viewPath, '/\\') . '(\.php|\.vphp)/ui', $this->views))) { |
|
124
|
|
|
// Sort view paths to get the shortest path |
|
125
|
|
|
usort($view, array($this, 'sortStrings')); |
|
126
|
1 |
|
|
|
127
|
|
|
// Set current full view path as last found view |
|
128
|
1 |
|
return end($view); |
|
129
|
|
|
} |
|
130
|
1 |
|
|
|
131
|
|
|
return false; |
|
|
|
|
|
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
1 |
|
|
|
135
|
|
|
/** @see iModule::title() */ |
|
136
|
1 |
|
public function title($title = null) |
|
137
|
|
|
{ |
|
138
|
|
|
return $this->set('title', $title); |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
/** @see iModule::id() */ |
|
142
|
|
|
public function id() |
|
143
|
|
|
{ |
|
144
|
|
|
return $this->id; |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
/** @see iModule::set() */ |
|
148
|
|
|
public function set($field, $value = null) |
|
149
|
|
|
{ |
|
150
|
|
|
$this->__set($field, $value); |
|
151
|
|
|
|
|
152
|
1 |
|
return $this; |
|
153
|
|
|
} |
|
154
|
1 |
|
|
|
155
|
|
|
/** @see iModuleViewable::toView() */ |
|
156
|
1 |
|
public function toView($prefix = null, array $restricted = array()) |
|
157
|
|
|
{ |
|
158
|
|
|
// Get all module data variables |
|
159
|
3 |
|
$view_data = array_merge($this->data, get_object_vars($this)); |
|
160
|
|
|
|
|
161
|
|
|
// Remove plain HTML from view data |
|
162
|
3 |
|
unset($view_data[self::VD_HTML]); |
|
163
|
|
|
|
|
164
|
|
|
return $view_data; |
|
165
|
3 |
|
} |
|
166
|
|
|
|
|
167
|
3 |
|
/** @see iModule::path() */ |
|
168
|
|
|
public function path($value = null) |
|
169
|
|
|
{ |
|
170
|
3 |
|
// Если передан параметр - установим его |
|
171
|
3 |
|
if (func_num_args()) { |
|
172
|
1 |
|
$this->path = isset($value{0}) ? rtrim(normalizepath($value), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : ''; |
|
173
|
|
|
|
|
174
|
|
|
return $this; |
|
175
|
|
|
} // Вернем относительный путь к файлам модуля |
|
176
|
3 |
|
else return $this->path; |
|
|
|
|
|
|
177
|
|
|
} |
|
178
|
|
|
|
|
179
|
|
|
/** @see iModule::html() */ |
|
180
|
|
|
public function html($value = null) |
|
181
|
|
|
{ |
|
182
|
|
|
//elapsed($this->id.' - Setting HTML for '.array_search( $this->data, $this->view_data ).'('.strlen($value).')'); |
|
183
|
|
|
|
|
184
|
|
|
// Если передан параметр то установим его |
|
185
|
3 |
|
if (func_num_args()) { |
|
186
|
|
|
$this->data[self::VD_HTML] = $value; |
|
187
|
|
|
} else { |
|
188
|
3 |
|
return $this->data['html']; |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
3 |
|
return $this; |
|
192
|
|
|
} |
|
193
|
3 |
|
|
|
194
|
|
|
public function view($viewPath) |
|
195
|
|
|
{ |
|
196
|
3 |
|
// Find full path to view file |
|
197
|
|
|
$viewPath = $this->findView($viewPath); |
|
198
|
|
|
|
|
199
|
1 |
|
//elapsed($this->id.' - Changing current view to '.$view_path); |
|
200
|
|
|
|
|
201
|
|
|
// Switch view context to founded module view |
|
202
|
|
|
$this->viewContext($viewPath); |
|
203
|
|
|
|
|
204
|
|
|
// Set current view path |
|
205
|
|
|
$this->view_path = $viewPath; |
|
206
|
4 |
|
|
|
207
|
|
|
// Продолжим цепирование |
|
208
|
|
|
return $this; |
|
209
|
4 |
|
} |
|
210
|
|
|
|
|
211
|
|
|
/** @see iModule::output() */ |
|
212
|
4 |
|
public function output($viewPath = null) |
|
213
|
|
|
{ |
|
214
|
|
|
// If view path not specified - use current correct view path |
|
215
|
4 |
|
if (!isset($viewPath)) { |
|
216
|
|
|
$viewPath = $this->view_path; |
|
217
|
|
|
} elseif (isset($viewPath{0})) { // Direct rendering of specific view, not default view data entry |
|
218
|
|
|
elapsed('Outputting to a view is deprecated, split your rendering chain into ->view(..)->output()'); |
|
219
|
3 |
|
$viewPath = $this->findView($viewPath); |
|
220
|
|
|
} |
|
221
|
3 |
|
|
|
222
|
|
|
//elapsed('['.$this->id.'] Rendering view context: ['.$viewPath.'] with ['.$renderer->id.']'); |
|
223
|
|
|
|
|
224
|
3 |
|
// Switch view context to new module view |
|
225
|
|
|
$this->viewContext($viewPath); |
|
226
|
|
|
|
|
227
|
|
|
//elapsed($this->id.' - Outputing '.$view_path.'-'.sizeof($this->data)); |
|
228
|
|
|
//elapsed(array_keys($this->view_data)); |
|
229
|
3 |
|
|
|
230
|
3 |
|
// Get current view context plain HTML |
|
231
|
|
|
$out = $this->data[self::VD_HTML]; |
|
232
|
|
|
|
|
233
|
3 |
|
// If view path specified |
|
234
|
3 |
|
if (isset($viewPath{0})) { |
|
235
|
|
|
// Временно изменим текущий модуль системы |
|
236
|
|
|
$old = s()->active($this); |
|
237
|
3 |
|
|
|
238
|
|
|
// Прорисуем представление модуля |
|
239
|
|
|
$out .= s()->render($this->path . $viewPath, $this->data); |
|
240
|
3 |
|
|
|
241
|
3 |
|
// Вернем на место текущий модуль системы |
|
242
|
|
|
s()->active($old); |
|
243
|
4 |
|
} elseif (!isset($out{0})) { // No plain HTML view data is set also |
|
244
|
|
|
return e('Cannot render view for module ## - No view path or data has been set', E_SAMSON_CORE_ERROR, $this->id); |
|
245
|
|
|
} |
|
246
|
1 |
|
|
|
247
|
|
|
// Clear currently outputted view context from VCS |
|
248
|
|
|
unset($this->view_data[$viewPath]); |
|
249
|
1 |
|
|
|
250
|
|
|
// Get last element from VCS |
|
251
|
|
|
end($this->view_data); |
|
252
|
1 |
|
|
|
253
|
|
|
// Get last element from VCS name |
|
254
|
|
|
$this->view_context = key($this->view_data); |
|
255
|
|
|
|
|
256
|
|
|
// Set internal view data pointer to last VCS entry |
|
257
|
1 |
|
$this->data = &$this->view_data[$this->view_context]; |
|
258
|
1 |
|
|
|
259
|
1 |
|
// Return view path to previous state |
|
260
|
|
|
$this->view_path = $this->view_context; |
|
261
|
|
|
|
|
262
|
1 |
|
// Вернем результат прорисовки |
|
263
|
|
|
return $out; |
|
264
|
|
|
} |
|
265
|
1 |
|
|
|
266
|
|
|
/** @see iModule::render() */ |
|
267
|
1 |
|
public function render($controller = null) |
|
268
|
|
|
{ |
|
269
|
1 |
|
//trace($this->id.'-'.$controller); |
|
270
|
|
|
// Временно изменим текущий модуль системы |
|
271
|
1 |
|
$old = &$this->system->active($this); |
|
272
|
1 |
|
|
|
273
|
1 |
|
// Если если передан контроллер модуля для выполнения перед его прорисовкой - выполним его |
|
274
|
|
|
if (isset($controller)) { |
|
275
|
1 |
|
// Выполним действие текущего модуля |
|
276
|
|
|
$this->action($controller == '' ? null : $controller); |
|
277
|
|
|
} |
|
278
|
1 |
|
|
|
279
|
|
|
//elapsed( $this->id.' - Rendering '.$this->view_path ); |
|
280
|
|
|
|
|
281
|
1 |
|
// Прорисуем представление и выведем его в текущий поток вывода |
|
282
|
1 |
|
echo $this->output(); |
|
283
|
|
|
|
|
284
|
|
|
// Ввостановим предыдущий текущий модуль контролера |
|
285
|
3 |
|
$this->system->active($old); |
|
286
|
|
|
} |
|
287
|
|
|
|
|
288
|
3 |
|
/** @see iModule::action() */ |
|
289
|
|
|
public function action($methodName = null) |
|
290
|
|
|
{ |
|
291
|
|
|
//trace( array_keys($this->controllers), true ); |
|
292
|
|
|
|
|
293
|
3 |
|
// Get parameters from URL |
|
294
|
|
|
$parameters = url()->parameters; |
|
295
|
|
|
|
|
296
|
|
|
// NEW ASYNC EVENT CHAIN LOGIC |
|
297
|
|
|
// If this is AJAX request - try to call async handlers |
|
298
|
|
|
// Added support for SamsonJS special header |
|
299
|
3 |
|
if ((isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] == '*/*') || isset($_SERVER['HTTP_SJSASYNC']) || isset($_POST['SJSASYNC'])) { |
|
300
|
|
|
// Copy parameters |
|
301
|
|
|
$arguments = $parameters; |
|
302
|
3 |
|
array_unshift($arguments, url()->method); |
|
303
|
|
|
|
|
304
|
2 |
|
// Response |
|
305
|
|
|
$event_result = array(); |
|
306
|
|
|
|
|
307
|
2 |
|
// Iterate supported methods |
|
308
|
|
|
for ($idx = 0; $idx < sizeof($arguments); $idx++) { |
|
|
|
|
|
|
309
|
|
|
$controller_name = self::ASYNC_PREFIX . $arguments[$idx]; |
|
310
|
2 |
|
|
|
311
|
2 |
|
// Build async method handler name and try to find method in arguments list |
|
312
|
|
|
$callback = &$this->controllers[$controller_name]; |
|
313
|
|
|
|
|
314
|
3 |
|
// If async controller handler exists |
|
315
|
|
|
if (isset($callback)) { |
|
316
|
|
|
// Get function arguments without function name |
|
317
|
3 |
|
$f_args = array_slice($arguments, $idx + 1); |
|
318
|
|
|
|
|
319
|
|
|
// Remove used cells from array |
|
320
|
3 |
|
$arguments = array_slice($arguments, $idx + 1); |
|
321
|
|
|
|
|
322
|
|
|
// Decrease index as we change arguments size |
|
323
|
3 |
|
$idx--; |
|
324
|
|
|
|
|
325
|
|
|
// Perform event and collect event result data |
|
326
|
3 |
|
$_event_result = call_user_func_array($callback, $f_args); |
|
327
|
|
|
|
|
328
|
|
|
// Anyway convert event result to array |
|
329
|
3 |
|
if (!is_array($_event_result)) $_event_result = array($_event_result); |
|
|
|
|
|
|
330
|
|
|
|
|
331
|
|
|
// If event successfully completed |
|
332
|
|
|
if (!isset($_event_result['status']) || !$_event_result['status']) { |
|
333
|
1 |
|
// Handle event chain fail |
|
334
|
|
|
$_event_result['message'] = "\n" . 'Event failed: ' . $controller_name; |
|
335
|
|
|
|
|
336
|
|
|
// Add event result array to results collection |
|
337
|
|
|
$event_result = array_merge($event_result, $_event_result); |
|
338
|
1 |
|
|
|
339
|
|
|
// Stop event-chain execution |
|
340
|
1 |
|
break; |
|
341
|
1 |
|
} // Add event result array to results collection |
|
342
|
1 |
|
else $event_result = array_merge($event_result, $_event_result); |
|
|
|
|
|
|
343
|
1 |
|
} |
|
344
|
|
|
} |
|
345
|
1 |
|
|
|
346
|
|
|
// If at least one event has been executed |
|
347
|
|
|
if (sizeof($event_result)) { |
|
348
|
1 |
|
// Set async responce |
|
349
|
|
|
s()->async(true); |
|
350
|
|
|
|
|
351
|
|
|
// Send success status |
|
352
|
|
|
header("HTTP/1.0 200 Ok"); |
|
353
|
|
|
|
|
354
|
1 |
|
// Encode event result as json object |
|
355
|
|
|
echo json_encode($event_result, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); |
|
356
|
1 |
|
|
|
357
|
|
|
return A_SUCCESS; |
|
|
|
|
|
|
358
|
|
|
} |
|
359
|
|
|
} |
|
360
|
1 |
|
|
|
361
|
|
|
// Get HTTP request type |
|
362
|
|
|
$request_type = $_SERVER['REQUEST_METHOD']; |
|
363
|
1 |
|
|
|
364
|
|
|
// Controller by name |
|
365
|
|
|
$naming = $methodName; |
|
366
|
1 |
|
// Controller by server request type |
|
367
|
|
|
$request = !isset($methodName{0}) ? strtolower($request_type == 'GET' ? self::CTR_BASE : self::OBJ_PREFIX . $request_type) : ''; |
|
368
|
|
|
// Universal controller |
|
369
|
1 |
|
$universal = self::CTR_UNI; |
|
370
|
1 |
|
|
|
371
|
|
|
// Controller selection logic chain |
|
372
|
|
|
$controller = (isset($this->controllers[$naming]) ? $this->controllers[$naming] : |
|
373
|
|
|
(isset($this->controllers[$request]) ? $this->controllers[$request] : |
|
374
|
|
|
(isset($this->controllers[$universal]) ? $this->controllers[$universal] : null))); |
|
375
|
1 |
|
|
|
376
|
|
|
// If we selected universal controller - change parameters signature |
|
377
|
1 |
|
if (isset($this->controllers[$universal]) && $controller == $this->controllers[$universal]) { |
|
378
|
1 |
|
// If method is specified - add it to universal controller parameters list |
|
379
|
|
|
if (isset(url()->method{0})) array_unshift($parameters, url()->method); |
|
|
|
|
|
|
380
|
2 |
|
} |
|
381
|
|
|
|
|
382
|
2 |
|
// Retrieve controller action callback name |
|
383
|
|
|
$controllerName = is_array($controller) ? $controller[1] : $controller; |
|
384
|
|
|
// If this controller has cached marker |
|
385
|
|
|
if (stripos($controllerName, self::CTR_CACHE) !== false) { |
|
386
|
|
|
// perform caching |
|
387
|
3 |
|
s()->cached(); |
|
388
|
|
|
} |
|
389
|
|
|
//elapsed('Performing #'.$this->id.' controller action -'.$controller); |
|
390
|
3 |
|
|
|
391
|
|
|
// Perform controller action |
|
392
|
|
|
$action_result = isset($controller) ? call_user_func_array($controller, $parameters) : A_FAILED; |
|
|
|
|
|
|
393
|
3 |
|
|
|
394
|
2 |
|
// Stop candidate search |
|
395
|
2 |
|
return !isset($action_result) ? A_SUCCESS : $action_result; |
|
|
|
|
|
|
396
|
1 |
|
} |
|
397
|
|
|
|
|
398
|
|
|
/** |
|
399
|
|
|
* Constructor |
|
400
|
2 |
|
* |
|
401
|
|
|
* @param string $id Module unique identifier |
|
402
|
|
|
* @param string $path Module location |
|
403
|
3 |
|
* @param ResourcesInterface $resourceMap Pointer to module resource map |
|
404
|
|
|
*/ |
|
405
|
|
|
public function __construct($id, $path = null, ResourcesInterface $resourceMap = null) |
|
406
|
3 |
|
{ |
|
407
|
1 |
|
// Store pointer to module resource map |
|
408
|
|
|
$this->resourceMap = &$resourceMap; |
|
409
|
|
|
// Save views list |
|
410
|
|
|
$this->views = $resourceMap->views; |
|
|
|
|
|
|
411
|
1 |
|
|
|
412
|
1 |
|
// Set default view context name |
|
413
|
1 |
|
$this->view_context = self::VD_POINTER_DEF; |
|
414
|
1 |
|
|
|
415
|
1 |
|
// Set up default view data pointer |
|
416
|
1 |
|
$this->data = &$this->view_data[$this->view_context]; |
|
417
|
1 |
|
|
|
418
|
3 |
|
// Set module identifier |
|
419
|
|
|
$this->id = $id; |
|
420
|
3 |
|
|
|
421
|
3 |
|
// Set path to module |
|
422
|
|
|
$this->path(realpath($path)); |
|
423
|
1 |
|
|
|
424
|
|
|
// Generate unique module identifier |
|
425
|
1 |
|
$this->uid = rand(0, 9999999) . '_' . microtime(true); |
|
426
|
1 |
|
|
|
427
|
|
|
// Add to module identifier to view data stack |
|
428
|
2 |
|
$this->data['id'] = $this->id; |
|
429
|
|
|
|
|
430
|
2 |
|
// Generate unique module cache path in local web-application |
|
431
|
|
|
$this->cache_path = __SAMSON_CWD__ . __SAMSON_CACHE_PATH . $this->id . '/'; |
|
432
|
|
|
|
|
433
|
|
|
// Save ONLY ONE copy of this instance in static instances collection, |
|
434
|
1 |
|
// avoiding rewriting by cloned modules |
|
435
|
|
|
!isset(self::$instances[$this->id]) ? self::$instances[$this->id] = &$this : ''; |
|
436
|
1 |
|
|
|
437
|
|
|
// Find all controller actions |
|
438
|
|
|
$functions = get_defined_functions(); |
|
439
|
|
|
foreach ($functions['user'] as $method) { |
|
440
|
|
|
// Try to find standard controllers |
|
441
|
|
|
switch (strtolower($method)) { |
|
442
|
|
|
// Ignore special controllers |
|
443
|
|
|
case $this->id . self::CTR_UNI : |
|
|
|
|
|
|
444
|
|
|
case $this->id . self::CTR_POST : |
|
|
|
|
|
|
445
|
|
|
case $this->id . self::CTR_PUT : |
|
|
|
|
|
|
446
|
1 |
|
case $this->id . self::CTR_DELETE : |
|
|
|
|
|
|
447
|
|
|
case $this->id . self::CTR_BASE : |
|
|
|
|
|
|
448
|
|
|
break; |
|
449
|
1 |
|
|
|
450
|
1 |
|
// Default controller |
|
451
|
1 |
|
//case $this->id: $this->controllers[ $method ] = $method; break; |
|
452
|
|
|
|
|
453
|
|
|
// Check if regular controller |
|
454
|
1 |
|
default: |
|
455
|
|
|
if (preg_match('/^' . $this->id . self::PROC_PREFIX . '(?<controller>.+)/i', $method, $matches)) { |
|
456
|
|
|
$this->controllers[$matches['controller']] = $method; |
|
457
|
1 |
|
} |
|
458
|
|
|
} |
|
459
|
1 |
|
} |
|
460
|
1 |
|
|
|
461
|
1 |
|
// Iterate class methods |
|
462
|
|
|
foreach (get_class_methods($this) as $method) { |
|
463
|
|
|
// Try to find standard controllers |
|
464
|
1 |
|
switch (strtolower($method)) { |
|
465
|
|
|
// Ignore special controllers |
|
466
|
|
|
case self::CTR_UNI: |
|
467
|
|
|
case self::CTR_POST : |
|
|
|
|
|
|
468
|
|
|
case self::CTR_PUT: |
|
469
|
|
|
case self::CTR_DELETE: |
|
470
|
|
|
case self::CTR_BASE : |
|
|
|
|
|
|
471
|
|
|
break; |
|
472
|
|
|
|
|
473
|
|
|
// Ignore magic methods |
|
474
|
|
|
case '__call': |
|
475
|
1 |
|
case '__wakeup': |
|
476
|
|
|
case '__sleep': |
|
477
|
|
|
case '__construct': |
|
478
|
1 |
|
case '__destruct': |
|
479
|
|
|
case '__set': |
|
480
|
|
|
case '__get': |
|
481
|
1 |
|
break; |
|
482
|
|
|
|
|
483
|
|
|
// Check if regular controller |
|
484
|
1 |
|
default: |
|
485
|
|
|
if (preg_match('/^' . self::OBJ_PREFIX . '(?<cache>cache_)?(?<controller>.+)/i', $method, $matches)) { |
|
486
|
|
|
// If this controller has a cachable mark - store them in special collection |
|
487
|
1 |
|
if (isset($matches['cache']) && isset($matches['cache']{1})) { |
|
488
|
1 |
|
$this->cacheControllers[$matches['controller']] = array($this, $method); |
|
489
|
|
|
} |
|
490
|
|
|
|
|
491
|
|
|
// Just regular controller |
|
492
|
|
|
$this->controllers[$matches['controller']] = array($this, $method); |
|
493
|
|
|
} |
|
494
|
|
|
} |
|
495
|
1 |
|
} |
|
496
|
|
|
|
|
497
|
|
|
if (function_exists($this->id)) $this->controllers[self::CTR_BASE] = $this->id; |
|
|
|
|
|
|
498
|
1 |
View Code Duplication |
if (method_exists($this, self::CTR_BASE)) $this->controllers[self::CTR_BASE] = array($this, self::CTR_BASE); |
|
|
|
|
|
|
499
|
|
|
else if (method_exists($this, self::CTR_CACHE_BASE)) $this->controllers[self::CTR_BASE] = array($this, self::CTR_CACHE_BASE); |
|
|
|
|
|
|
500
|
|
|
|
|
501
|
1 |
View Code Duplication |
if (function_exists($this->id . self::CTR_POST)) $this->controllers[self::CTR_POST] = $this->id . self::CTR_POST; |
|
|
|
|
|
|
502
|
1 |
View Code Duplication |
if (method_exists($this, self::CTR_POST)) $this->controllers[self::CTR_POST] = array($this, self::CTR_POST); |
|
|
|
|
|
|
503
|
|
|
else if (method_exists($this, self::CTR_CACHE_POST)) $this->controllers[self::CTR_POST] = array($this, self::CTR_CACHE_POST); |
|
|
|
|
|
|
504
|
|
|
|
|
505
|
|
View Code Duplication |
if (function_exists($this->id . self::CTR_PUT)) $this->controllers[self::CTR_PUT] = $this->id . self::CTR_PUT; |
|
|
|
|
|
|
506
|
|
View Code Duplication |
if (method_exists($this, self::CTR_PUT)) $this->controllers[self::CTR_PUT] = array($this, self::CTR_PUT); |
|
|
|
|
|
|
507
|
|
|
else if (method_exists($this, self::CTR_CACHE_PUT)) $this->controllers[self::CTR_PUT] = array($this, self::CTR_CACHE_PUT); |
|
|
|
|
|
|
508
|
|
|
|
|
509
|
|
View Code Duplication |
if (function_exists($this->id . self::CTR_DELETE)) $this->controllers[self::CTR_DELETE] = $this->id . self::CTR_DELETE; |
|
|
|
|
|
|
510
|
|
View Code Duplication |
if (method_exists($this, self::CTR_DELETE)) $this->controllers[self::CTR_DELETE] = array($this, self::CTR_DELETE); |
|
|
|
|
|
|
511
|
|
|
else if (method_exists($this, self::CTR_CACHE_DELETE)) $this->controllers[self::CTR_DELETE] = array($this, self::CTR_CACHE_DELETE); |
|
|
|
|
|
|
512
|
|
|
|
|
513
|
|
View Code Duplication |
if (function_exists($this->id . self::CTR_UNI)) $this->controllers[self::CTR_UNI] = $this->id . self::CTR_UNI; |
|
|
|
|
|
|
514
|
|
View Code Duplication |
if (method_exists($this, self::CTR_UNI)) $this->controllers[self::CTR_UNI] = array($this, self::CTR_UNI); |
|
|
|
|
|
|
515
|
|
|
else if (method_exists($this, self::CTR_CACHE_UNI)) $this->controllers[self::CTR_UNI] = array($this, self::CTR_CACHE_UNI); |
|
|
|
|
|
|
516
|
|
|
|
|
517
|
|
|
// Make view path relative to module - remove module path from view path |
|
518
|
|
|
$this->views = str_replace($this->path, '', $this->views); |
|
519
|
|
|
|
|
520
|
|
|
//elapsed('Registering module: '.$this->id.'('.$path.')' ); |
|
521
|
|
|
} |
|
522
|
|
|
|
|
523
|
|
|
/** Обработчик уничтожения объекта */ |
|
524
|
|
|
public function __destruct() |
|
525
|
|
|
{ |
|
526
|
|
|
//trace('Уничтожение модуля:'.$this->id ); |
|
527
|
|
|
|
|
528
|
|
|
// Очистим коллекцию загруженых модулей |
|
529
|
|
|
unset(Module::$instances[$this->id]); |
|
530
|
|
|
} |
|
531
|
|
|
|
|
532
|
|
|
// Магический метод для получения переменных представления модуля |
|
533
|
|
|
public function __get($field) |
|
534
|
|
|
{ |
|
535
|
|
|
// Установим пустышку как значение переменной |
|
536
|
|
|
$result = null; |
|
537
|
|
|
|
|
538
|
|
|
// Если указанная переменная представления существует - получим её значение |
|
539
|
|
|
if (isset($this->data[$field])) $result = &$this->data[$field]; |
|
|
|
|
|
|
540
|
|
|
// Выведем ошибку |
|
541
|
|
|
else return e('Ошибка получения данных модуля(##) - Требуемые данные(##) не найдены', E_SAMSON_CORE_ERROR, array($this->id, $field)); |
|
|
|
|
|
|
542
|
|
|
|
|
543
|
|
|
// Иначе вернем пустышку |
|
544
|
|
|
return $result; |
|
545
|
|
|
} |
|
546
|
|
|
|
|
547
|
|
|
|
|
548
|
|
|
/** |
|
549
|
|
|
* Create unique module cache folder structure in local web-application |
|
550
|
|
|
* @param string $file Path to file relative to module cache location |
|
551
|
|
|
* @param boolean $clear Flag to perform generic cache folder clearence |
|
552
|
|
|
* @param string $dir Directory in the cache location |
|
553
|
|
|
* @return boolean TRUE if cache file has to be regenerated |
|
554
|
|
|
*/ |
|
555
|
|
|
protected function cache_refresh(& $file, $clear = true, $dir = null) |
|
|
|
|
|
|
556
|
|
|
{ |
|
557
|
|
|
// If module cache folder does not exists - create it |
|
558
|
|
|
if (!file_exists($this->cache_path)) mkdir($this->cache_path, 0777, TRUE); |
|
|
|
|
|
|
559
|
|
|
|
|
560
|
|
|
$path = !isset($dir)?$this->cache_path:$this->cache_path.$dir.'/'; |
|
561
|
|
|
|
|
562
|
|
|
// If module cache folder does not exists - create it |
|
563
|
|
|
if (!file_exists($path)) mkdir($path, 0777, TRUE); |
|
|
|
|
|
|
564
|
|
|
|
|
565
|
|
|
// Build full path to cached file |
|
566
|
|
|
$file = $path . $file; |
|
567
|
|
|
|
|
568
|
|
|
// If cached file does not exsits |
|
569
|
|
|
if (file_exists($file)) return false; |
|
|
|
|
|
|
570
|
|
|
// Needed file does not exists |
|
571
|
|
|
else { |
|
572
|
|
|
// If clearence flag set to true - clear all files in module cache directory with same extension |
|
573
|
|
|
if ($clear) File::clear($path, pathinfo($file, PATHINFO_EXTENSION)); |
|
|
|
|
|
|
574
|
|
|
|
|
575
|
|
|
// Singal for cache file regeneration |
|
576
|
|
|
return true; |
|
577
|
|
|
} |
|
578
|
|
|
} |
|
579
|
|
|
|
|
580
|
|
|
// TODO: Переделать обработчик в одинаковый вид для объектов и простых |
|
581
|
|
|
|
|
582
|
|
|
/** |
|
583
|
|
|
* |
|
584
|
|
|
* @param unknown $object |
|
585
|
|
|
* @param string $viewprefix |
|
586
|
|
|
*/ |
|
587
|
|
|
private function _setObject($object, $viewprefix = null) |
|
588
|
|
|
{ |
|
589
|
|
|
// Generate viewprefix as only lowercase classname without NS if it is not specified |
|
590
|
|
|
$class_name = is_string($viewprefix) ? $viewprefix : '' . mb_strtolower(classname(get_class($object)), 'UTF-8'); |
|
591
|
|
|
|
|
592
|
|
|
// Save object to view data |
|
593
|
|
|
$this->data[$class_name] = $object; |
|
594
|
|
|
|
|
595
|
|
|
// Add separator |
|
596
|
|
|
$class_name .= '_'; |
|
597
|
|
|
|
|
598
|
|
|
// Generate objects view array data and merge it with view data |
|
599
|
|
|
$this->data = array_merge($this->data, $object->toView($class_name)); |
|
600
|
|
|
} |
|
601
|
|
|
|
|
602
|
|
|
/** |
|
603
|
|
|
* |
|
604
|
|
|
* @param unknown $array |
|
605
|
|
|
* @param string $viewprefix |
|
606
|
|
|
*/ |
|
607
|
|
|
private function _setArray($array, $viewprefix = null) |
|
608
|
|
|
{ |
|
609
|
|
|
// Save array to view data |
|
610
|
|
|
$this->data[$viewprefix] = $array; |
|
611
|
|
|
|
|
612
|
|
|
// Add array values to view data |
|
613
|
|
|
$this->data = array_merge($this->data, $array); |
|
614
|
|
|
} |
|
615
|
|
|
|
|
616
|
|
|
// Магический метод для установки переменных представления модуля |
|
617
|
|
|
public function __set($field, $value = null) |
|
618
|
|
|
{ |
|
619
|
|
|
// This is object |
|
620
|
|
|
if (is_object($field)) { |
|
621
|
|
|
$implements = class_implements($field); |
|
622
|
|
|
// If iModuleViewable implements is passed |
|
623
|
|
|
if ( |
|
624
|
|
|
// TODO: Remove old interface support in future |
|
625
|
|
|
in_array(ns_classname('iModuleViewable', 'samson\core'), $implements) |
|
|
|
|
|
|
626
|
|
|
|| in_array(AutoLoader::className('IViewSettable', 'samson\core'), $implements) |
|
627
|
|
|
|| in_array('samsonframework\core\RenderInterface', $implements) |
|
628
|
|
|
) { |
|
629
|
|
|
$this->_setObject($field, $value); |
|
630
|
|
|
} |
|
631
|
|
|
} // If array is passed |
|
632
|
|
|
else if (is_array($field)) $this->_setArray($field, $value); |
|
|
|
|
|
|
633
|
|
|
// Set view variable |
|
634
|
|
|
else $this->data[$field] = $value; |
|
|
|
|
|
|
635
|
|
|
} |
|
636
|
|
|
|
|
637
|
|
|
/** Magic method for calling unexisting object methods */ |
|
638
|
|
|
public function __call($method, $arguments) |
|
639
|
|
|
{ |
|
640
|
|
|
//elapsed($this->id.' - __Call '.$method); |
|
641
|
|
|
|
|
642
|
|
|
// If value is passed - set it |
|
643
|
|
|
if (sizeof($arguments)) { |
|
644
|
|
|
// If first argument is object or array - pass method name as second parameter |
|
645
|
|
|
if (is_object($arguments[0]) || is_array($arguments[0])) $this->__set($arguments[0], $method); |
|
|
|
|
|
|
646
|
|
|
// Standard logic |
|
647
|
|
|
else $this->__set($method, $arguments[0]); |
|
|
|
|
|
|
648
|
|
|
} |
|
649
|
|
|
|
|
650
|
|
|
// Chaining |
|
651
|
|
|
return $this; |
|
652
|
|
|
} |
|
653
|
|
|
|
|
654
|
|
|
/** Обработчик сериализации объекта */ |
|
655
|
|
|
public function __sleep() |
|
656
|
|
|
{ |
|
657
|
|
|
return array('id', 'path', 'version', 'data', 'controllers', 'views'); |
|
658
|
|
|
} |
|
659
|
|
|
|
|
660
|
|
|
/** Обработчик десериализации объекта */ |
|
661
|
|
|
public function __wakeup() |
|
662
|
|
|
{ |
|
663
|
|
|
// Fill global instances |
|
664
|
|
|
self::$instances[$this->id] = &$this; |
|
665
|
|
|
|
|
666
|
|
|
// Set up default view data pointer |
|
667
|
|
|
$this->view_data[self::VD_POINTER_DEF] = $this->data; |
|
668
|
|
|
|
|
669
|
|
|
// Set reference to view context entry |
|
670
|
|
|
$this->data = &$this->view_data[self::VD_POINTER_DEF]; |
|
671
|
|
|
} |
|
672
|
|
|
|
|
673
|
|
|
/** Группа методов для доступа к аттрибутам в виде массива */ |
|
674
|
|
|
public function offsetSet($offset, $value) |
|
675
|
|
|
{ |
|
676
|
|
|
$this->__set($offset, $value); |
|
677
|
|
|
} |
|
678
|
|
|
|
|
679
|
|
|
public function offsetGet($offset) |
|
680
|
|
|
{ |
|
681
|
|
|
return $this->__get($offset); |
|
682
|
|
|
} |
|
683
|
|
|
|
|
684
|
|
|
public function offsetUnset($offset) |
|
685
|
|
|
{ |
|
686
|
|
|
$this->data[$offset] = ''; |
|
687
|
|
|
} |
|
688
|
|
|
|
|
689
|
|
|
public function offsetExists($offset) |
|
690
|
|
|
{ |
|
691
|
|
|
return isset($this->data[$offset]); |
|
692
|
|
|
} |
|
693
|
|
|
} |
|
694
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.