Completed
Push — trunk ( 34029c...0f5c9b )
by SuperNova.WS
03:54
created

sn_module   F

Complexity

Total Complexity 83

Size/Duplication

Total Lines 380
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 380
ccs 0
cts 175
cp 0
rs 1.5789
c 1
b 0
f 0
wmc 83

13 Methods

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

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
use Common\Hooker\Pimp;
4
5
class sn_module {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
6
  // Manifest params
7
  const M_REQUIRE = 'require';
8
  const M_ROOT_RELATIVE = 'root_relative';
9
  const M_LOAD_ORDER = 'load_order';
10
11
  /**
12
   * SN version in which module was committed. Can be treated as version in which module guaranteed to work
13
   * @var string $versionCommitted
14
   */
15
  public $versionCommitted = '#43a15.0#';
16
17
  public $manifest = [
18
    'package'   => 'core',
19
    'name'      => 'sn_module',
20
    'version'   => '1c0',
21
    'copyright' => 'Project "SuperNova.WS" #43a15.0# copyright © 2009-2017 Gorlum',
22
23
    self::M_LOAD_ORDER => MODULE_LOAD_ORDER_DEFAULT,
24
25
    self::M_REQUIRE       => [],
26
    self::M_ROOT_RELATIVE => '',
27
28
    'installed' => true,
29
    'active'    => true,
30
31
    // 'constants' array - contents of this array would be instaled into engine
32
    'constants' => [
33
//      '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...
34
    ],
35
36
    'vars'      => [], // Just a placeholder. vars assigned via special method __assign_vars(). Need 'cause of new constants that can be defined within module. See below
37
38
    // 'functions' array - this functions would be installed as hooks
39
    // Key: overwritable function name to replace
40
    // Value: which method to use. Format: [*][<object_name>][.]<method>
41
    // '*' means that new function would replace old one
42
    // If object_name is ommited but "." is present - hook linked to global function
43
    // If only "method" present - overwritable linked to appropriate method of current object
44
    // Function/Method should be accessible on module init
45
    'functions' => [
46
//      '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...
47
//      'test_function' => '.my_test_function',
48
//      'this_object_test_method' => 'test_method',
49
    ],
50
51
    // 'menu' array - this menu items would be merged into main game menu
52
    // Array element almost identical to $sn_menu with additional param 'LOCATION'.
53
    // '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...
54
    // [-|+][<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...
55
    // <menu_item_id> identifies menu item aginst new menu item would be placed. When ommited new item placed against whole menu
56
    // -/+ 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
57
    // Empty or non-existent LOCATION equivalent to '+' - place item at end of menu
58
    // Non-existent menu_item_id treated as ommited
59
    'menu'      => [],
60
61
    // 'page' array - defines pages which will handle this module and appropriate handlers
62
    'page'      => [],
63
  ];
64
65
  /**
66
   * New way to add functions instead of manifest['functions']
67
   *
68
   * [
69
   *   (string)$functionName => [
70
   *     (callable)$callable,
71
   *     (string)'methodName',                             // Local method name aka $this->methodName
72
   *     (callable array)[$this|objectName, 'methodName'], // Callable array
73
   *   ],
74
   * ]
75
   *
76
   * @var array $functions
77
   */
78
  protected $functions = [];
79
80
  protected $hooks = [];
81
82
  protected $config = array();
83
84
  protected $module_full_class_path = __FILE__;
85
86
  protected $filename = '';
87
88
  /**
89
   * @param string   $functionName
90
   * @param callable $callable
91
   */
92
  public function addFunctionHook($functionName, $callable) {
93
    $this->functions[$functionName][] = $callable;
94
  }
95
96
  /**
97
   * Динамическое назначение переменных
98
   *
99
   * Актуально, когда записываемые данные зависят от статуса игры
100
   * Например - назначаются константы внутри модуля
101
   *
102
   * @return array
103
   */
104
  protected function __assign_vars() {
105
    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...
106
      'sn_data' => array(
107
        UNIT_STRUCTURE_NEW => array( // Will honor new constants
108
          'name' => 'robot_factory',
109
          'type' => UNIT_STRUCTURE,
110
          'location' => LOC_PLANET,
111
          'cost' => array(
112
            RES_METAL     => 400,
113
            RES_CRYSTAL   => 120,
114
            RES_DEUTERIUM => 200,
115
            RES_ENERGY    => 0,
116
            'factor' => 2,
117
          ),
118
          'metal' => 400,
119
          'crystal' => 120,
120
          'deuterium' => 200,
121
          'energy' => 0,
122
          'factor' => 2,
123
        ),
124
      ),
125
*/
126
    );
127
  }
