Completed
Push — master ( e9449e...f4f594 )
by Vitaly
36s
created

Core::composer()   C

Complexity

Conditions 8
Paths 24

Size

Total Lines 62
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 72

Importance

Changes 19
Bugs 4 Features 6
Metric Value
c 19
b 4
f 6
dl 0
loc 62
ccs 0
cts 15
cp 0
rs 6.943
cc 8
eloc 32
nc 24
nop 1
crap 72

How to fix   Long Method   

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
    /** @var string Current system environment */
48
    protected $environment;
49
50
    /**
51
     * Core constructor.
52
     *
53
     * @param ResourceMap|null $map system resources
54
     */
55
    public function __construct(ResourceMap $map = null)
56
    {
57
        if (!isset($map)) {
58
            // Get correct web-application path
59
            $this->system_path = __SAMSON_CWD__;
60
61
            // Get web-application resource map
62
            $this->map = ResourceMap::get($this->system_path, false, array('src/'));
63
        } else { // Use data from passed map
64
            $this->map = $map;
65
            $this->system_path = $map->entryPoint;
66
        }
67
68
        // Connect static collection with this dynamic field to avoid duplicates
69
        $this->module_stack = &Module::$instances;
70
71
        // Temporary add template worker
72
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
73
74
        $whoops = new \Whoops\Run;
75
        $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...
76
        $whoops->register();
77
78
        // Fire core creation event
79
        Event::fire('core.created', array(&$this));
80
81
        // Signal core configure event
82
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
83
    }
84
85
    /**
86
     * Generic wrap for Event system subscription.
87
     * @see \samson\core\\samsonphp\event\Event::subscribe()
88
     *
89
     * @param string   $key     Event identifier
90
     * @param callable $handler Event handler
91
     * @param array    $params  Event parameters
92
     *
93
     * @return $this Chaining
94
     */
95
    public function subscribe($key, $handler, $params = array())
96
    {
97
        Event::subscribe($key, $handler, $params);
98
99
        return $this;
100
    }
101
102
    /**
103
     * Change current system working environment or receive
104
     * current system enviroment if no arguments are passed.
105
     *
106
     * @param string $environment Environment identifier
107
     *
108
     * TODO: Function has two different logics - needs to be changed!
109
     * @return $this|string Chaining or current system environment
110 1
     */
111
    public function environment($environment = Scheme::BASE)
112
    {
113
        if (func_num_args() !== 0) {
114
            $this->environment = $environment;
115
116
            // Signal core environment change
117
            Event::signal('core.environment.change', array($environment, &$this));
118
            return $this;
119
        }
120
121 1
        return $this->environment;
122
    }
123
124
    /**
125
     * Generate special response header triggering caching mechanisms
126
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
127
     * @param string $accessibility Cache-control accessibility value(default public)
128
     */
129
    public function cached($cacheLife = 3600, $accessibility = 'public')
130
    {
131
        static $cached;
132
        // Protect sending cached headers once
133
        if (!isset($cached) or $cached !== true) {
134
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
135
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
136
            header('Pragma: cache');
137
138
            $cached = true;
139
        }
140
    }
141
142
    /**
143
     * Set asynchronous mode.
144
     * This mode will not output template and will just path everything that
145
     * was outputted to client.
146
     *
147
     * @param bool $async True to switch to asynchronous output mode
148
     *
149
     * @return $this Chaining
150
     */
151
    public function async($async)
152
    {
153
        $this->async = $async;
154
155
        return $this;
156
    }
157
158
    /** @see iCore::path() */
159
    public function path($path = null)
160
    {
161
        // Если передан аргумент
162
        if (func_num_args()) {
163
            // Сформируем новый относительный путь к главному шаблону системы
164
            $this->template_path = $path . $this->template_path;
165
166
            // Сохраним относительный путь к Веб-приложению
167
            $this->system_path = $path;
168
169
            // Продолжил цепирование
170
            return $this;
171
        }
172
173
        // Вернем текущее значение
174
        return $this->system_path;
175
    }
176
177
    /**    @see iModule::active() */
178
    public function &active(iModule &$module = null)
