Completed
Push — master ( 520f8e...705d06 )
by Nikita
07:05
created

Core::load()   D

Complexity

Conditions 13
Paths 21

Size

Total Lines 82
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 159.9651

Importance

Changes 25
Bugs 7 Features 10
Metric Value
c 25
b 7
f 10
dl 0
loc 82
ccs 1
cts 22
cp 0.0455
rs 4.9922
cc 13
eloc 32
nc 21
nop 2
crap 159.9651

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * This file is part of the SamsonPHP\Core package.
4
 * (c) 2013 Vitaly Iegorov <[email protected]>
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
namespace samson\core;
10
11
use samsonframework\core\SystemInterface;
12
use samsonframework\resource\ResourceMap;
13
use samsonphp\config\Scheme;
14
use samsonphp\core\exception\ViewPathNotFound;
15
use samsonphp\event\Event;
16
17
/**
18
 * Core of SamsonPHP
19
 * 
20
 * @package SamsonPHP
21
 * @author 	Vitaly Iegorov <[email protected]>
1 ignored issue
show
Coding Style introduced by
Spaces must be used for alignment; tabs are not allowed
Loading history...
22
 * @version @version@
23
 */
24
class Core implements SystemInterface
25
{
26
    /* Rendering models */
27
    /** Standard algorithm for view rendering */
28
    const RENDER_STANDART = 1;
29
    /** View rendering algorithm from array of view variables */
30
    const RENDER_VARIABLE = 3;
31
32
    /** @var  ResourceMap Current web-application resource map */
33
    public $map;
34
35
    /** @var Module[] Collection of loaded modules */
36
    public $module_stack = array();
37
    /** @var string Path to current web-application */
38
    public $system_path = __SAMSON_CWD__;
39
    /** @var string View path loading mode */
40
    public $render_mode = self::RENDER_STANDART;
41
    /** @var Module Pointer to current active module */
42
    protected $active = null;
43
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
44
    protected $async = false;
45
    /** @var string Path to main system template */
46
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
47
48
    /**
49
     * Core constructor.
50
     *
51
     * @param ResourceMap|null $map system resources
52
     */
53
    public function __construct(ResourceMap $map = null)
54
    {
55
        if (!isset($map)) {
56
            // Get correct web-application path
57
            $this->system_path = __SAMSON_CWD__;
58
59
            // Get web-application resource map
60
            $this->map = ResourceMap::get($this->system_path, false, array('src/'));
61
        } else { // Use data from passed map
62
            $this->map = $map;
63
            $this->system_path = $map->entryPoint;
64
        }
65
66
        // Connect static collection with this dynamic field to avoid duplicates
67
        $this->module_stack = &Module::$instances;
68
69
        // Temporary add template worker
70
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
71
72
        $whoops = new \Whoops\Run;
73
        $whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
0 ignored issues
show
Documentation introduced by
new \Whoops\Handler\PrettyPageHandler() is of type object<Whoops\Handler\PrettyPageHandler>, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
74
        $whoops->register();
75
76
        // Fire core creation event
77
        Event::fire('core.created', array(&$this));
78
79
        // Signal core configure event
80
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
81
    }
82
83
    /**
84
     * Generic wrap for Event system subscription.
85
     * @see \samson\core\\samsonphp\event\Event::subscribe()
86
     *
87
     * @param string   $key     Event identifier
88
     * @param callable $handler Event handler
89
     * @param array    $params  Event parameters
90
     *
91
     * @return $this Chaining
92
     */
93
    public function subscribe($key, $handler, $params = array())
94
    {
95
        Event::subscribe($key, $handler, $params);
96
97
        return $this;
98
    }
99
100
    /**
101
     * Change current system working environment.
102
     *
103
     * @param string $environment Environment identifier
104
     *
105
*@return self Chaining
106
     */
107
    public function environment($environment = Scheme::BASE)
108
    {
109
        // Signal core environment change
110 1
        Event::signal('core.environment.change', array($environment, &$this));
111
112
        return $this;
113
    }
114
115
    /**
116
     * Generate special response header triggering caching mechanisms
117
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
118
     * @param string $accessibility Cache-control accessibility value(default public)
119
     */
120
    public function cached($cacheLife = 3600, $accessibility = 'public')
121 1
    {
122
        static $cached;
123
        // Protect sending cached headers once
124
        if (!isset($cached) or $cached !== true) {
125
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
126
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
127
            header('Pragma: cache');
128
129
            $cached = true;
130
        }
131
    }
132
133
    /**
134
     * Set asynchronous mode.
135
     * This mode will not output template and will just path everything that
136
     * was outputted to client.
137
     *
138
     * @param bool $async True to switch to asynchronous output mode
139
     *
140
     * @return $this Chaining
141
     */
142
    public function async($async)
143
    {
144
        $this->async = $async;
145
146
        return $this;
147
    }
148
149
    /** @see iCore::path() */
150
    public function path($path = null)
151
    {
152
        // Если передан аргумент
153
        if (func_num_args()) {
154
            // Сформируем новый относительный путь к главному шаблону системы
155
            $this->template_path = $path . $this->template_path;
156
157
            // Сохраним относительный путь к Веб-приложению
158
            $this->system_path = $path;
159
160
            // Продолжил цепирование
161
            return $this;
162
        }
163
164
        // Вернем текущее значение
165
        return $this->system_path;
166
    }
167
168
    /**    @see iModule::active() */
169
    public function &active(iModule &$module = null)
170
    {
171
        // Сохраним старый текущий модуль
172
        $old = &$this->active;
173
174
        // Если передано значение модуля для установки как текущий - проверим и установим его
175
        if (isset($module)) {
176
            $this->active = &$module;
177
        }
178
179
        // Вернем значение текущего модуля
180
        return $old;
181
    }
182
183
    /**
184
     * Retrieve module instance by identifier.
185
     *
186
     * @param string|null $module Module identifier
187
     *
188
     * @return null|Module Found or active module
189
     */
190
    public function &module($module = null)
191
    {
192
        $return = null;
193
194
        // Ничего не передано - вернем текущуй модуль системы
195
        if (!isset($module) && isset($this->active)) {
196
            $return = &$this->active;
197
        } elseif (is_object($module)) {
198
            $return = &$module;
199
        } elseif (is_string($module) && isset($this->module_stack[$module])) {
200
            $return = &$this->module_stack[$module];
201
        }
202
203
        // Ничего не получилось вернем ошибку
204
        if ($return === null) {
205
            e('Не возможно получить модуль(##) системы', E_SAMSON_CORE_ERROR, array($module));
0 ignored issues
show
Deprecated Code introduced by
The function e() has been deprecated with message: Use custom exceptions

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
206
        }
207
208
        return $return;
209
    }
210
211
    /**
212
     * Unload module from core.
213
     *
214
     * @param string $moduleID Module identifier
215
     */
216
    public function unload($moduleID)
217
    {
218
        if (isset($this->module_stack[$moduleID])) {
219
            unset($this->module_stack[$moduleID]);
220
        }
221
    }
222
223
    /**
224
     * Insert generic html template tags and data
225
     *
226
     * @param string $templateHtml Generated HTML
227
     *
228
     * @deprecated Must be moved to a new HTML output object
229
     * @return mixed Changed HTML template
230
     */
231
    public function generateTemplate(&$templateHtml)
232
    {
233
        // Добавим путь к ресурсам для браузера
234
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
235
        // Добавим отметку времени для JavaScript
236
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
237
238
        // Добавим поддержку HTML для старых IE
239
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
240
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
241
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
242
        $headHtml .= "\n" . '<![endif]-->';
243
244
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
245
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
246
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
247
248
        // Вставим указатель JavaScript ресурсы в конец HTML документа
249
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
250
251
        return $templateHtml;
252
    }
253
254
    /**
255
     * Start SamsonPHP framework.
256
     *
257
     * @param string $default Default module identifier
258
     *
259
     * @throws ViewPathNotFound
260
     */
261
    public function start($default)
262
    {
263
        // TODO: Change ExternalModule::init() signature
264 2
        // Fire core started event
265
        Event::fire('core.started');
266
267
        // TODO: Does not see why it should be here
268
        // Set main template path
269
        $this->template($this->template_path);
270
271 2
        // Security layer
272
        $securityResult = true;
273
        // Fire core security event
274 2
        Event::fire('core.security', array(&$this, &$securityResult));
275
276
        /** @var mixed $result External route controller action result */
277
        $result = false;
278 2
279
        // If we have passed security application layer
280 2
        if ($securityResult) {
281
            // Fire core routing event - go to routing application layer
282 2
            Event::signal('core.routing', array(&$this, &$result, $default));
283 2
        }
284
285
        // If no one has passed back routing callback
286 2
        if (!isset($result) || $result === false) {
287
            // Fire core e404 - routing failed event
288 2
            $result = Event::signal('core.e404', array(url()->module, url()->method));
289
        }
290 2
291
        // Response
292
        $output = '';
293
294
        // If this is not asynchronous response and controller has been executed
295 2
        if (!$this->async && ($result !== false)) {
296
            // Store module data
297
            $data = $this->active->toView();
298
299
            // Render main template
300
            $output = $this->render($this->template_path, $data);
301
302
            // Fire after render event
303
            Event::fire('core.rendered', array(&$output, &$data, &$this->active));
304
        }
305
306
        // Output results to client
307
        echo $output;
308
309
        // Fire ended event
310
        Event::fire('core.ended', array(&$output));
311
    }
312
313
    /**	@see iCore::template() */
1 ignored issue
show
Coding Style introduced by
Spaces must be used for alignment; tabs are not allowed
Loading history...
314
    public function template( $template = NULL, $absolutePath = false )
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
Coding Style introduced by
Expected 0 spaces between opening bracket and argument "$template"; 1 found
Loading history...
Coding Style introduced by
Expected 0 spaces between argument "$absolutePath" and closing bracket; 1 found
Loading history...
315
    {
316
        // Если передан аргумент
317
        if( func_num_args() ){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
Coding Style introduced by
Expected 0 spaces before closing bracket; 1 found
Loading history...
318
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
319
        }
320 2
321
        // Аргументы не переданы - вернем текущий путь к шаблону системы
322
        return $this->template_path;
323 2
    }
324
325
    /**
326 2
     * Render file to a buffer.
327
     *
328
     * @param string $view Path to file
329 2
     * @param array  $data Collection of variables to path to file
330
     *
331
     * @return string Rendered file contents
332 2
     * @throws ViewPathNotFound
333
     */
334
    public function render($view, $data = array())
335 2
    {
336
        // TODO: Make rendering as external system, to split up these 3 rendering options
337
338 2
        // Объявить ассоциативный массив переменных в данном контексте
339
        if (is_array($data)) {
340
            extract($data);
341
        }
342
343
        // Начать вывод в буффер
344
        ob_start();
345
346
        // Path to another template view, by default we are using default template folder path,
347
        // for meeting first condition
348
        $templateView = $view;
349
350
        if (locale() != SamsonLocale::DEF) {
351
            // Modify standard view path with another template
352 7
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
353
        }
354 7
355
        // Depending on core view rendering model
356 7
        switch ($this->render_mode) {
357
            // Standard algorithm for view rendering
358
            case self::RENDER_STANDART:
359
                // Trying to find another template path, by default it's an default template path
360
                if (file_exists($templateView)) {
361
                    include($templateView);
362
                } elseif (file_exists($view)) {
363
                    // If another template wasn't found - we will use default template path
364
                    include($view);
365
                } else { // Error no template view was found
366
                    throw(new ViewPathNotFound($view));
367
                }
368
                break;
369
370
            // View rendering algorithm from array of view variables
371
            case self::RENDER_VARIABLE:
372
                // Collection of views
373
                $views = &$GLOBALS['__compressor_files'];
374
                // Trying to find another template path, by default it's an default template path
375
                if (isset($views[$templateView])) {
376
                    eval(' ?>' . $views[$templateView] . '<?php ');
377
                } elseif (isset($views[$view])) {
378
                    // If another template wasn't found - we will use default template path
379
                    eval(' ?>' . $views[$view] . '<?php ');
380
                } else { // Error no template view was found
381
                    throw(new ViewPathNotFound($view));
382
                }
383
                break;
384
        }
385
386
        // Получим данные из буффера вывода
387
        $html = ob_get_contents();
388
389
        // Очистим буффер
390
        ob_end_clean();
391
392
        // Fire core render event
393
        Event::fire('core.render', array(&$html, &$data, &$this->active));
394
395
        ////elapsed('End rendering '.$__view);
396
        return $html;
397
    }
398
399
    //[PHPCOMPRESSOR(remove,start)]
400
401
    /**
402
     * Load system from composer.json
403 2
     * @param string $dependencyFilePath Path to dependencies file
404
     * @return $this Chaining
405
     */
406 2
    public function composer($dependencyFilePath = null)
407
    {
408
        $composerModules = array();
409 2
410 2
        Event::fire(
411 2
            'core.composer.create',
412
            array(
413
                &$composerModules,
414 2
                isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path,
415
                array(
416
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/'),
417
                    'ignoreKey' => 'samson_module_ignore',
418
                    'includeKey' => 'samson_module_include'
419
                )
420
            )
421
        );
422
423
        $modulesToLoad = array();
424
425
        // Iterate requirements
426
        foreach ($composerModules as $requirement => $parameters) {
427
            $modulesToLoad[__SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement] = array_merge(
428
                is_array($parameters) ? $parameters : array($parameters),
429
                array('module_id' => $requirement)
430
            );
431
        }
432
433
        $localModulesPath = '../src';
434
435
        $resourceMap = ResourceMap::get($localModulesPath);
436
437
        foreach ($resourceMap->modules as $moduleFile) {
438
            $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
439
            $modulePath = explode('/', $modulePath);
440
            $modulePath = $localModulesPath.'/'.$modulePath[1];
441
            $modulesToLoad[$modulePath] = array();
442
        }
443
444
        foreach($modulesToLoad as $path => $parameters) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FOREACH keyword; 0 found
Loading history...
445
            $this->load($path, $parameters);
446
        }
447
448
        // Create local module and set it as active
449
        $this->active = new CompressableLocalModule('local', $this->system_path, $this->map, $this);
450
451
        // TODO: This should be changed to one single logic
452
        // Require all local module model files
453
        foreach ($this->map->models as $model) {
454
            // TODO: Why have to require once?
455
            require_once($model);
456
        }
457
458
        // Create all local modules
459
        foreach ($this->map->controllers as $controller) {
460
            // Require class into PHP
461
            require($controller);
462
463
            // Create module connector instance
464
            new CompressableLocalModule(basename($controller, '.php'), $this->system_path, $this->map, $this);
465
        }
466
467
        return $this;
468
    }