128
129
  /**
130
   * sn_module constructor.
131
   *
132
   * @param string $filename
133
   */
134
  public function __construct($filename = __FILE__) {
135
    $this->filename = $filename;
136
137
    // Getting module PHP class name
138
    $class_module_name = get_called_class();
139
140
    // Validating source settings. Some fields are mandatory in each manifest
141
    // Should be removed when manifest would be parsed to separate fields
142
    foreach (['name', 'package', 'installed', 'active', 'version'] as $mandatoryManifest) {
143
      if (!array_key_exists($mandatoryManifest, $this->manifest)) {
144
        throw new Exception('{ There is no mandatory field "' . $mandatoryManifest . '" in manifest of module } "' . $class_module_name . '"');
145
      }
146
    }
147
148
    // Getting module root relative to SN
149
    $this->manifest[static::M_ROOT_RELATIVE] = str_replace(
150
      [SN_ROOT_PHYSICAL, basename($this->filename)],
151
      '',
152
      str_replace('\\', '/', $this->filename)
153
    );
154
155
    // TODO: Load configuration from DB. Manifest setting
156
    // Trying to load configuration from file
157
    $config_exists = false;
158
    // Конфигурация может лежать в config_path в манифеста или в корне модуля
159
    if (isset($this->manifest['config_path']) && file_exists($config_filename = $this->manifest['config_path'] . '/config.php')) {
160
      $config_exists = true;
161
    } elseif (file_exists($config_filename = dirname($filename) . '/config.php')) {
162
      $config_exists = true;
163
    }
164
165
    if ($config_exists) {
166
      include($config_filename);
167
      $module_config_array = $class_module_name . '_config';
168
      $this->config = $$module_config_array;
169
    }
170
171
    // Registering classes with autoloader
172
    \Core\Autoloader::register($this->getRootRelative() . 'classes/');
173
174
    // TODO - currently not possible because each module is not a service
175
    // When it's done - remove double registration from loadModulesFromDirectory()
176
    // 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...
177
//    SN::$gc->modules->registerModule($class_module_name, $this);
178
  }
179
180
  protected function __patch_menu(&$sn_menu_extra, &$menu_patch) {
181
    if (isset($menu_patch) && is_array($menu_patch) && !empty($menu_patch)) {
182
      foreach ($menu_patch as $menu_item_name => $menu_item_data) {
183
        $sn_menu_extra[$menu_item_name] = $menu_item_data;
184
      }
185
    }
186
  }
187
188
189
  /**
190
   *
191
   */
