Completed
Push — master ( 10f334...e9449e )
by Nikita
10:19
created

Core::module()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

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