469
470
    /**
471
     * Load module from path to core.
472
     *
473
     * @param string $path       Path for module loading
474
     * @param array  $parameters Collection of loading parameters
475
     *
476
     * @return $this|bool
477
     */
478
    public function load($path, $parameters = array())
479
    {
480
        // Check path
481
        if (file_exists($path)) {
482
            /** @var ResourceMap $resourceMap Gather all resources from path */
483
            $resourceMap = ResourceMap::get($path);
484
            if (isset($resourceMap->module[0])) {
485
486
                /** @var string $controllerPath Path to module controller file */
487
                $controllerPath = $resourceMap->module[1];
488
489
                /** @var string $moduleClass Name of module controller class to load */
490
                $moduleClass = $resourceMap->module[0];
491
492
                // Require module controller class into PHP
493
                if (file_exists($controllerPath)) {
494
                    require_once($controllerPath);
495
                }
496
497
                // TODO: this should be done via composer autoload file field
498
                // Iterate all function-style controllers and require them
499
                foreach ($resourceMap->controllers as $controller) {
500
                    require_once($controller);
501
                }
502
503
                /** @var ExternalModule $connector Create module controller instance */
504
                $connector = new $moduleClass($path, $resourceMap, $this);
505
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
506
                /** @var \samson\core\ExternalModule $connector Create module controller instance */
507
                $connector = new VirtualModule($path, $resourceMap, $this,
508
                    str_replace('/', '', $parameters['module_id']));
509
510
                // Set composer parameters
511
                $connector->composerParameters = $parameters;
512
            }
513
514
            if (is_object($connector)) {
515
516
                // Set composer parameters
517
                $connector->composerParameters = $parameters;
0 ignored issues
show
Bug introduced by
The variable $connector does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
518
519
                // Get module identifier
520
                $moduleID = $connector->id();
521
522
                // Fire core module load event
523
                Event::fire('core.module_loaded', array($moduleID, &$connector));
524
525
                // Signal core module configure event
526
                Event::signal('core.module.configure', array(&$connector, $moduleID));
527
528
                // TODO: Think how to decouple this
529
                // Call module preparation handler
530
                if ( ! $connector->prepare()) {
531
                    // Handle module failed preparing
532
                }
533
534
                // Trying to find parent class for connecting to it to use View/Controller inheritance
535
                $parentClass = get_parent_class($connector);
536
                if ( ! in_array($parentClass,
537
                    array('samson\core\ExternalModule', 'samson\core\CompressableExternalModule'))
538
                ) {
539
                    // Переберем загруженные в систему модули
540
                    foreach ($this->module_stack as &$m) {
541
                        // Если в систему был загружен модуль с родительским классом
542
                        if (get_class($m) == $parentClass) {
543
                            $connector->parent = &$m;
544
                            //elapsed('Parent connection for '.$moduleClass.'('.$connector->uid.') with '.$parent_class.'('.$m->uid.')');
545
                        }
546
                    }
547
                }
548
            }
549
            else { // Signal error
550
                return e('Cannot load module from: "##"', E_SAMSON_FATAL_ERROR, $path);
0 ignored issues
show
Deprecated Code introduced by
The function e() has been deprecated with message: Use custom exceptions

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
551
            }
552
553
        } else {
554
            return e('Cannot load module from[##]', E_SAMSON_FATAL_ERROR, $path);
0 ignored issues
show
Deprecated Code introduced by
The function e() has been deprecated with message: Use custom exceptions

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
555
        }
556
557
        // Chaining
558 7
        return $this;
559
    }
560
    //[PHPCOMPRESSOR(remove,end)]
561 7
562
    /** Магический метод для десериализации объекта */
563
    public function __wakeup()
564 7
    {
565
        $this->active = &$this->module_stack['local'];
566
    }
567 7
568
    /** Магический метод для сериализации объекта */
569
    public function __sleep()
570 7
    {
571
        return array('module_stack', 'render_mode');
572
    }
573
}
574