179
    {
180
        // Сохраним старый текущий модуль
181
        $old = &$this->active;
182
183
        // Если передано значение модуля для установки как текущий - проверим и установим его
184
        if (isset($module)) {
185
            $this->active = &$module;
186
        }
187
188
        // Вернем значение текущего модуля
189
        return $old;
190
    }
191
192
    /**
193
     * Retrieve module instance by identifier.
194
     *
195
     * @param string|null $module Module identifier
196
     *
197
     * @return null|Module Found or active module
198
     */
199
    public function &module($module = null)
200
    {
201
        $return = null;
202
203
        // Ничего не передано - вернем текущуй модуль системы
204
        if (!isset($module) && isset($this->active)) {
205
            $return = &$this->active;
206
        } elseif (is_object($module)) {
207
            $return = &$module;
208
        } elseif (is_string($module) && isset($this->module_stack[$module])) {
209
            $return = &$this->module_stack[$module];
210
        }
211
212
        // Ничего не получилось вернем ошибку
213
        if ($return === null) {
214
            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...
215
        }
216
217
        return $return;
218
    }
219
220
    /**
221
     * Unload module from core.
222
     *
223
     * @param string $moduleID Module identifier
224
     */
225
    public function unload($moduleID)
226
    {
227
        if (isset($this->module_stack[$moduleID])) {
228
            unset($this->module_stack[$moduleID]);
229
        }
230
    }
231
232
    /**
233
     * Insert generic html template tags and data
234
     *
235
     * @param string $templateHtml Generated HTML
236
     *
237
     * @deprecated Must be moved to a new HTML output object
238
     * @return mixed Changed HTML template
239
     */
240
    public function generateTemplate(&$templateHtml)
241
    {
242
        // Добавим путь к ресурсам для браузера
243
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
244
        // Добавим отметку времени для JavaScript
245
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
246
247
        // Добавим поддержку HTML для старых IE
248
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
249
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
250
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
251
        $headHtml .= "\n" . '<![endif]-->';
252
253
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
254
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
255
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
256
257
        // Вставим указатель JavaScript ресурсы в конец HTML документа
258
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
259
260
        return $templateHtml;
261
    }
262
263
    /**
264 2
     * Start SamsonPHP framework.
265
     *
266
     * @param string $default Default module identifier
267
     *
268
     * @throws ViewPathNotFound
269
     */
270
    public function start($default)
271 2
    {
272
        // TODO: Change ExternalModule::init() signature
273
        // Fire core started event
274 2
        Event::fire('core.started');
275
276
        // TODO: Does not see why it should be here
277
        // Set main template path
278 2
        $this->template($this->template_path);
279
280 2
        // Security layer
281
        $securityResult = true;
282 2
        // Fire core security event
283 2
        Event::fire('core.security', array(&$this, &$securityResult));
284
285
        /** @var mixed $result External route controller action result */
286 2
        $result = false;
287
288 2
        // If we have passed security application layer
289
        if ($securityResult) {
290 2
            // Fire core routing event - go to routing application layer
291
            Event::signal('core.routing', array(&$this, &$result, $default));
292
        }
293
294
        // If no one has passed back routing callback
295 2
        if (!isset($result) || $result === false) {
296
            // Fire core e404 - routing failed event
297
            $result = Event::signal('core.e404', array(url()->module, url()->method));
298
        }
299
300
        // Response
301
        $output = '';
302
303
        // If this is not asynchronous response and controller has been executed
304
        if (!$this->async && ($result !== false)) {
305
            // Store module data
306
            $data = $this->active->toView();
307
308
            // Render main template
309
            $output = $this->render($this->template_path, $data);
310
311
            // Fire after render event
312
            Event::fire('core.rendered', array(&$output, &$data, &$this->active));
313
        }
314
315
        // Output results to client
316
        echo $output;
317
318
        // Fire ended event
319
        Event::fire('core.ended', array(&$output));
320 2
    }
321
322
    /**	@see iCore::template() */
1 ignored issue
show
Coding Style introduced by
Spaces must be used for alignment; tabs are not allowed
Loading history...
323 2
    public function template( $template = NULL, $absolutePath = false )
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between opening bracket and argument "$template"; 1 found
Loading history...
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 argument "$absolutePath" and closing bracket; 1 found
Loading history...
324
    {
325
        // Если передан аргумент
326 2
        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...
327
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
328
        }
