Completed
Push — master ( f4d401...8d3e9d )
by Vitaly
10:58
created

Core::render()   C

Complexity

Conditions 9
Paths 28

Size

Total Lines 64
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 44.8244

Importance

Changes 7
Bugs 1 Features 3
Metric Value
c 7
b 1
f 3
dl 0
loc 64
ccs 5
cts 21
cp 0.2381
rs 6.5449
cc 9
eloc 29
nc 28
nop 2
crap 44.8244

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\LoadableInterface;
12
use samsonframework\core\SystemInterface;
13
use samsonframework\resource\ResourceMap;
14
use samsonphp\config\Scheme;
15
use samsonphp\core\exception\CannotLoadModule;
16
use samsonphp\core\exception\ViewPathNotFound;
17
use samsonphp\event\Event;
18
19
/**
20
 * Core of SamsonPHP
21
 * 
22
 * @package SamsonPHP
23
 * @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...
24
 * @version @version@
25
 */
26
class Core implements SystemInterface
27
{
28
    /* Rendering models */
29
    /** Standard algorithm for view rendering */
30
    const RENDER_STANDART = 1;
31
    /** View rendering algorithm from array of view variables */
32
    const RENDER_VARIABLE = 3;
33
34
    /** @var  ResourceMap Current web-application resource map */
35
    public $map;
36
37
    /** @var Module[] Collection of loaded modules */
38
    public $module_stack = array();
39
    /** @var string Path to current web-application */
40
    public $system_path = __SAMSON_CWD__;
41
    /** @var string View path loading mode */
42
    public $render_mode = self::RENDER_STANDART;
43
    /** @var Module Pointer to current active module */
44
    protected $active = null;
45
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
46
    protected $async = false;
47
    /** @var string Path to main system template */
48
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
49
    /** @var string Current system environment */
50
    protected $environment;
51
52
    /**
53
     * Core constructor.
54
     *
55
     * @param ResourceMap|null $map system resources
56
     */
57
    public function __construct(ResourceMap $map = null)
58
    {
59
        if (!isset($map)) {
60
            // Get correct web-application path
61
            $this->system_path = __SAMSON_CWD__;
62
63
            // Get web-application resource map
64
            $this->map = ResourceMap::get($this->system_path, false, array('src/'));
65
        } else { // Use data from passed map
66
            $this->map = $map;
67
            $this->system_path = $map->entryPoint;
68
        }
69
70
        // Connect static collection with this dynamic field to avoid duplicates
71
        $this->module_stack = &Module::$instances;
72
73
        // Temporary add template worker
74
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
75
76
        $whoops = new \Whoops\Run;
77
        $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...
78
        $whoops->register();
79
80
        // Fire core creation event
81
        Event::fire('core.created', array(&$this));
82
83
        // Signal core configure event
84
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
85
    }
86
87
    /**
88
     * Generic wrap for Event system subscription.
89
     * @see \samson\core\\samsonphp\event\Event::subscribe()
90
     *
91
     * @param string   $key     Event identifier
92
     * @param callable $handler Event handler
93
     * @param array    $params  Event parameters
94
     *
95
     * @return $this Chaining
96
     */
97
    public function subscribe($key, $handler, $params = array())
98
    {
99
        Event::subscribe($key, $handler, $params);
100
101
        return $this;
102
    }
103
104
    /**
105
     * Change current system working environment or receive
106
     * current system enviroment if no arguments are passed.
107
     *
108
     * @param string $environment Environment identifier
109
     *
110 1
     * TODO: Function has two different logics - needs to be changed!
111
     * @return $this|string Chaining or current system environment
112
     */
113
    public function environment($environment = Scheme::BASE)
114
    {
115
        if (func_num_args() !== 0) {
116
            $this->environment = $environment;
117
118
            // Signal core environment change
119
            Event::signal('core.environment.change', array($environment, &$this));
120
            return $this;
121 1
        }
122
123
        return $this->environment;
124
    }
125
126
    /**
127
     * Generate special response header triggering caching mechanisms
128
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
129
     * @param string $accessibility Cache-control accessibility value(default public)
130
     */
131
    public function cached($cacheLife = 3600, $accessibility = 'public')
132
    {
133
        static $cached;
134
        // Protect sending cached headers once
135
        if (!isset($cached) or $cached !== true) {
136
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
137
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
138
            header('Pragma: cache');
139
140
            $cached = true;
141
        }
142
    }
143
144
    /**
145
     * Set asynchronous mode.
146
     * This mode will not output template and will just path everything that
147
     * was outputted to client.
148
     *
149
     * @param bool $async True to switch to asynchronous output mode
150
     *
151
     * @return $this Chaining
152
     */
153
    public function async($async)
154
    {
155
        $this->async = $async;
156
157
        return $this;
158
    }
159
160
    /** @see iCore::path() */
161
    public function path($path = null)
162
    {
163
        // Если передан аргумент
164
        if (func_num_args()) {
165
            // Сформируем новый относительный путь к главному шаблону системы
166
            $this->template_path = $path . $this->template_path;
167
168
            // Сохраним относительный путь к Веб-приложению
169
            $this->system_path = $path;
170
171
            // Продолжил цепирование
172
            return $this;
173
        }
174
175
        // Вернем текущее значение
176
        return $this->system_path;
177
    }
178
179
    /**    @see iModule::active() */
180
    public function &active(iModule &$module = null)
181
    {
182
        // Сохраним старый текущий модуль
183
        $old = &$this->active;
184
185
        // Если передано значение модуля для установки как текущий - проверим и установим его
186
        if (isset($module)) {
187
            $this->active = &$module;
188
        }
189
190
        // Вернем значение текущего модуля
191
        return $old;
192
    }
193
194
    /**
195
     * Retrieve module instance by identifier.
196
     *
197
     * @param string|null $module Module identifier
198
     *
199
     * @return null|Module Found or active module
200
     */
201
    public function &module($module = null)
202
    {
203
        $return = null;
204
205
        // Ничего не передано - вернем текущуй модуль системы
206
        if (!isset($module) && isset($this->active)) {
207
            $return = &$this->active;
208
        } elseif (is_object($module)) {
209
            $return = &$module;
210
        } elseif (is_string($module) && isset($this->module_stack[$module])) {
211
            $return = &$this->module_stack[$module];
212
        }
213
214
        // Ничего не получилось вернем ошибку
215
        if ($return === null) {
216
            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...
217
        }
218
219
        return $return;
220
    }
221
222
    /**
223
     * Unload module from core.
224
     *
225
     * @param string $moduleID Module identifier
226
     */
227
    public function unload($moduleID)
228
    {
229
        if (isset($this->module_stack[$moduleID])) {
230
            unset($this->module_stack[$moduleID]);
231
        }
232
    }
233
234
    /**
235
     * Insert generic html template tags and data
236
     *
237
     * @param string $templateHtml Generated HTML
238
     *
239
     * @deprecated Must be moved to a new HTML output object
240
     * @return mixed Changed HTML template
241
     */
242
    public function generateTemplate(&$templateHtml)
243
    {
244
        // Добавим путь к ресурсам для браузера
245
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
246
        // Добавим отметку времени для JavaScript
247
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
248
249
        // Добавим поддержку HTML для старых IE
250
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
251
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
252
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
253
        $headHtml .= "\n" . '<![endif]-->';
254
255
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
256
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
257
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
258
259
        // Вставим указатель JavaScript ресурсы в конец HTML документа
260
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
261
262
        return $templateHtml;
263
    }
264 2
265
    /**
266
     * Start SamsonPHP framework.
267
     *
268
     * @param string $default Default module identifier
269
     *
270
     * @throws ViewPathNotFound
271 2
     */
272
    public function start($default)
273
    {
274 2
        // TODO: Change ExternalModule::init() signature
275
        // Fire core started event
276
        Event::fire('core.started');
277
278 2
        // TODO: Does not see why it should be here
279
        // Set main template path
280 2
        $this->template($this->template_path);
281
282 2
        // Security layer
283 2
        $securityResult = true;
284
        // Fire core security event
285
        Event::fire('core.security', array(&$this, &$securityResult));
286 2
287
        /** @var mixed $result External route controller action result */
288 2
        $result = false;
289
290 2
        // If we have passed security application layer
291
        if ($securityResult) {
292
            // Fire core routing event - go to routing application layer
293
            Event::signal('core.routing', array(&$this, &$result, $default));
294
        }
295 2
296
        // If no one has passed back routing callback
297
        if (!isset($result) || $result === false) {
298
            // Fire core e404 - routing failed event
299
            $result = Event::signal('core.e404', array(url()->module, url()->method));
300
        }
301
302
        // Response
303
        $output = '';
304
305
        // If this is not asynchronous response and controller has been executed
306
        if (!$this->async && ($result !== false)) {
307
            // Store module data
308
            $data = $this->active->toView();
309
310
            // Render main template
311
            $output = $this->render($this->template_path, $data);
312
313
            // Fire after render event
314
            Event::fire('core.rendered', array(&$output, &$data, &$this->active));
315
        }
316
317
        // Output results to client
318
        echo $output;
319
320 2
        // Fire ended event
321
        Event::fire('core.ended', array(&$output));
322
    }
323 2
324
    /**	@see iCore::template() */
1 ignored issue
show
Coding Style introduced by
Spaces must be used for alignment; tabs are not allowed
Loading history...
325
    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...
326 2
    {
327
        // Если передан аргумент
328
        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...
329 2
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
330
        }
331
332 2
        // Аргументы не переданы - вернем текущий путь к шаблону системы
333
        return $this->template_path;
334
    }
