Completed
Push — master ( 5df87c...d73adc )
by Vitaly
08:09
created

Core::load()   C

Complexity

Conditions 12
Paths 11

Size

Total Lines 75
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 21
Bugs 5 Features 9
Metric Value
c 21
b 5
f 9
dl 0
loc 75
ccs 0
cts 19
cp 0
rs 5.3413
cc 12
eloc 29
nc 11
nop 2
crap 156

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]>
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
    const EVENT_CREATED = 'core.created';
33
    const EVENT_CONFIGURE = 'core.configure';
34
    const EVENT_MODULE_LOADED = 'core.module.loaded';
35
    const EVENT_MODULE_CONFIGURE = 'core.module.configure';
36
    const EVENT_START = 'core.started';
37
    const EVENT_SECURITY = 'core.security';
38
    const EVENT_ROUTING = 'core.routing';
39
    const EVENT_E404 = 'core.e404';
40
    const EVENT_RENDER = 'core.render';
41
    const EVENT_RENDERED = 'core.rendered';
42
    const EVENT_END = 'core.end';
43
44
    /** @var  ResourceMap Current web-application resource map */
45
    public $map;
46
47
    /** @var Module[] Collection of loaded modules */
48
    public $module_stack = array();
49
    /** @var string Path to current web-application */
50
    public $system_path = __SAMSON_CWD__;
51
    /** @var string View path loading mode */
52
    public $render_mode = self::RENDER_STANDART;
53
    /** @var Module Pointer to current active module */
54
    protected $active = null;
55
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
56
    protected $async = false;
57
    /** @var string Path to main system template */
58
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
59
60
    /**
61
     * Core constructor.
62
     *
63
     * @param ResourceMap|null $map system resources
64
     */
65
    public function __construct(ResourceMap $map = null)
66
    {
67
        if (!isset($map)) {
68
            // Get correct web-application path
69
            $this->system_path = __SAMSON_CWD__;
70
71
            // Get web-application resource map
72
            $this->map = ResourceMap::get($this->system_path, false, array('src/'));
73
        } else { // Use data from passed map
74
            $this->map = $map;
75
            $this->system_path = $map->entryPoint;
76
        }
77
78
        // Connect static collection with this dynamic field to avoid duplicates
79
        $this->module_stack = &Module::$instances;
80
81
        // Temporary add template worker
82
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
83
84
        // Fire core creation event
85
        Event::fire('core.created', array(&$this));
86
87
        // Signal core configure event
88
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
89
    }
90
91
    /**
92
     * Generic wrap for Event system subscription.
93
     * @see \samson\core\\samsonphp\event\Event::subscribe()
94
     *
95
     * @param string   $key     Event identifier
96
     * @param callable $handler Event handler
97
     * @param array    $params  Event parameters
98
     *
99
     * @return $this Chaining
100
     */
101
    public function subscribe($key, $handler, $params = array())
102
    {
103
        Event::subscribe($key, $handler, $params);
104
105
        return $this;
106
    }
107
108
    /**
109
     * Change current system working environment.
110 1
     *
111
     * @param string $environment Environment identifier
112
     *
113
     * @return self Chaining
114
     */
115
    public function environment($environment = Scheme::BASE)
116
    {
117
        // Signal core environment change
118
        Event::signal('core.environment.change', array($environment, &$this));
119
120
        return $this;
121 1
    }
122
123
    /**
124
     * Generate special response header triggering caching mechanisms
125
     *
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));
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() */
323 2
    public function template($template = null)
