1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
class sn_module { |
4
|
|
|
/** |
5
|
|
|
* @var sn_module[] |
6
|
|
|
*/ |
7
|
|
|
protected static $sn_module = array(); |
8
|
|
|
|
9
|
|
|
public static $sn_module_list = array(); |
10
|
|
|
|
11
|
|
|
public $manifest = array( |
12
|
|
|
'package' => 'core', |
13
|
|
|
'name' => 'sn_module', |
14
|
|
|
'version' => '1c0', |
15
|
|
|
'copyright' => 'Project "SuperNova.WS" #41a52.93# copyright © 2009-2014 Gorlum', |
16
|
|
|
|
17
|
|
|
'require' => array(), |
18
|
|
|
'root_relative' => '', |
19
|
|
|
|
20
|
|
|
'installed' => true, |
21
|
|
|
'active' => true, |
22
|
|
|
|
23
|
|
|
// 'constants' array - contents of this array would be instaled into engine. 'UNIT_STRUCTURE_NEW' => 999999, |
24
|
|
|
'constants' => array(), |
25
|
|
|
|
26
|
|
|
'vars' => array(), // Just a placeholder. vars assigned via special method __assign_vars(). Need 'cause of new constants that can be defined within module. See below |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* 'functions' array - this functions would be installed as hooks |
30
|
|
|
* Key: overwritable function name to replace |
31
|
|
|
* Value: which method to use. Format: [*][<object_name>][.]<method> |
32
|
|
|
* '*' means that new function would replace old one |
33
|
|
|
* If object_name is ommited but "." is present - hook linked to global function |
34
|
|
|
* If only "method" present - overwritable linked to appropriate method of current object |
35
|
|
|
* Examples: |
36
|
|
|
* 'test_object.test_method' - will |
37
|
|
|
* Function/Method should be accessible on module init |
38
|
|
|
* // 'test_object_test_method' => 'test_object.test_method', |
39
|
|
|
* // 'test_function' => '.my_test_function', |
40
|
|
|
* // 'this_object_test_method' => 'test_method', |
41
|
|
|
*/ |
42
|
|
|
'functions' => array(), |
43
|
|
|
|
44
|
|
|
// 'menu' array - this menu items would be merged into main game menu |
45
|
|
|
// Array element almost identical to $sn_menu with additional param 'LOCATION'. |
46
|
|
|
// 'LOCATION' => '-news', // Special atrtribute for modules |
|
|
|
|
47
|
|
|
// [-|+][<menu_item_id>] |
|
|
|
|
48
|
|
|
// <menu_item_id> identifies menu item aginst new menu item would be placed. When ommited new item placed against whole menu |
49
|
|
|
// -/+ indicates that new item should be placed before/after identified menu item (or whole menu). If ommited and menu item exists - new item will replace previous one |
50
|
|
|
// Empty or non-existent LOCATION equivalent to '+' - place item at end of menu |
51
|
|
|
// Non-existent menu_item_id treated as ommited |
52
|
|
|
'menu' => array(), |
53
|
|
|
|
54
|
|
|
// 'page' array - defines pages which will handle this module and appropriate handlers |
55
|
|
|
'page' => array(), |
56
|
|
|
); |
57
|
|
|
|
58
|
|
|
protected $config = array(); |
59
|
|
|
|
60
|
|
|
protected $module_full_class_path = __FILE__; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Динамическое назначение переменных |
64
|
|
|
* |
65
|
|
|
* Актуально, когда записываемые данные зависят от статуса игры |
66
|
|
|
* Например - назначаются константы внутри модуля |
67
|
|
|
* |
68
|
|
|
* @return array |
69
|
|
|
*/ |
70
|
|
|
public function __assign_vars() { |
71
|
|
|
return array(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
public function loadModuleRootConfig() { |
75
|
|
|
require SN_ROOT_PHYSICAL . 'config.php'; |
76
|
|
|
|
77
|
|
|
$module_config_array = get_class($this) . '_config'; |
78
|
|
|
if(!empty($$module_config_array) && is_array($$module_config_array)) { |
79
|
|
|
$this->config = $$module_config_array; |
80
|
|
|
|
81
|
|
|
return true; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
return false; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
public function __construct($filename = __FILE__) { |
88
|
|
|
// Getting module PHP class name |
89
|
|
|
$class_module_name = get_class($this); |
90
|
|
|
|
91
|
|
|
// Getting module root relative to SN |
92
|
|
|
$this->manifest['root_relative'] = str_replace(array(SN_ROOT_PHYSICAL, basename($filename)), '', str_replace('\\', '/', $filename)); |
93
|
|
|
|
94
|
|
|
// TODO: Load configuration from DB. Manifest setting |
95
|
|
|
// Trying to load configuration from file |
96
|
|
|
if(!$config_exists = $this->loadModuleRootConfig()) { |
97
|
|
|
// Конфигурация может лежать в config_path в манифеста или в корне модуля |
98
|
|
|
if(isset($this->manifest['config_path']) && file_exists($config_filename = $this->manifest['config_path'] . '/config.php')) { |
99
|
|
|
$config_exists = true; |
100
|
|
|
} elseif(file_exists($config_filename = dirname($filename) . '/config.php')) { |
101
|
|
|
$config_exists = true; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
if($config_exists) { |
105
|
|
|
include($config_filename); |
106
|
|
|
$module_config_array = $class_module_name . '_config'; |
107
|
|
|
$this->config = $$module_config_array; |
108
|
|
|
} |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
// Registering module |
112
|
|
|
self::$sn_module[$class_module_name] = $this; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
|
116
|
|
|
public function initialize() { |
117
|
|
|
global $sn_menu_extra, $sn_menu_admin_extra; |
118
|
|
|
|
119
|
|
|
// Checking module status - is it installed and active |
120
|
|
|
$this->check_status(); |
121
|
|
|
if(!$this->manifest['active']) { |
122
|
|
|
return; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
$this->setSystemConstants(); |
126
|
|
|
$this->setSystemVariables(); |
127
|
|
|
$this->addSystemHandlers(); |
128
|
|
|
$this->mergeI18N(); |
129
|
|
|
|
130
|
|
|
// Patching game menu - if any |
131
|
|
|
isset($this->manifest['menu']) ? $this->mergeMenu($sn_menu_extra, $this->manifest['menu']) : false; |
132
|
|
|
isset($this->manifest['menu_admin']) ? $this->mergeMenu($sn_menu_admin_extra, $this->manifest['menu_admin']) : false; |
133
|
|
|
|
134
|
|
|
$this->mergeJavascript(); |
135
|
|
|
$this->mergeCss(); |
136
|
|
|
$this->mergeNavbarButton(); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
protected function setSystemConstants() { |
140
|
|
|
// Setting constants - if any |
141
|
|
|
if(empty($this->manifest['constants']) || !is_array($this->manifest['constants'])) { |
142
|
|
|
return; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
foreach($this->manifest['constants'] as $constant_name => $constant_value) { |
146
|
|
|
!defined($constant_name) ? define($constant_name, $constant_value) : false; |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
protected function setSystemVariables() { |
151
|
|
|
// Adding vars - if any |
152
|
|
|
// Due to possible introduce of new constants in previous step vars is assigned via special method to honor new constants |
153
|
|
|
// Assignation can work with simple variables and with multidimensional arrays - for ex. 'sn_data[groups][test]' |
154
|
|
|
// New values from module variables will overwrite previous values (for root variables) and array elements with corresponding indexes (for arrays) |
155
|
|
|
// Constants as array indexes are honored - it's make valid such declarations as 'sn_data[ques][QUE_STRUCTURES]' |
156
|
|
|
$this->manifest['vars'] = $this->__assign_vars(); |
157
|
|
|
if(empty($this->manifest['vars']) || !is_array($this->manifest['vars'])) { |
158
|
|
|
return; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
$vars_assigned = array(); |
162
|
|
|
foreach($this->manifest['vars'] as $var_name => $var_value) { |
163
|
|
|
$sub_vars = explode('[', str_replace(']', '', $var_name)); |
164
|
|
|
$var_name = $sub_vars[0]; |
165
|
|
|
|
166
|
|
|
if(!isset($vars_assigned[$var_name])) { |
167
|
|
|
$vars_assigned[$var_name] = true; |
168
|
|
|
global $$var_name; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
$pointer = &$$var_name; |
172
|
|
|
if(($n = count($sub_vars)) > 1) { |
173
|
|
|
for($i = 1; $i < $n; $i++) { |
174
|
|
|
if(defined($sub_vars[$i])) { |
175
|
|
|
$sub_vars[$i] = constant($sub_vars[$i]); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
if(!isset($pointer[$sub_vars[$i]]) && $i != $n) { |
179
|
|
|
$pointer[$sub_vars[$i]] = array(); |
180
|
|
|
} |
181
|
|
|
$pointer = &$pointer[$sub_vars[$i]]; |
182
|
|
|
} |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
if(!isset($pointer) || !is_array($pointer)) { |
186
|
|
|
$pointer = $var_value; |
187
|
|
|
} elseif(is_array($$var_name)) { |
188
|
|
|
$pointer = array_merge_recursive_numeric($pointer, $var_value); |
189
|
|
|
} |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
protected function mergeMenu(&$sn_menu_extra, &$menu_patch) { |
194
|
|
|
if(!is_array($menu_patch)) { |
195
|
|
|
return; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
foreach($menu_patch as $menu_item_name => $menu_item_data) { |
199
|
|
|
$sn_menu_extra[$menu_item_name] = $menu_item_data; |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
protected function addSystemHandlers() { |
204
|
|
|
// Overriding function if any |
205
|
|
|
sn_sys_handler_add(classSupernova::$functions, $this->manifest['functions'], $this); |
206
|
|
|
|
207
|
|
|
foreach(classSupernova::$sn_mvc as $handler_type => &$handler_data) { |
208
|
|
|
sn_sys_handler_add($handler_data, $this->manifest['mvc'][$handler_type], $this, $handler_type); |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
protected function mergeNavbarButton() { |
213
|
|
|
if(empty($this->manifest['navbar_prefix_button']) || !is_array($this->manifest['navbar_prefix_button'])) { |
214
|
|
|
return; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
foreach($this->manifest['navbar_prefix_button'] as $button_image => $button_url_relative) { |
218
|
|
|
classSupernova::$sn_mvc['navbar_prefix_button'][$button_image] = $button_url_relative; |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
protected function mergeI18N() { |
223
|
|
|
$arrayName = 'i18n'; |
224
|
|
|
if(empty($this->manifest[$arrayName]) || !is_array($this->manifest[$arrayName])) { |
225
|
|
|
return; |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
foreach($this->manifest[$arrayName] as $pageName => &$contentList) { |
229
|
|
|
foreach($contentList as &$i18n_file_data) { |
230
|
|
|
if(is_array($i18n_file_data) && !$i18n_file_data['path']) { |
231
|
|
|
$i18n_file_data['path'] = $this->manifest['root_relative']; |
232
|
|
|
} |
233
|
|
|
} |
234
|
|
|
if(!isset(classSupernova::$sn_mvc[$arrayName][$pageName])) { |
235
|
|
|
classSupernova::$sn_mvc[$arrayName][$pageName] = array(); |
236
|
|
|
} |
237
|
|
|
classSupernova::$sn_mvc[$arrayName][$pageName] += $contentList; |
238
|
|
|
} |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @param string $arrayName - name of manifest fields to get data from |
243
|
|
|
*/ |
244
|
|
|
protected function mergeArraySpecial($arrayName) { |
245
|
|
|
$this->mergeArrayParam($this->manifest[$arrayName], classSupernova::$sn_mvc[$arrayName]); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* @param array $arrayFrom |
250
|
|
|
* @param array $arrayMergeTo |
251
|
|
|
*/ |
252
|
|
|
protected function mergeArrayParam(&$arrayFrom, &$arrayMergeTo) { |
253
|
|
|
if (!is_array($arrayFrom) || empty($arrayFrom)) { |
254
|
|
|
return; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
foreach ($arrayFrom as $pageName => &$contentList) { |
258
|
|
|
!isset($arrayMergeTo[$pageName]) ? $arrayMergeTo[$pageName] = array() : false; |
259
|
|
|
foreach ($contentList as $contentName => &$content) { |
260
|
|
|
$arrayMergeTo[$pageName][$contentName] = $content; |
261
|
|
|
} |
262
|
|
|
} |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
protected function mergeCss() { $this->mergeArrayParam($this->manifest['css'], classSupernova::$css); } |
266
|
|
|
|
267
|
|
|
protected function mergeJavascript() { $this->mergeArraySpecial('javascript'); } |
268
|
|
|
|
269
|
|
|
public function check_status() { } |
270
|
|
|
|
271
|
|
|
public static function orderModules() { |
272
|
|
|
// load_order: |
273
|
|
|
// 100000 - default load order |
274
|
|
|
// 999999 - core_ship_constructor |
275
|
|
|
// 2000000000 - that requires that all possible modules loaded already |
276
|
|
|
// 2000100000 - game_skirmish |
277
|
|
|
|
278
|
|
|
// Генерируем список требуемых модулей |
279
|
|
|
$load_order = array(); |
280
|
|
|
$sn_req = array(); |
281
|
|
|
|
282
|
|
|
foreach (sn_module::$sn_module as $loaded_module_name => $module_data) { |
283
|
|
|
$load_order[$loaded_module_name] = !empty($module_data->manifest['load_order']) ? $module_data->manifest['load_order'] : 100000; |
284
|
|
|
if (!empty($module_data->manifest['require'])) { |
285
|
|
|
foreach ($module_data->manifest['require'] as $require_name) { |
286
|
|
|
$sn_req[$loaded_module_name][$require_name] = 0; |
287
|
|
|
} |
288
|
|
|
} |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
// pdump($load_order, '$load_order'); |
|
|
|
|
292
|
|
|
|
293
|
|
|
// Создаем последовательность инициализации модулей |
294
|
|
|
// По нормальным делам надо сначала читать их конфиги - вдруг какой-то модуль отключен? |
295
|
|
|
do { |
296
|
|
|
$prev_order = $load_order; |
297
|
|
|
|
298
|
|
|
foreach ($sn_req as $loaded_module_name => &$req_data) { |
299
|
|
|
$level = 1; |
300
|
|
|
foreach ($req_data as $req_name => &$req_level) { |
301
|
|
|
if ($load_order[$req_name] == -1 || !isset($load_order[$req_name])) { |
302
|
|
|
$level = $req_level = -1; |
303
|
|
|
break; |
304
|
|
|
} else { |
305
|
|
|
$level += $load_order[$req_name]; |
306
|
|
|
} |
307
|
|
|
$req_level = $load_order[$req_name]; |
308
|
|
|
} |
309
|
|
|
if ($level > $load_order[$loaded_module_name] || $level == -1) { |
310
|
|
|
$load_order[$loaded_module_name] = $level; |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
} while($prev_order != $load_order); |
314
|
|
|
|
315
|
|
|
asort($load_order); |
316
|
|
|
|
317
|
|
|
unset($sn_req); |
318
|
|
|
|
319
|
|
|
// Инициализируем модули |
320
|
|
|
// По нормальным делам это должна быть загрузка модулей и лишь затем инициализация - что бы минимизировать размер процесса в памяти |
321
|
|
|
foreach ($load_order as $loaded_module_name => $load_order_order) { |
322
|
|
|
if ($load_order_order >= 0) { |
323
|
|
|
sn_module::$sn_module[$loaded_module_name]->check_status(); |
324
|
|
|
if (!sn_module::$sn_module[$loaded_module_name]->manifest['active']) { |
325
|
|
|
unset(sn_module::$sn_module[$loaded_module_name]); |
326
|
|
|
continue; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
sn_module::$sn_module[$loaded_module_name]->initialize(); |
330
|
|
|
sn_module::$sn_module_list[sn_module::$sn_module[$loaded_module_name]->manifest['package']][$loaded_module_name] = &sn_module::$sn_module[$loaded_module_name]; |
331
|
|
|
} else { |
332
|
|
|
unset(sn_module::$sn_module[$loaded_module_name]); |
333
|
|
|
} |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
// Скрипач не нужон |
337
|
|
|
unset($load_order); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* @param string $moduleName |
342
|
|
|
* |
343
|
|
|
* @return bool |
344
|
|
|
*/ |
345
|
|
|
public static function isModuleActive($moduleName) { |
346
|
|
|
return |
347
|
|
|
!empty(sn_module::$sn_module[$moduleName]) |
348
|
|
|
&& |
349
|
|
|
sn_module::$sn_module[$moduleName] instanceof sn_module |
350
|
|
|
&& |
351
|
|
|
sn_module::$sn_module[$moduleName]->manifest['active']; |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
/** |
355
|
|
|
* @param string $moduleName |
356
|
|
|
* |
357
|
|
|
* @return sn_module |
358
|
|
|
*/ |
359
|
|
|
public static function getModule($moduleName) { |
360
|
|
|
return sn_module::$sn_module[$moduleName]; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
} |
364
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.