329 2
330
        // Аргументы не переданы - вернем текущий путь к шаблону системы
331
        return $this->template_path;
332 2
    }
333
334
    /**
335 2
     * Render file to a buffer.
336
     *
337
     * @param string $view Path to file
338 2
     * @param array  $data Collection of variables to path to file
339
     *
340
     * @return string Rendered file contents
341
     * @throws ViewPathNotFound
342
     */
343
    public function render($view, $data = array())
344
    {
345
        // TODO: Make rendering as external system, to split up these 3 rendering options
346
347
        // Объявить ассоциативный массив переменных в данном контексте
348
        if (is_array($data)) {
349
            extract($data);
350
        }
351
352 7
        // Начать вывод в буффер
353
        ob_start();
354 7
355
        // Path to another template view, by default we are using default template folder path,
356 7
        // for meeting first condition
357
        $templateView = $view;
358
359
        if (locale() != SamsonLocale::DEF) {
360
            // Modify standard view path with another template
361
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
362
        }
363
364
        // Depending on core view rendering model
365
        switch ($this->render_mode) {
366
            // Standard algorithm for view rendering
367
            case self::RENDER_STANDART:
368
                // Trying to find another template path, by default it's an default template path
369
                if (file_exists($templateView)) {
370
                    include($templateView);
371
                } elseif (file_exists($view)) {
372
                    // If another template wasn't found - we will use default template path
373
                    include($view);
374
                } else { // Error no template view was found
375
                    throw(new ViewPathNotFound($view));
376
                }
377
                break;
378
379
            // View rendering algorithm from array of view variables
380
            case self::RENDER_VARIABLE:
381
                // Collection of views
382
                $views = &$GLOBALS['__compressor_files'];
383
                // Trying to find another template path, by default it's an default template path
384
                if (isset($views[$templateView])) {
385
                    eval(' ?>' . $views[$templateView] . '<?php ');
386
                } elseif (isset($views[$view])) {
387
                    // If another template wasn't found - we will use default template path
388
                    eval(' ?>' . $views[$view] . '<?php ');
389
                } else { // Error no template view was found
390
                    throw(new ViewPathNotFound($view));
391
                }
392
                break;
393
        }
394
395
        // Получим данные из буффера вывода
396
        $html = ob_get_contents();
397
398
        // Очистим буффер
399
        ob_end_clean();
400
401
        // Fire core render event
402
        Event::fire('core.render', array(&$html, &$data, &$this->active));
403 2
404
        ////elapsed('End rendering '.$__view);
405
        return $html;
406 2
    }
407
408
    //[PHPCOMPRESSOR(remove,start)]
409 2
410 2
    /**
411 2
     * Load system from composer.json
412
     * @param string $dependencyFilePath Path to dependencies file
413
     * @return $this Chaining
414 2
     */
415
    public function composer($dependencyFilePath = null)