192
  public function initialize() {
193
    // Checking module status - is it installed and active
194
    $this->check_status();
195
    if (!$this->manifest['active']) {
196
      return;
197
    }
198
199
    // Setting constants - if any
200
    if (isset($this->manifest['constants']) && is_array($this->manifest['constants']) && !empty($this->manifest['constants'])) {
201
      foreach ($this->manifest['constants'] as $constant_name => $constant_value) {
202
        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...
203
      }
204
    }
205
206
    // Adding vars - if any
207
    // Due to possible introduce of new constants in previous step vars is assigned via special method to honor new constants
208
    // Assignation can work with simple variables and with multidimensional arrays - for ex. 'sn_data[groups][test]'
209
    // New values from module variables will overwrite previous values (for root variables) and array elements with corresponding indexes (for arrays)
210
    // Constants as array indexes are honored - it's make valid such declarations as 'sn_data[ques][QUE_STRUCTURES]'
211
    $this->manifest['vars'] = $this->__assign_vars();
212
    if (!empty($this->manifest['vars'])) {
213
      $vars_assigned = array();
214
      foreach ($this->manifest['vars'] as $var_name => $var_value) {
215
        $sub_vars = explode('[', str_replace(']', '', $var_name));
216
        $var_name = $sub_vars[0];
217
218
        if (!isset($vars_assigned[$var_name])) {
219
          $vars_assigned[$var_name] = true;
220
          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...
221
        }
222
223
        $pointer = &$$var_name;
224
        if (($n = count($sub_vars)) > 1) {
225
          for ($i = 1; $i < $n; $i++) {
226
            if (defined($sub_vars[$i])) {
227
              $sub_vars[$i] = constant($sub_vars[$i]);
228
            }
229
230
            if (!isset($pointer[$sub_vars[$i]]) && $i != $n) {
231
              $pointer[$sub_vars[$i]] = array();
232
            }
233
            $pointer = &$pointer[$sub_vars[$i]];
234
          }
235
        }
236
237
        if (!isset($pointer) || !is_array($pointer)) {
238
          $pointer = $var_value;
239
        } elseif (is_array($$var_name)) {
240
          $pointer = array_merge_recursive_numeric($pointer, $var_value);
241
        }
242
      }
243
    }
244
245
    // Overriding function if any
246
    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...
247
    sn_sys_handler_add($functions, $this->manifest['functions'], $this);
248
249
    foreach ($this->functions as $functionName => $callableList) {
250
      !is_array($callableList) ? $callableList = [$callableList] : false;
251
      foreach ($callableList as $callable) {
252
        sys_handler_add_one($functions, $functionName, $callable, static::class, '');
253
      }
254
    }
255
256
    $this->registerHooks();
257
258
    // Patching game menu - if any
259
    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...
260
    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...
261
    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...
262
263
    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...
264
    foreach ($sn_mvc as $handler_type => &$handler_data) {
265
      sn_sys_handler_add($handler_data, $this->manifest['mvc'][$handler_type], $this, $handler_type);
266
    }
267
268
    if (isset($this->manifest['i18n']) && is_array($this->manifest['i18n']) && !empty($this->manifest['i18n'])) {
269
      foreach ($this->manifest['i18n'] as $i18n_page_name => &$i18n_file_list) {
270
        foreach ($i18n_file_list as &$i18n_file_data) {
271
          if (is_array($i18n_file_data) && !$i18n_file_data['path']) {
272
            $i18n_file_data['path'] = $this->getRootRelative();
273
          }
274
        }
275
        if (!isset($sn_mvc['i18n'][$i18n_page_name])) {
276
          $sn_mvc['i18n'][$i18n_page_name] = array();
277
        }
278
        $sn_mvc['i18n'][$i18n_page_name] += $i18n_file_list;
279
      }
280
    }
281
282
    if (!empty($this->manifest['javascript']) && is_array($this->manifest['javascript'])) {
283
      foreach ($this->manifest['javascript'] as $javascript_page_name => &$javascript_list) {
284
        !isset($sn_mvc['javascript'][$javascript_page_name]) ? $sn_mvc['javascript'][$javascript_page_name] = array() : false;
285
        foreach ($javascript_list as $script_name => &$script_content) {
286
          $sn_mvc['javascript'][$javascript_page_name][$script_name] = $script_content;
287
        }
288
      }
289
    }
290
291
    if (!empty($this->manifest['css']) && is_array($this->manifest['css'])) {
292
      foreach ($this->manifest['css'] as $javascript_page_name => &$javascript_list) {
293
        !isset($sn_mvc['css'][$javascript_page_name]) ? $sn_mvc['css'][$javascript_page_name] = array() : false;
294
        foreach ($javascript_list as $script_name => &$script_content) {
295
          $sn_mvc['css'][$javascript_page_name][$script_name] = $script_content;
296
        }
297
      }
298
    }
299
300
    if (!empty($this->manifest['navbar_prefix_button']) && is_array($this->manifest['navbar_prefix_button'])) {
301
      foreach ($this->manifest['navbar_prefix_button'] as $button_image => $button_url_relative) {
302
        $sn_mvc['navbar_prefix_button'][$button_image] = $button_url_relative;
303
      }
304
    }
305
306
    if (!empty($this->manifest['navbar_main_button']) && is_array($this->manifest['navbar_main_button'])) {
307
      foreach ($this->manifest['navbar_main_button'] as $button_image => $button_url_relative) {
308
        $sn_mvc['navbar_main_button'][$button_image] = $button_url_relative;
309
      }
310
    }
311
  }