335 2
336
    /**
337
     * Render file to a buffer.
338 2
     *
339
     * @param string $view Path to file
340
     * @param array  $data Collection of variables to path to file
341
     *
342
     * @return string Rendered file contents
343
     * @throws ViewPathNotFound
344
     */
345
    public function render($view, $data = array())
346
    {
347
        // TODO: Make rendering as external system, to split up these 3 rendering options
348
349
        // Объявить ассоциативный массив переменных в данном контексте
350
        if (is_array($data)) {
351
            extract($data);
352 7
        }
353
354 7
        // Начать вывод в буффер
355
        ob_start();
356 7
357
        // Path to another template view, by default we are using default template folder path,
358
        // for meeting first condition
359
        $templateView = $view;
360
361
        if (locale() != SamsonLocale::DEF) {
362
            // Modify standard view path with another template
363
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
364
        }
365
366
        // Depending on core view rendering model
367
        switch ($this->render_mode) {
368
            // Standard algorithm for view rendering
369
            case self::RENDER_STANDART:
370
                // Trying to find another template path, by default it's an default template path
371
                if (file_exists($templateView)) {
372
                    include($templateView);
373
                } elseif (file_exists($view)) {
374
                    // If another template wasn't found - we will use default template path
375
                    include($view);
376
                } else { // Error no template view was found
377
                    throw(new ViewPathNotFound($view));
378
                }
379
                break;
380
381
            // View rendering algorithm from array of view variables
382
            case self::RENDER_VARIABLE:
383
                // Collection of views
384
                $views = &$GLOBALS['__compressor_files'];
385
                // Trying to find another template path, by default it's an default template path
386
                if (isset($views[$templateView])) {
387
                    eval(' ?>' . $views[$templateView] . '<?php ');
388
                } elseif (isset($views[$view])) {
389
                    // If another template wasn't found - we will use default template path
390
                    eval(' ?>' . $views[$view] . '<?php ');
391
                } else { // Error no template view was found
392
                    throw(new ViewPathNotFound($view));
393
                }
394
                break;
395
        }
396
397
        // Получим данные из буффера вывода
398
        $html = ob_get_contents();
399
400
        // Очистим буффер
401
        ob_end_clean();
402
403 2
        // Fire core render event
404
        Event::fire('core.render', array(&$html, &$data, &$this->active));
405
406 2
        ////elapsed('End rendering '.$__view);
407
        return $html;
408
    }