416
    {
417
        $composerModules = array();
418
419
        Event::fire(
420
            'core.composer.create',
421
            array(
422
                &$composerModules,
423
                isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path,
424
                array(
425
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/'),
426
                    'ignoreKey' => 'samson_module_ignore',
427
                    'includeKey' => 'samson_module_include'
428
                )
429
            )
430
        );
431
432
        // Iterate requirements
433
        foreach ($composerModules as $requirement => $parameters) {
434
            $this->load(__SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement,
435
                array_merge(
436
                    is_array($parameters) ? $parameters : array($parameters),
437
                    array('module_id' => $requirement)
438
                )
439
            );
440
        }
441
442
        $localModulesPath = '../src';
443
        ResourceMap::get('cache');
444
        // TODO: Nested modules relation
445
        for ($i = 0; $i < 2; $i++) {
446
            $resourceMap = ResourceMap::get($localModulesPath);
447
448
            foreach ($resourceMap->modules as $moduleFile) {
449
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
450
                $modulePath = explode('/', $modulePath);
451
                $modulePath = $localModulesPath . '/' . $modulePath[1];
452
                $this->load($modulePath, $parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters seems to be defined by a foreach iteration on line 433. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
453
            }
454
        }
455
456
        // Create local module and set it as active
457
        $this->active = new VirtualModule($this->system_path, $this->map, $this, 'local');
458
459
        // TODO: This should be changed to one single logic
460
        // Require all local module model files
461
        foreach ($this->map->models as $model) {
462
            // TODO: Why have to require once?
463
            require_once($model);
464
        }
465
466
        // Create all local modules
467
        foreach ($this->map->controllers as $controller) {
468
            // Require class into PHP
469
            require($controller);
470
471
            // Create module connector instance
472
            new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
473
        }
474
475
        return $this;
476
    }
477
478
    /**
479
     * Load module from path to core.
480
     *
481
     * @param string $path       Path for module loading
482
     * @param array  $parameters Collection of loading parameters
483
     *
484
     * @return $this|bool
485
     */
486
    public function load($path, $parameters = array())
487
    {
488
        // Check path
489
        if (file_exists($path)) {
490
            /** @var ResourceMap $resourceMap Gather all resources from path */
491
            $resourceMap = ResourceMap::get($path);
492
            if (isset($resourceMap->module[0])) {
493
494
                /** @var string $controllerPath Path to module controller file */
495
                $controllerPath = $resourceMap->module[1];
496
497
                /** @var string $moduleClass Name of module controller class to load */
498
                $moduleClass = $resourceMap->module[0];
499
500
                // Require module controller class into PHP
501
                if (file_exists($controllerPath)) {
502
                    require_once($controllerPath);
503
                }
504
505
                // TODO: this should be done via composer autoload file field
506
                // Iterate all function-style controllers and require them
507
                foreach ($resourceMap->controllers as $controller) {
508
                    require_once($controller);
509
                }
510
511
                /** @var ExternalModule $connector Create module controller instance */
512
                $connector = new $moduleClass($path, $resourceMap, $this);
513
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
514
                /** @var \samson\core\ExternalModule $connector Create module controller instance */
515
                $connector = new VirtualModule($path, $resourceMap, $this,
516
                    str_replace('/', '', $parameters['module_id']));
517
518
                // Set composer parameters
519
                $connector->composerParameters = $parameters;
520
            }
521
522
            if (isset($connector) && is_object($connector)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
523
524
                // Set composer parameters
525
                $connector->composerParameters = $parameters;
526
527
                // Get module identifier
528
                $moduleID = $connector->id();
529
530
                // Fire core module load event
531
                Event::fire('core.module_loaded', array($moduleID, &$connector));
532
533
                // Signal core module configure event
534
                Event::signal('core.module.configure', array(&$connector, $moduleID));
535
536
                // TODO: Think how to decouple this
537
                // Call module preparation handler
538
                if ( ! $connector->prepare()) {
539
                    // Handle module failed preparing
540
                }
541
542
                // Trying to find parent class for connecting to it to use View/Controller inheritance
543
                $parentClass = get_parent_class($connector);
544
                if ( ! in_array($parentClass,
545
                    array('samson\core\ExternalModule', 'samson\core\CompressableExternalModule'))
546
                ) {
547
                    // Переберем загруженные в систему модули
548
                    foreach ($this->module_stack as &$m) {
549
                        // Если в систему был загружен модуль с родительским классом
550
                        if (get_class($m) == $parentClass) {
551
                            $connector->parent = &$m;
552
                            //elapsed('Parent connection for '.$moduleClass.'('.$connector->uid.') with '.$parent_class.'('.$m->uid.')');
553
                        }
554
                    }
555
                }
556
            }
557
            else { // Signal error
558 7
                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...
559
            }
560
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
561 7
        } else {
562
            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...
563
        }
564 7
565
        // Chaining
566
        return $this;
567 7
    }
568
    //[PHPCOMPRESSOR(remove,end)]
569
570 7
    /** Магический метод для десериализации объекта */
571
    public function __wakeup()
572
    {
573 7
        $this->active = &$this->module_stack['local'];
574
    }
575
576 7
    /** Магический метод для сериализации объекта */
577 7
    public function __sleep()
578
    {
579
        return array('module_stack', 'render_mode');
580
    }
581
}
582