312
313
  public function check_status() {
314
  }
315
316
  /**
317
   * Checks if module is active
318
   *
319
   * @return bool
320
   */
321
  public function isActive() {
322
    return !empty($this->manifest['active']) && !empty($this->manifest['installed']);
323
  }
324
325
  /**
326
   * Register pages in $manifest['mvc']['pages'] for further use
327
   *
328
   * @param string[] $pages - array of records ['pageName' => 'pageFile']. 'pageFile' is currently unused
329
   */
330
  protected function __mvcRegisterPagesOld($pages) {
331
    !is_array($this->manifest['mvc']['pages']) ? $this->manifest['mvc']['pages'] = array() : false;
332
    if (is_array($pages) && !empty($pages)) {
333
      $this->manifest['mvc']['pages'] = array_merge($this->manifest['mvc']['pages'], $pages);
334
    }
335
  }
336
337
  protected function registerHooks() {
338
    foreach ($this->hooks as $hookName => $hookRecord) {
339
      // Priority can be first element of hook array
340
      $priority = Pimp::ORDER_AS_IS;
341
      if (is_array($hookRecord) && count($hookRecord) > 1 && is_numeric(reset($hookRecord))) {
342
        $priority = intval(reset($hookRecord));
343
        array_shift($hookRecord);
344
      }
345
346
      // There is 2 elements in callable array
347
      if (is_array($hookRecord) && 2 == count($hookRecord)) {
348
        // Checking - if first should be replaced with $this
349
        if (THIS_STRING === reset($hookRecord)) {
350
          $hookRecord = [$this, end($hookRecord)];
351
        }
352
      }
353
354
      SN::$gc->pimp->register($hookName, $hookRecord, $priority);
355
    }
356
  }
357
358
  public function getLoadOrder() {
359
    return !empty($this->manifest[self::M_LOAD_ORDER]) ? $this->manifest[self::M_LOAD_ORDER] : MODULE_LOAD_ORDER_DEFAULT;
360
  }
361
362
  public function getRootRelative() {
363
    if (empty($this->manifest[static::M_ROOT_RELATIVE])) {
364
      $this->manifest[static::M_ROOT_RELATIVE] = str_replace([SN_ROOT_PHYSICAL, basename($this->filename)], '', str_replace('\\', '/', $this->filename));
365
    }
366
367
    return $this->manifest[static::M_ROOT_RELATIVE];
368
  }
369
370
  protected function getTemplateRootRelative() {
371
    return $this->getRootRelative() . 'design/templates/';
372
  }
373
374
  /**
375
   *
376
   * Should stay public due using in Festivals (?)
377
   *
378
   * @param string   $templateName
379
   * @param template $template
380
   *
381
   * @return template
382
   */
383
  public function addModuleTemplate($templateName, $template) {
384
    return gettemplate($templateName, $template, $this->getTemplateRootRelative());
385
  }
386
387
}
388