409 2
410 2
    //[PHPCOMPRESSOR(remove,start)]
411 2
412
    /**
413
     * Load system from composer.json
414 2
     * @param string $dependencyFilePath Path to dependencies file
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
            $this->load(__SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement,
437
                array_merge(
438
                    is_array($parameters) ? $parameters : array($parameters),
439
                    array('module_id' => $requirement)
440
                )
441
            );
442
        }
443
444
        $localModulesPath = '../src';
445
        ResourceMap::get('cache');
446
        // TODO: Nested modules relation
447
        for ($i = 0; $i < 2; $i++) {
448
            $resourceMap = ResourceMap::get($localModulesPath);
449
450
            foreach ($resourceMap->modules as $moduleFile) {
451
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
452
                $modulePath = explode('/', $modulePath);
453
                $modulePath = $localModulesPath . '/' . $modulePath[1];
454
                $this->load($modulePath, $parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters seems to be defined by a foreach iteration on line 435. 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...
455
            }
456
        }
457
458
        // Create local module and set it as active
459
        $this->active = new VirtualModule($this->system_path, $this->map, $this, 'local');
460
461
        // TODO: This should be changed to one single logic
462
        // Require all local module model files
463
        foreach ($this->map->models as $model) {
464
            // TODO: Why have to require once?
465
            require_once($model);
466
        }
467
468
        // Create all local modules
469
        foreach ($this->map->controllers as $controller) {
470
            // Require class into PHP
471
            require($controller);
472
473
            // Create module connector instance
474
            new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
475
        }
476
477
        return $this;
478
    }
479
480
    /**
481
     * Initialize module.
482
     *
483
     * @param ExternalModule $instance           Module instance for initialization
484
     * @param array          $composerParameters Collection of extra parameters from composer.json file
485
     */