324
    {
325
        // Если передан аргумент
326 2
        if (func_num_args()) {
327
            $this->template_path = $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
     *
413
     * @param string $dependencyFilePath Path to dependencies file
414 2
     *
415
     * @return $this Chaining
416
     */
417
    public function composer($dependencyFilePath = null)
418
    {
419
        $composerModules = array();
420
421
        Event::fire(
422
            'core.composer.create',
423
            array(
424
                &$composerModules,
425
                isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path,
426
                array(
427
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/'),
428
                    'ignoreKey' => 'samson_module_ignore',
429
                    'includeKey' => 'samson_module_include'
430
                )
431
            )
432
        );
433
434
        // Iterate requirements
435
        foreach ($composerModules as $requirement => $parameters) {
436
            // Load module
437
            $this->load(
438
                __SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement,
439
                array_merge(
440
                    is_array($parameters) ? $parameters : array($parameters),
441
                    array('module_id' => $requirement)
442
                )
443
            );
444
        }
445
446
        // Create local module and set it as active
447
        $this->active = new CompressableLocalModule('local', $this->system_path, $this->map, $this);
448
449
        // TODO: This should be changed to one single logic
450
        // Require all local module model files
451
        foreach ($this->map->models as $model) {
452
            // TODO: Why have to require once?
453
            require_once($model);
454
        }
455
456
        // Create all local modules
457
        foreach ($this->map->controllers as $controller) {
458
            // Require class into PHP
459
            require($controller);
460
461
            // Create module connector instance
462
            new CompressableLocalModule(basename($controller, '.php'), $this->system_path, $this->map, $this);
463
        }
464
465
        return $this;
466
    }
467
468
    /**
469
     * Load module from path to core.
470
     *
471
     * @param string $path       Path for module loading
472
     * @param array  $parameters Collection of loading parameters
473
     *
474
     * @return $this|bool
475
     */
476
    public function load($path, $parameters = array())
477
    {
478
        // Check path
479
        if (file_exists($path)) {
480
            /** @var ResourceMap $resourceMap Gather all resources from path */
481
            $resourceMap = ResourceMap::get($path);
482
            if (isset($resourceMap->module[0])) {
483
484
                /** @var string $controllerPath Path to module controller file */
485
                $controllerPath = $resourceMap->module[1];
486
487
                /** @var string $moduleClass Name of module controller class to load */
488
                $moduleClass = $resourceMap->module[0];
489
490
                // Require module controller class into PHP
491
                if (file_exists($controllerPath)) {
492
                    require_once($controllerPath);
493
                }
494
495
                // TODO: this should be done via composer autoload file field
496
                // Iterate all function-style controllers and require them
497
                foreach ($resourceMap->controllers as $controller) {
498
                    require_once($controller);
499
                }
500
501
                /** @var ExternalModule $connector Create module controller instance */
502
                $connector = new $moduleClass($path, $resourceMap, $this);
503
504
                // Set composer parameters
505
                $connector->composerParameters = $parameters;
506
507
                // Get module identifier
508
                $moduleID = $connector->id();
509
510
                // Fire core module load event
511
                Event::fire('core.module_loaded', array($moduleID, &$connector));
512
513
                // Signal core module configure event
514
                Event::signal('core.module.configure', array(&$connector, $moduleID));
515
516
                // TODO: Think how to decouple this
517
                // Call module preparation handler
518
                if (!$connector->prepare()) {
519
                    // Handle module failed preparing
520
                }
521
522
                // Trying to find parent class for connecting to it to use View/Controller inheritance
523
                $parentClass = get_parent_class($connector);
524
                if (!in_array($parentClass, array(AutoLoader::className('samson\core\ExternalModule'), AutoLoader::className('samson\core\CompressableExternalModule')))) {
525
                    // Переберем загруженные в систему модули
526
                    foreach ($this->module_stack as &$m) {
527
                        // Если в систему был загружен модуль с родительским классом
528
                        if (get_class($m) == $parentClass) {
529
                            $connector->parent = &$m;
530
                            //elapsed('Parent connection for '.$moduleClass.'('.$connector->uid.') with '.$parent_class.'('.$m->uid.')');
531
                        }
532
                    }
533
                }
534
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
535
                /** @var \samson\core\ExternalModule $connector Create module controller instance */
536
                $connector = new CompressableExternalModule($path, $resourceMap, $this);
537
538
                // Set composer parameters
539
                $connector->composerParameters = $parameters;
540
            } else { // Signal error
541
                return e('Cannot load module from: "##"', E_SAMSON_FATAL_ERROR, $path);
542
            }
543
544
        } else {
545
            return e('Cannot load module from[##]', E_SAMSON_FATAL_ERROR, $path);
546
        }
547
548
        // Chaining
549
        return $this;
550
    }
551
    //[PHPCOMPRESSOR(remove,end)]
552
553
    /** Магический метод для десериализации объекта */
554
    public function __wakeup()
555
    {
556
        $this->active = &$this->module_stack['local'];
557
    }
558 7
559
    /** Магический метод для сериализации объекта */
560
    public function __sleep()
561 7
    {
562
        return array('module_stack', 'render_mode');
563
    }
564
}
565