Completed
Push — trunk ( 0e7a2e...6a6c5e )
by SuperNova.WS
07:28
created

sn_module   F

Complexity

Total Complexity 88

Size/Duplication

Total Lines 406
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 406
ccs 0
cts 187
cp 0
rs 1.5789
c 0
b 0
f 0
wmc 88

13 Methods

Rating   Name   Duplication   Size   Complexity  
B __patch_menu() 0 4 5
A isActive() 0 2 2
F initialize() 0 131 53
A addModuleTemplate() 0 2 1
A check_status() 0 1 1
B registerHooks() 0 18 8
A addFunctionHook() 0 2 1
A getRootRelative() 0 6 2
A getLoadOrder() 0 2 2
A __mvcRegisterPagesOld() 0 4 4
A __assign_vars() 0 2 1
A getTemplateRootRelative() 0 2 1
C __construct() 0 39 7

How to fix   Complexity   

Complex Class

Complex classes like sn_module often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use sn_module, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Modules;
4
5
use Common\Hooker\Pimp;
6
use Exception;
7
use SN;
8
use template;
9
10
class sn_module {
11
  // Manifest params
12
  const M_REQUIRE = 'require';
13
  const M_ROOT_RELATIVE = 'root_relative';
14
  const M_LOAD_ORDER = 'load_order';
15
16
  /**
17
   * SN version in which module was committed. Can be treated as version in which module guaranteed to work
18
   * @var string $versionCommitted
19
   */
20
  public $versionCommitted = '#43a15.20#';
21
22
  public $manifest = [
23
    'package'   => 'core',
24
    'name'      => 'Modules\sn_module',
25
    'version'   => '1c0',
26
    'copyright' => 'Project "SuperNova.WS" #43a15.20# copyright © 2009-2017 Gorlum',
27
28
    self::M_LOAD_ORDER => MODULE_LOAD_ORDER_DEFAULT,
29
30
    self::M_REQUIRE       => [],
31
    self::M_ROOT_RELATIVE => '',
32
33
    'installed' => true,
34
    'active'    => true,
35
36
    // 'constants' array - contents of this array would be instaled into engine
37
    'constants' => [
38
//      'UNIT_STRUCTURE_NEW' => 999999,
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
39
    ],
40
41
    'vars'      => [], // Just a placeholder. vars assigned via special method __assign_vars(). Need 'cause of new constants that can be defined within module. See below
42
43
    // 'functions' array - this functions would be installed as hooks
44
    // Key: overwritable function name to replace
45
    // Value: which method to use. Format: [*][<object_name>][.]<method>
46
    // '*' means that new function would replace old one
47
    // If object_name is ommited but "." is present - hook linked to global function
48
    // If only "method" present - overwritable linked to appropriate method of current object
49
    // Function/Method should be accessible on module init
50
    'functions' => [
51
//      'test_object_test_method' => 'test_object.test_method',
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
52
//      'test_function' => '.my_test_function',
53
//      'this_object_test_method' => 'test_method',
54
    ],
55
56
    // 'menu' array - this menu items would be merged into main game menu
57
    // Array element almost identical to $sn_menu with additional param 'LOCATION'.
58
    // 'LOCATION' => '-news', // Special atrtribute for modules
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
59
    // [-|+][<menu_item_id>]
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
60
    // <menu_item_id> identifies menu item aginst new menu item would be placed. When ommited new item placed against whole menu
61
    // -/+ 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
62
    // Empty or non-existent LOCATION equivalent to '+' - place item at end of menu
63
    // Non-existent menu_item_id treated as ommited
64
    'menu'      => [],
65
66
    // 'page' array - defines pages which will handle this module and appropriate handlers
67
    'page'      => [],
68
69
    /**
70
     * 'mvc' subarray
71
     * [
72
     *    FIELD_MODEL =>
73
     *    FIELD_VIEW =>
74
     *    MVC_OPTIONS =>
75
     * ]
76
     */
77
    'mvc'       => [],
78
  ];
79
80
  /**
81
   * New way to add functions instead of manifest['functions']
82
   *
83
   * [
84
   *   (string)$functionName => [
85
   *     (callable)$callable,
86
   *     (string)'methodName',                             // Local method name aka $this->methodName
87
   *     (callable array)[$this|objectName, 'methodName'], // Callable array
88
   *   ],
89
   * ]
90
   *
91
   * @var array $functions
92
   */
93
  protected $functions = [];
94
95
  protected $hooks = [];
96
97
  protected $config = array();
98
99
  protected $module_full_class_path = __FILE__;
100
101
  protected $filename = '';
102
103
  /**
104
   * @param string   $functionName
105
   * @param callable $callable
106
   */
107
  public function addFunctionHook($functionName, $callable) {
108
    $this->functions[$functionName][] = $callable;
109
  }
110
111
  /**
112
   * Динамическое назначение переменных
113
   *
114
   * Актуально, когда записываемые данные зависят от статуса игры
115
   * Например - назначаются константы внутри модуля
116
   *
117
   * @return array
118
   */
119
  protected function __assign_vars() {
120
    return array(/*
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
121
      'sn_data' => array(
122
        UNIT_STRUCTURE_NEW => array( // Will honor new constants
123
          'name' => 'robot_factory',
124
          'type' => UNIT_STRUCTURE,
125
          'location' => LOC_PLANET,
126
          'cost' => array(
127
            RES_METAL     => 400,
128
            RES_CRYSTAL   => 120,
129
            RES_DEUTERIUM => 200,
130
            RES_ENERGY    => 0,
131
            'factor' => 2,
132
          ),
133
          'metal' => 400,
134
          'crystal' => 120,
135
          'deuterium' => 200,
136
          'energy' => 0,
137
          'factor' => 2,
138
        ),
139
      ),
140
*/
141
    );
142
  }
143
144
  /**
145
   * sn_module constructor.
146
   *
147
   * @param string $filename
148
   */
149
  public function __construct($filename = __FILE__) {
150
    $this->filename = $filename;
151
152
    // Getting module PHP class name
153
    $class_module_name = get_called_class();
154
155
    // Validating source settings. Some fields are mandatory in each manifest
156
    // Should be removed when manifest would be parsed to separate fields
157
    foreach (['name', 'package', 'installed', 'active', 'version'] as $mandatoryManifest) {
158
      if (!array_key_exists($mandatoryManifest, $this->manifest)) {
159
        throw new Exception('{ There is no mandatory field "' . $mandatoryManifest . '" in manifest of module } "' . $class_module_name . '"');
160
      }
161
    }
162
163
    // Getting module root relative to SN
164
    $this->manifest[static::M_ROOT_RELATIVE] = str_replace(
165
      [SN_ROOT_PHYSICAL, basename($this->filename)],
166
      '',
167
      str_replace('\\', '/', $this->filename)
168
    );
169
170
    // TODO: Load configuration from DB. Manifest setting
171
    // Trying to load configuration from file
172
    $config_exists = false;
173
    // Конфигурация может лежать в config_path в манифеста или в корне модуля
174
    if (isset($this->manifest['config_path']) && file_exists($config_filename = $this->manifest['config_path'] . '/config.php')) {
175
      $config_exists = true;
176
    } elseif (file_exists($config_filename = dirname($filename) . '/config.php')) {
177
      $config_exists = true;
178
    }
179
180
    if ($config_exists) {
181
      include($config_filename);
182
      $module_config_array = $class_module_name . '_config';
183
      $this->config = $$module_config_array;
184
    }
185
186
    // Registering classes with autoloader
187
    \Core\Autoloader::register($this->getRootRelative() . 'classes/');
188
189
    // TODO - currently not possible because each module is not a service
190
    // When it's done - remove double registration from loadModulesFromDirectory()
191
    // Registering module in manager
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
192
//    SN::$gc->modules->registerModule($class_module_name, $this);
193
  }
194
195
  protected function __patch_menu(&$sn_menu_extra, &$menu_patch) {
196
    if (isset($menu_patch) && is_array($menu_patch) && !empty($menu_patch)) {
197
      foreach ($menu_patch as $menu_item_name => $menu_item_data) {
198
        $sn_menu_extra[$menu_item_name] = $menu_item_data;
199
      }
200
    }
201
  }
202
203
204
  /**
205
   *
206
   */
207
  public function initialize() {
208
    // Checking module status - is it installed and active
209
    $this->check_status();
210
    if (!$this->manifest['active']) {
211
      return;
212
    }
213
214
    // Setting constants - if any
215
    if (isset($this->manifest['constants']) && is_array($this->manifest['constants']) && !empty($this->manifest['constants'])) {
216
      foreach ($this->manifest['constants'] as $constant_name => $constant_value) {
217
        defined($constant_name) or define($constant_name, $constant_value);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
218
      }
219
    }
220
221
    // Adding vars - if any
222
    // Due to possible introduce of new constants in previous step vars is assigned via special method to honor new constants
223
    // Assignation can work with simple variables and with multidimensional arrays - for ex. 'sn_data[groups][test]'
224
    // New values from module variables will overwrite previous values (for root variables) and array elements with corresponding indexes (for arrays)
225
    // Constants as array indexes are honored - it's make valid such declarations as 'sn_data[ques][QUE_STRUCTURES]'
226
    $this->manifest['vars'] = $this->__assign_vars();
227
    if (!empty($this->manifest['vars'])) {
228
      $vars_assigned = array();
229
      foreach ($this->manifest['vars'] as $var_name => $var_value) {
230
        $sub_vars = explode('[', str_replace(']', '', $var_name));
231
        $var_name = $sub_vars[0];
232
233
        if (!isset($vars_assigned[$var_name])) {
234
          $vars_assigned[$var_name] = true;
235
          global $$var_name;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
236
        }
237
238
        $pointer = &$$var_name;
239
        if (($n = count($sub_vars)) > 1) {
240
          for ($i = 1; $i < $n; $i++) {
241
            if (defined($sub_vars[$i])) {
242
              $sub_vars[$i] = constant($sub_vars[$i]);
243
            }
244
245
            if (!isset($pointer[$sub_vars[$i]]) && $i != $n) {
246
              $pointer[$sub_vars[$i]] = array();
247
            }
248
            $pointer = &$pointer[$sub_vars[$i]];
249
          }
250
        }
251
252
        if (!isset($pointer) || !is_array($pointer)) {
253
          $pointer = $var_value;
254
        } elseif (is_array($$var_name)) {
255
          $pointer = array_merge_recursive_numeric($pointer, $var_value);
256
        }
257
      }
258
    }
259
260
    // Overriding function if any
261
    global $functions;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
262
    sn_sys_handler_add($functions, $this->manifest['functions'], $this);
263
264
    foreach ($this->functions as $functionName => $callableList) {
265
      !is_array($callableList) ? $callableList = [$callableList] : false;
266
      foreach ($callableList as $callable) {
267
        sys_handler_add_one($functions, $functionName, $callable, static::class, '');
268
      }
269
    }
270
271
    $this->registerHooks();
272
273
    // Patching game menu - if any
274
    global $sn_menu_extra, $sn_menu_admin_extra;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
275
    isset($this->manifest['menu']) and $this->__patch_menu($sn_menu_extra, $this->manifest['menu']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
276
    isset($this->manifest['menu_admin']) and $this->__patch_menu($sn_menu_admin_extra, $this->manifest['menu_admin']);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
277
278
    global $sn_mvc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
279
    foreach ($sn_mvc as $handler_type => &$handler_data) {
280
      if ($handler_type == MVC_OPTIONS) {
281
        continue;
282
      }
283
      sn_sys_handler_add($handler_data, $this->manifest['mvc'][$handler_type], $this, $handler_type);
284
    }
285
286
    if (!empty($this->manifest['mvc'][MVC_OPTIONS])) {
287
      foreach ($this->manifest['mvc'][MVC_OPTIONS] as $pageName => $pageOptions) {
288
        if (empty($pageOptions)) {
289
          continue;
290
        }
291
292
        !is_array($sn_mvc['pages'][$pageName][MVC_OPTIONS]) ? $sn_mvc['pages'][$pageName][MVC_OPTIONS] = [] : false;
293
        $sn_mvc['pages'][$pageName][MVC_OPTIONS] = array_merge($sn_mvc['pages'][$pageName][MVC_OPTIONS], $pageOptions);
294
      }
295
    }
296
297
    if (isset($this->manifest['i18n']) && is_array($this->manifest['i18n']) && !empty($this->manifest['i18n'])) {
298
      foreach ($this->manifest['i18n'] as $i18n_page_name => &$i18n_file_list) {
299
        foreach ($i18n_file_list as &$i18n_file_data) {
300
          if (is_array($i18n_file_data) && !$i18n_file_data['path']) {
301
            $i18n_file_data['path'] = $this->getRootRelative();
302
          }
303
        }
304
        if (!isset($sn_mvc['i18n'][$i18n_page_name])) {
305
          $sn_mvc['i18n'][$i18n_page_name] = array();
306
        }
307
        $sn_mvc['i18n'][$i18n_page_name] += $i18n_file_list;
308
      }
309
    }
310
311
    if (!empty($this->manifest['javascript']) && is_array($this->manifest['javascript'])) {
312
      foreach ($this->manifest['javascript'] as $javascript_page_name => &$javascript_list) {
313
        !isset($sn_mvc['javascript'][$javascript_page_name]) ? $sn_mvc['javascript'][$javascript_page_name] = array() : false;
314
        foreach ($javascript_list as $script_name => &$script_content) {
315
          $sn_mvc['javascript'][$javascript_page_name][$script_name] = $script_content;
316
        }
317
      }
318
    }
319
320
    if (!empty($this->manifest['css']) && is_array($this->manifest['css'])) {
321
      foreach ($this->manifest['css'] as $javascript_page_name => &$javascript_list) {
322
        !isset($sn_mvc['css'][$javascript_page_name]) ? $sn_mvc['css'][$javascript_page_name] = array() : false;
323
        foreach ($javascript_list as $script_name => &$script_content) {
324
          $sn_mvc['css'][$javascript_page_name][$script_name] = $script_content;
325
        }
326
      }
327
    }
328
329
    if (!empty($this->manifest['navbar_prefix_button']) && is_array($this->manifest['navbar_prefix_button'])) {
330
      foreach ($this->manifest['navbar_prefix_button'] as $button_image => $button_url_relative) {
331
        $sn_mvc['navbar_prefix_button'][$button_image] = $button_url_relative;
332
      }
333
    }
334
335
    if (!empty($this->manifest['navbar_main_button']) && is_array($this->manifest['navbar_main_button'])) {
336
      foreach ($this->manifest['navbar_main_button'] as $button_image => $button_url_relative) {
337
        $sn_mvc['navbar_main_button'][$button_image] = $button_url_relative;
338
      }
339
    }
340
  }
341
342
  public function check_status() {
343
  }
344
345
  /**
346
   * Checks if module is active
347
   *
348
   * @return bool
349
   */
350
  public function isActive() {
351
    return !empty($this->manifest['active']) && !empty($this->manifest['installed']);
352
  }
353
354
  /**
355
   * Register pages in $manifest['mvc']['pages'] for further use
356
   *
357
   * @param string[] $pages - array of records ['pageName' => 'pageFile']. 'pageFile' is currently unused
358
   *
359
   * @deprecated
360
   */
361
  protected function __mvcRegisterPagesOld($pages) {
362
    !is_array($this->manifest['mvc']['pages']) ? $this->manifest['mvc']['pages'] = [] : false;
363
    if (is_array($pages) && !empty($pages)) {
364
      $this->manifest['mvc']['pages'] = array_merge($this->manifest['mvc']['pages'], $pages);
365
    }
366
  }
367
368
  protected function registerHooks() {
369
    foreach ($this->hooks as $hookName => $hookRecord) {
370
      // Priority can be first element of hook array
371
      $priority = Pimp::ORDER_AS_IS;
372
      if (is_array($hookRecord) && count($hookRecord) > 1 && is_numeric(reset($hookRecord))) {
373
        $priority = intval(reset($hookRecord));
374
        array_shift($hookRecord);
375
      }
376
377
      // There is 2 elements in callable array
378
      if (is_array($hookRecord) && 2 == count($hookRecord)) {
379
        // Checking - if first should be replaced with $this
380
        if (THIS_STRING === reset($hookRecord)) {
381
          $hookRecord = [$this, end($hookRecord)];
382
        }
383
      }
384
385
      SN::$gc->pimp->register($hookName, $hookRecord, $priority);
386
    }
387
  }
388
389
  public function getLoadOrder() {
390
    return !empty($this->manifest[self::M_LOAD_ORDER]) ? $this->manifest[self::M_LOAD_ORDER] : MODULE_LOAD_ORDER_DEFAULT;
391
  }
392
393
  public function getRootRelative() {
394
    if (empty($this->manifest[static::M_ROOT_RELATIVE])) {
395
      $this->manifest[static::M_ROOT_RELATIVE] = str_replace([SN_ROOT_PHYSICAL, basename($this->filename)], '', str_replace('\\', '/', $this->filename));
396
    }
397
398
    return $this->manifest[static::M_ROOT_RELATIVE];
399
  }
400
401
  protected function getTemplateRootRelative() {
402
    return $this->getRootRelative() . 'design/templates/';
403
  }
404
405
  /**
406
   *
407
   * Should stay public due using in Festivals (?)
408
   *
409
   * @param string   $templateName
410
   * @param template $template
411
   *
412
   * @return template
413
   */
414
  public function addModuleTemplate($templateName, $template) {
415
    return gettemplate($templateName, $template, $this->getTemplateRootRelative());
416
  }
417
418
}
419