486
    protected function initModule($instance, $composerParameters)
487
    {
488
        $identifier = $instance->id();
489
490
        // Set composer parameters
491
        $instance->composerParameters = $composerParameters;
492
493
        // TODO: Change event signature to single approach
494
        // Fire core module load event
495
        Event::fire('core.module_loaded', array($identifier, &$instance));
496
497
        // Signal core module configure event
498
        Event::signal('core.module.configure', array(&$instance, $identifier));
499
500
        // Call module preparation handler
501
        if (!$instance->prepare()) {
502
            // Handle module failed preparing
503
        }
504
505
        // Trying to find parent class for connecting to it to use View/Controller inheritance
506
        $parentClass = get_parent_class($instance);
507
        if (!in_array($parentClass,
508
            array('samson\core\ExternalModule', 'samson\core\CompressableExternalModule'))
509
        ) {
510
            // Переберем загруженные в систему модули
511
            foreach ($this->module_stack as &$m) {
512
                // Если в систему был загружен модуль с родительским классом
513
                if (get_class($m) === $parentClass) {
514
                    $instance->parent = &$m;
515
                    //elapsed('Parent connection for '.$moduleClass.'('.$connector->uid.') with '.$parent_class.'('.$m->uid.')');
516
                }
517
            }
518
        }
519
    }
520
521
    /**
522
     * Load module from path to core.
523
     *
524
     * @param string $path       Path for module loading
525
     * @param array  $parameters Collection of loading parameters
526
     *
527
     * @return $this|bool
528
     * @throws \samsonphp\core\exception\CannotLoadModule
529
     */
530
    public function load($path, $parameters = array())
531
    {
532
        // Check path
533
        if (file_exists($path)) {
534
            /** @var ResourceMap $resourceMap Gather all resources from path */
535
            $resourceMap = ResourceMap::get($path);
536
            if (isset($resourceMap->module[0])) {
537
538
                /** @var string $controllerPath Path to module controller file */
539
                $controllerPath = $resourceMap->module[1];
540
541
                /** @var string $moduleClass Name of module controller class to load */
542
                $moduleClass = $resourceMap->module[0];
543
544
                // Require module controller class into PHP
545
                if (file_exists($controllerPath)) {
546
                    require_once($controllerPath);
547
                }
548
549
                // TODO: this should be done via composer autoload file field
550
                // Iterate all function-style controllers and require them
551
                foreach ($resourceMap->controllers as $controller) {
552
                    require_once($controller);
553
                }
554
555
                $this->initModule(
556
                    new $moduleClass($path, $resourceMap, $this),
557
                    $parameters
558 7
                );
559
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
560
                $this->initModule(
561 7
                    new VirtualModule($path, $resourceMap, $this, str_replace('/', '', $parameters['module_id'])),
562
                    $parameters
563
                );
564 7
            } elseif (count($resourceMap->classes)) {
565
                /** Update for future version: Search classes that implement LoadableInterface */
566
                foreach ($resourceMap->classes as $classPath => $class) {
567 7
                    // This class implements LoadableInterface LoadableInterface::class
568
                    if (in_array('\samsonframework\core\LoadableInterface', $resourceMap->classData[$classPath]['implements'])) {
569
                        $this->initModule(
570 7
                            new VirtualModule(
571
                                $path,
572
                                $resourceMap,
573 7
                                $this,
574
                                str_replace('/', '', $resourceMap->classData[$classPath]['className'])
0 ignored issues
show
Bug introduced by
The property classData does not seem to exist in samsonframework\resource\ResourceMap.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
575
                            ),
576 7
                            $parameters
577 7
                        );
578
                    }
579
                }
580
            }
581
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
582
        } else {
583
            throw new CannotLoadModule($path);
584
        }
585
586
        // Chaining
587
        return $this;
588
    }
589
    //[PHPCOMPRESSOR(remove,end)]
590
591
    /** Магический метод для десериализации объекта */
592
    public function __wakeup()
593
    {
594
        $this->active = &$this->module_stack['local'];
595
    }
596
597
    /** Магический метод для сериализации объекта */
598
    public function __sleep()
599
    {
600
        return array('module_stack', 'render_mode');
601
    }
602
}
603