Completed
Push — master ( 93639e...8bbe63 )
by
unknown
10:15
created

Core::load()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 54
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 13
Bugs 2 Features 5
Metric Value
cc 9
eloc 22
nc 9
nop 2
dl 0
loc 54
ccs 0
cts 0
cp 0
crap 90
rs 7.255
c 13
b 2
f 5

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 declare(strict_types=1);
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 Doctrine\Common\Annotations\AnnotationReader;
12
use samson\activerecord\dbMySQL;
13
use samson\activerecord\dbQuery;
14
use samson\url\URL;
15
use samsonframework\container\Builder;
16
use samsonframework\container\metadata\ClassMetadata;
17
use samsonframework\container\metadata\MethodMetadata;
18
use samsonframework\container\metadata\PropertyMetadata;
19
use samsonframework\containerannotation\AnnotationClassResolver;
20
use samsonframework\containerannotation\AnnotationMetadataCollector;
21
use samsonframework\containerannotation\AnnotationMethodResolver;
22
use samsonframework\containerannotation\AnnotationPropertyResolver;
23
use samsonframework\containerannotation\AnnotationResolver;
24
use samsonframework\containerannotation\Inject;
25
use samsonframework\containerannotation\Injectable;
26
use samsonframework\containerannotation\InjectArgument;
27
use samsonframework\core\PreparableInterface;
28
use samsonframework\core\SystemInterface;
29
use samsonframework\di\ContainerInterface;
30
use samsonframework\orm\Database;
31
use samsonframework\orm\Query;
32
use samsonframework\resource\ResourceMap;
33
use samsonphp\config\Scheme;
34
use samsonphp\core\exception\CannotLoadModule;
35
use samsonphp\core\exception\ViewPathNotFound;
36
use samsonphp\core\Module;
37
use samsonphp\event\Event;
38
use samsonframework\container\ContainerBuilderInterface;
39
use stylelikeio\config\ActiveRecordConfig;
40
41
/**
42
 * SamsonPHP Core.
43
 *
44
 * @author Vitaly Iegorov <[email protected]>
45
 */
46
class Core implements SystemInterface
47
{
48
    /** @var ContainerInterface */
49
    protected $container;
50
51
    /** @var ClassMetadata[] */
52
    protected $metadataCollection = [];
53
54
    /** @var ContainerBuilderInterface */
55
    protected $builder;
56
57
    /** @var string Current system environment */
58
    protected $environment;
59
60
    /* Rendering models */
61
    /** @deprecated Standard algorithm for view rendering */
62
    const RENDER_STANDART = 1;
63
    /** @deprecated View rendering algorithm from array of view variables */
64
    const RENDER_VARIABLE = 3;
65
66
    /** @deprecated @var  ResourceMap Current web-application resource map */
67
    public $map;
68
69
    protected $classes = [];
70
   
71
    /** @deprecated @var string Path to current web-application */
72
    public $system_path = __SAMSON_CWD__;
73
    /** @deprecated @var string View path loading mode */
74
    public $render_mode = self::RENDER_STANDART;
75
    /** @var Module Pointer to current active module */
76
    protected $active = null;
77
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
78
    protected $async = false;
79
    /** @var string Path to main system template */
80
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
81
82
    /** @return \Container Get system container */
83
    public function getContainer()
84
    {
85
        return $this->container;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->container; (samsonframework\di\ContainerInterface) is incompatible with the return type declared by the interface samsonframework\core\SystemInterface::getContainer of type samsonframework\container\ContainerInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
86
    }
87
88
    /**
89
     * Core constructor.
90
     *
91
     * @param ContainerBuilderInterface $builder Container builder
92
     * @param ResourceMap|null $map system resources
93
     */
94
    public function __construct(ContainerBuilderInterface $builder, ResourceMap $map = null)
95
    {
96
        $this->builder = $builder;
97
98
        if (!isset($map)) {
99
            // Get correct web-application path
100
            $this->system_path = __SAMSON_CWD__;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
101
102
            // Get web-application resource map
103
            $this->map = ResourceMap::get($this->system_path, false, array('src/'));
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$map has been deprecated with message: @var ResourceMap Current web-application resource map

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
104
        } else { // Use data from passed map
105
            $this->map = $map;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$map has been deprecated with message: @var ResourceMap Current web-application resource map

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
106
            $this->system_path = $map->entryPoint;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
107
        }
108
109
        // Temporary add template worker
110 1
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
111
112
        // Fire core creation event
113
        Event::fire('core.created', array(&$this));
114
115
        // Signal core configure event
116
        Event::signal('core.configure', array($this->system_path . __SAMSON_CONFIG_PATH));
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
117
    }
118
119
    /**
120
     * Generic wrap for Event system subscription.
121 1
     * @see \samson\core\\samsonphp\event\Event::subscribe()
122
     *
123
     * @param string   $key     Event identifier
124
     * @param callable $handler Event handler
125
     * @param array    $params  Event parameters
126
     *
127
     * @return $this Chaining
128
     */
129
    public function subscribe($key, $handler, $params = array())
130
    {
131
        Event::subscribe($key, $handler, $params);
132
133
        return $this;
134
    }
135
136
    /**
137
     * Change current system working environment or receive
138
     * current system enviroment if no arguments are passed.
139
     *
140
     * @param string $environment Environment identifier
141
     *
142
     * TODO: Function has two different logics - needs to be changed!
143
     * @return $this|string Chaining or current system environment
144
     */
145
    public function environment($environment = Scheme::BASE)
146
    {
147
        if (func_num_args() !== 0) {
148
            $this->environment = $environment;
149
150
            // Signal core environment change
151
            Event::signal('core.environment.change', array($environment, &$this));
152
            return $this;
153
        }
154
155
        return $this->environment;
156
    }
157
158
    /**
159
     * Generate special response header triggering caching mechanisms
160
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
161
     * @param string $accessibility Cache-control accessibility value(default public)
162
     */
163
    public function cached($cacheLife = 3600, $accessibility = 'public')
164
    {
165
        static $cached;
166
        // Protect sending cached headers once
167
        if (!isset($cached) or $cached !== true) {
168
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
169
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
170
            header('Pragma: cache');
171
172
            $cached = true;
173
        }
174
    }
175
176
    /**
177
     * Set asynchronous mode.
178
     * This mode will not output template and will just path everything that
179
     * was outputted to client.
180
     *
181
     * @param bool $async True to switch to asynchronous output mode
182
     *
183
     * @return $this Chaining
184
     */
185
    public function async($async)
186
    {
187
        $this->async = $async;
188
189
        return $this;
190
    }
191
192
    /** @see iCore::path() */
193
    public function path($path = null)
194
    {
195
        // Если передан аргумент
196
        if (func_num_args()) {
197
            // Сформируем новый относительный путь к главному шаблону системы
198
            $this->template_path = $path . $this->template_path;
199
200
            // Сохраним относительный путь к Веб-приложению
201
            $this->system_path = $path;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
202
203
            // Продолжил цепирование
204
            return $this;
205
        }
206
207
        // Вернем текущее значение
208
        return $this->system_path;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
209
    }
210
211
    /**    @see iModule::active() */
212
    public function &active(&$module = null)
213
    {
214
        // Сохраним старый текущий модуль
215
        $old = &$this->active;
216
217
        // Если передано значение модуля для установки как текущий - проверим и установим его
218
        if (isset($module)) {
219
            $this->active = &$module;
220
        }
221
222
        // Вернем значение текущего модуля
223
        return $old;
224
    }
225
226
    /**
227
     * Retrieve module instance by identifier.
228
     *
229
     * @param string|null $module Module identifier
230
     *
231
     * @return null|Module Found or active module
232
     */
233
    public function &module($module = null)
234
    {
235
        $return = null;
236
237
        // Ничего не передано - вернем текущуй модуль системы
238
        if (!isset($module) && isset($this->active)) {
239
            $return = &$this->active;
240
        } elseif (is_object($module)) {
241
            $return = &$module;
242
        } elseif (is_string($module)) {
243
            $return = $this->container->get($module);
244
        }
245
246
        // Ничего не получилось вернем ошибку
247
        if ($return === null) {
248
            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...
249
        }
250
251
        return $return;
252
    }
253
254
    /**
255
     * Unload module from core.
256
     *
257
     * @param string $moduleID Module identifier
258
     */
259
    public function unload($moduleID)
260
    {
261
        if (isset($this->module_stack[$moduleID])) {
0 ignored issues
show
Bug introduced by
The property module_stack does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
262
            unset($this->module_stack[$moduleID]);
263
        }
264 2
    }
265
266
    /**
267
     * Insert generic html template tags and data
268
     *
269
     * @param string $templateHtml Generated HTML
270
     *
271 2
     * @deprecated Must be moved to a new HTML output object
272
     * @return mixed Changed HTML template
273
     */
274 2
    public function generateTemplate(&$templateHtml)
275
    {
276
        // Добавим путь к ресурсам для браузера
277
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
278 2
        // Добавим отметку времени для JavaScript
279
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
280 2
281
        // Добавим поддержку HTML для старых IE
282 2
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
283 2
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
284
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
285
        $headHtml .= "\n" . '<![endif]-->';
286 2
287
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
288 2
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
289
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
290 2
291
        // Вставим указатель JavaScript ресурсы в конец HTML документа
292
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
293
294
        return $templateHtml;
295 2
    }
296
297
    /**
298
     * Start SamsonPHP framework.
299
     *
300
     * @param string $default Default module identifier
301
     *
302
     * @throws ViewPathNotFound
303
     */
304
    public function start($default)
305
    {
306
        // TODO: Change ExternalModule::init() signature
307
        // Fire core started event
308
        Event::fire('core.started');
309
310
        // TODO: Does not see why it should be here
311
        // Set main template path
312
        $this->template($this->template_path);
313
314
        // Security layer
315
        $securityResult = true;
316
        // Fire core security event
317
        Event::fire('core.security', array(&$this, &$securityResult));
318
319
        /** @var mixed $result External route controller action result */
320 2
        $result = false;
321
322
        // If we have passed security application layer
323 2
        if ($securityResult) {
324
            // Fire core routing event - go to routing application layer
325
            Event::signal('core.routing', array(&$this, &$result, $default));
326 2
        }
327
328
        // If no one has passed back routing callback
329 2
        if (!isset($result) || $result === false) {
330
            // Fire core e404 - routing failed event
331
            $result = Event::signal('core.e404', array(url()->module, url()->method));
332 2
        }
333
334
        // Response
335 2
        $output = '';
336
337
        // If this is not asynchronous response and controller has been executed
338 2
        if (!$this->async && ($result !== false)) {
339
            // Store module data
340
            $data = $this->active->toView();
341
342
            // Render main template
343
            $output = $this->render($this->template_path, $data);
344
345
            // Fire after render event
346
            Event::fire('core.rendered', array(&$output));
347
        }
348
349
        // Output results to client
350
        echo $output;
351
352 7
        // Fire ended event
353
        Event::fire('core.ended', array(&$output));
354 7
    }
355
356 7
    /**	@see iCore::template() */
357
    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...
358
    {
359
        // Если передан аргумент
360
        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...
361
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
362
        }
363
364
        // Аргументы не переданы - вернем текущий путь к шаблону системы
365
        return $this->template_path;
366
    }
367
368
    /**
369
     * Render file to a buffer.
370
     *
371
     * @param string $view Path to file
372
     * @param array  $data Collection of variables to path to file
373
     *
374
     * @return string Rendered file contents
375
     * @throws ViewPathNotFound
376
     */
377
    public function render($view, $data = array())
378
    {
379
        // TODO: Make rendering as external system, to split up these 3 rendering options
380
381
        // Объявить ассоциативный массив переменных в данном контексте
382
        if (is_array($data)) {
383
            extract($data);
384
        }
385
386
        // Начать вывод в буффер
387
        ob_start();
388
389
        // Path to another template view, by default we are using default template folder path,
390
        // for meeting first condition
391
        $templateView = $view;
392
393
        if (locale() != SamsonLocale::DEF) {
394
            // Modify standard view path with another template
395
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
396
        }
397
398
        // Depending on core view rendering model
399
        switch ($this->render_mode) {
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$render_mode has been deprecated with message: @var string View path loading mode

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
400
            // Standard algorithm for view rendering
401
            case self::RENDER_STANDART:
0 ignored issues
show
Deprecated Code introduced by
The constant samson\core\Core::RENDER_STANDART has been deprecated with message: Standard algorithm for view rendering

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
402
                // Trying to find another template path, by default it's an default template path
403 2
                if (file_exists($templateView)) {
404
                    include($templateView);
405
                } elseif (file_exists($view)) {
406 2
                    // If another template wasn't found - we will use default template path
407
                    include($view);
408
                } else { // Error no template view was found
409 2
                    throw(new ViewPathNotFound($view));
410 2
                }
411 2
                break;
412
413
            // View rendering algorithm from array of view variables
414 2
            case self::RENDER_VARIABLE:
0 ignored issues
show
Deprecated Code introduced by
The constant samson\core\Core::RENDER_VARIABLE has been deprecated with message: View rendering algorithm from array of view variables

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
415
                // Collection of views
416
                $views = &$GLOBALS['__compressor_files'];
417
                // Trying to find another template path, by default it's an default template path
418
                if (isset($views[$templateView])) {
419
                    eval(' ?>' . $views[$templateView] . '<?php ');
420
                } elseif (isset($views[$view])) {
421
                    // If another template wasn't found - we will use default template path
422
                    eval(' ?>' . $views[$view] . '<?php ');
423
                } else { // Error no template view was found
424
                    throw(new ViewPathNotFound($view));
425
                }
426
                break;
427
        }
428
429
        // Получим данные из буффера вывода
430
        $html = ob_get_contents();
431
432
        // Очистим буффер
433
        ob_end_clean();
434
435
        // Fire core render event
436
        Event::fire('core.render', array(&$html, &$data, &$this->active));
437
438
        ////elapsed('End rendering '.$__view);
439
        return $html;
440
    }
441
442
    //[PHPCOMPRESSOR(remove,start)]
443
444
    /**
445
     * Load system from composer.json
446
     * @param string $dependencyFilePath Path to dependencies file
447
     * @return $this Chaining
448
     */
449
    public function composer($dependencyFilePath = null)
450
    {
451
        $composerModules = array();
452
453
        Event::fire(
454
            'core.composer.create',
455
            array(
456
                &$composerModules,
457
                isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path,
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
458
                array(
459
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'),
460
                    'ignoreKey' => 'samson_module_ignore',
461
                    'includeKey' => 'samson_module_include'
462
                )
463
            )
464
        );
465
466
        $modulesToLoad = [];
467
        $preClasses = [];
468
        $preModules = [];
469
470
        // Iterate requirements
471
        foreach ($composerModules as $requirement => $parameters) {
472
            $modulePath = __SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement;
473
            $moduleName = $this->load($modulePath,
474
            array_merge(
475
                is_array($parameters) ? $parameters : array($parameters),
476
                array('module_id' => $requirement)
477
            ));
478
            if (array_key_exists('samsonframework_precontainer', $parameters)) {
479
                $preModules[$moduleName] = $parameters;
480
                foreach (ResourceMap::get($modulePath)->classes as $className) {
481
                    //$preClasses[str_replace(['\\', '/'], '_', $className)] = $className;
482
                }
483
            }
484
            $modulesToLoad[$moduleName] = $parameters;
485
        }
486
487
        $metadata = new ClassMetadata();
488
        $metadata->className = get_class($this);
489
        $metadata->name = 'core';
490
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
491
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
492
        $metadata->methodsMetadata['__construct']->dependencies['map'] = 'resource_map';
493
        $this->metadataCollection[$metadata->name] = $metadata;
494
495
        $metadata = new ClassMetadata();
496
        $metadata->className = ResourceMap::class;
497
        $metadata->name = 'resource_map';
498
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
499
        $this->metadataCollection[$metadata->name] = $metadata;
500
501
        // Create local module and set it as active
502
        $this->createMetadata(VirtualModule::class, 'local', $this->system_path);
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
503
504
        // TODO: This should be changed to one single logic
505
        // Require all local module model files
506
        foreach ($this->map->models as $model) {
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$map has been deprecated with message: @var ResourceMap Current web-application resource map

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
507
            // TODO: Why have to require once?
508
            require_once($model);
509
        }
510
511
        // Create all local modules
512
        foreach ($this->map->controllers as $controller) {
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$map has been deprecated with message: @var ResourceMap Current web-application resource map

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
513
            // Require class into PHP
514
            require($controller);
515
516
            //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
517
518
            $this->createMetadata(VirtualModule::class, basename($controller, '.php'), $this->system_path);
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

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

Loading history...
519
        }
520
521
        $localModulesPath = '../src';
522
        ResourceMap::get('cache');
523
        // TODO: Nested modules relation
524
        for ($i = 0; $i < 2; $i++) {
525
            $resourceMap = ResourceMap::get($localModulesPath);
526
527
            foreach ($resourceMap->modules as $moduleFile) {
528
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
529
                $modulePath = explode('/', $modulePath);
530
                $modulePath = $localModulesPath . '/' . $modulePath[1];
531
                $moduleName = $this->load($modulePath, $parameters);
0 ignored issues
show
Bug introduced by
The variable $parameters seems to be defined by a foreach iteration on line 471. 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...
532
                $modulesToLoad[$moduleName] = $parameters;
533
            }
534
        }
535
536
        $preMetadataCollection = [];
537
        foreach ($preModules as $moduleName => $parameters) {
538
            $preMetadataCollection[$moduleName] = $this->metadataCollection[$moduleName];
539
        }
540
        $preMetadataCollection['core'] = $this->metadataCollection['core'];
541
        $preMetadataCollection['resource_map'] = $this->metadataCollection['resource_map'];
542
543
        $this->loadMetadata($preMetadataCollection, $preModules, $preClasses);
544
        $this->loadMetadata($this->metadataCollection, $modulesToLoad, $this->classes);
545
546
        $this->active = $this->container->getLocal();
547
548
        return $this;
549
    }
550
551
    /**
552
     * @param ClassMetadata[] $metadataCollection
553
     * @param array $modulesToLoad
554
     * @param array $classes
555
     */
556
    protected function loadMetadata(array $metadataCollection, array $modulesToLoad, array $classes)
557
    {
558 7
        // Load annotation and parse classes
559
        new Injectable();
560
        new InjectArgument(['var' => 'type']);
561 7
562
        $reader = new AnnotationReader();
563
        $resolver = new AnnotationResolver(
564 7
            new AnnotationClassResolver($reader),
565
            new AnnotationPropertyResolver($reader),
566
            new AnnotationMethodResolver($reader)
567 7
        );
568
        $annotationCollector = new AnnotationMetadataCollector($resolver);
569
        $metadataCollection = $annotationCollector->collect($classes, $metadataCollection);
570 7
571
        // Regroup classes metadata by class name instead of alias
572
        $classesMetadata = [];
573 7
        foreach ($metadataCollection as $alias => $classMetadata) {
574
            $classesMetadata[$classMetadata->className] = $classMetadata;
575
        }
576 7
577 7
        // Gather all interface implementations
578
        $implementsByAlias = [];
579
        foreach (get_declared_classes() as $class) {
580
            $classImplements = class_implements($class);
581
            foreach (get_declared_interfaces() as $interface) {
582
                if (in_array($interface, $classImplements, true)) {
583
                    if (array_key_exists($class, $classesMetadata)) {
584
                        $implementsByAlias[strtolower('\\'.$interface)][] = $classesMetadata[$class]->name;
585
                    }
586
                }
587
            }
588
        }
589
590
        // Gather all class implementations
591
        $serviceAliasesByClass = [];
592
        foreach (get_declared_classes() as $class) {
593
            if (array_key_exists($class, $classesMetadata)) {
594
                $serviceAliasesByClass[strtolower('\\' . $class)][] = $classesMetadata[$class]->name;
595
            }
596
        }
597
598
        /**
599
         * TODO: now we need to implement not forcing to load fixed dependencies into modules
600
         * to give ability to change constructors and inject old variable into properties
601
         * and them after refactoring remove them. With this we can only specify needed dependencies
602
         * in new modules, and still have old ones working.
603
         */
604
605
        foreach ($metadataCollection as $alias => $metadata) {
606 View Code Duplication
            foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
607
                $dependency = strtolower($propertyMetadata->dependency);
608
                if (array_key_exists($dependency, $implementsByAlias)) {
609
                    $propertyMetadata->dependency = $implementsByAlias[$dependency][0];
610
                } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
611
                    $propertyMetadata->dependency = $serviceAliasesByClass[$dependency][0];
612
                } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
613
614
                }
615
            }
616
            foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
617 View Code Duplication
                foreach ($methodMetadata->dependencies as $argument => $dependency) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
618
                    $dependency = strtolower($dependency);
619
                    if (array_key_exists($dependency, $implementsByAlias)) {
620
                        $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
621
                    } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
622
                        $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0];
623
                    } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
624
625
                    }
626
                }
627
            }
628
        }
629
630
        // Generate XML configs
631
        (new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_');
632
633
        // Load container class
634
        $containerName = 'Container' . rand(1, 10000);
635
        $containerPath = $this->path().'www/cache/' . $containerName . '.php';
636
        file_put_contents($containerPath, $this->builder->build($metadataCollection, $containerName));
637
        require_once($containerPath);
638
639
        // Inject current core into container
640
        $this->container = new $containerName();
641
        $containerReflection = new \ReflectionClass(get_class($this->container));
642
        $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES);
643
        $serviceProperty->setAccessible(true);
644
        $containerServices = $serviceProperty->getValue($this->container);
645
        $containerServices['core'] = $this;
646
        $serviceProperty->setValue($this->container, $containerServices);
647
        $serviceProperty->setAccessible(false);
648
649
        foreach ($modulesToLoad as $identifier => $parameters) {
650
            $instance = $this->container->get($identifier);
651
652
            // Set composer parameters
653
            $instance->composerParameters = $parameters;
654
655
            // TODO: Change event signature to single approach
656
            // Fire core module load event
657
            Event::fire('core.module_loaded', [$identifier, &$instance]);
658
659
            // Signal core module configure event
660
            Event::signal('core.module.configure', [&$instance, $identifier]);
661
662
            if ($instance instanceof PreparableInterface) {
663
                // Call module preparation handler
664
                if (!$instance->prepare()) {
665
                    //throw new \Exception($identifier.' - Module preparation stage failed');
666
                }
667
            }
668
669
            // Try to set module parent module
670
            $instance->parent = $this->getClassParentModule(get_parent_class($instance));
671
        }
672
673
    }
674
675
    /**
676
     * Find parent module by OOP class inheritance.
677
     *
678
     * @param string $className Class name for searching parent modules
679
     * @param array  $ignoredClasses Collection of ignored classes
680
     *
681
     * @return null|mixed Parent service instance if present
682
     */
683
    protected function getClassParentModule(
684
        $className,
685
        array $ignoredClasses = [ExternalModule::class, CompressableExternalModule::class, Service::class, CompressableService::class]
686
    ) {
687
        // Skip ignored class names
688
        if (!in_array($className, $ignoredClasses, true)) {
689
            // Iterate loaded services
690
            foreach ($this->getContainer()->getServices('module') as $service) {
691
                if (get_class($service) === $className) {
692
                    return $service;
693
                }
694
            }
695
        }
696
697
        return null;
698
    }
699
700
    /**
701
     * Load module from path to core.
702
     *
703
     * @param string $path       Path for module loading
704
     * @param array  $parameters Collection of loading parameters
705
     *
706
     * @return string module name
707
     * @throws \samsonphp\core\exception\CannotLoadModule
708
     */
709
    public function load($path, $parameters = array())
710
    {
711
        $name = '';
712
        // Check path
713
        if (file_exists($path)) {
714
715
            /** @var ResourceMap $resourceMap Gather all resources from path */
716
            $resourceMap = ResourceMap::get($path);
717
718
            foreach ($resourceMap->classes as $classPath => $className) {
719
                $this->classes[str_replace(['\\', '/'], '_', $className)] = $className;
720
            }
721
722
723
            if (isset($resourceMap->module[0])) {
724
725
726
                /** @var string $controllerPath Path to module controller file */
727
                $controllerPath = $resourceMap->module[1];
728
729
                /** @var string $moduleClass Name of module controller class to load */
730
                $moduleClass = $resourceMap->module[0];
731
732
                // Require module controller class into PHP
733
                if (file_exists($controllerPath)) {
734
                    require_once($controllerPath);
735
                }
736
737
                // TODO: this should be done via composer autoload file field
738
                // Iterate all function-style controllers and require them
739
                foreach ($resourceMap->controllers as $controller) {
740
                    require_once($controller);
741
                }
742
743
                $reflection = new \ReflectionClass($moduleClass);
744
                $name = $reflection->getDefaultProperties();
745
                $name = $this->createMetadata($moduleClass, $name['id'] ?? $moduleClass, $path);
746
747
                $this->classes[$name] = $moduleClass;
748
749
                /*$this->initModule(
750
                    new $moduleClass($path, $resourceMap, $this),
751
                    $parameters
752
                );*/
753
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
754
                $name = $this->createMetadata(VirtualModule::class, $parameters['module_id'], $path);
755
            }
756
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
757
        } else {
758
            throw new CannotLoadModule($path);
759
        }
760
761
        return $name;
762
    }
763
    //[PHPCOMPRESSOR(remove,end)]
764
765
    /** Магический метод для десериализации объекта */
766
    public function __wakeup()
767
    {
768
        $this->active = &$this->module_stack['local'];
769
    }
770
771
    /** Магический метод для сериализации объекта */
772
    public function __sleep()
773
    {
774
        return array('module_stack', 'render_mode');
775
    }
776
777
    protected function createMetadata($class, $name, $path, $scope = 'module')
778
    {
779
        $metadata = new ClassMetadata();
780
        $class = ltrim($class, '\\');
781
        $name = strtolower(ltrim($name, '\\'));
782
        $metadata->className = $class;
783
        $metadata->name = str_replace(['\\', '/'], '_', $name ?? $class);
784
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
785
        $metadata->scopes[] = $scope;
786
        $metadata->propertiesMetadata['system'] = new PropertyMetadata($metadata);
787
        $metadata->propertiesMetadata['system']->name = 'system';
788
        $metadata->propertiesMetadata['system']->dependency = 'core';
789
        $metadata->propertiesMetadata['system']->isPublic = false;
790
        $metadata->propertiesMetadata['resourceMap'] = new PropertyMetadata($metadata);
791
        $metadata->propertiesMetadata['resourceMap']->name = 'resourceMap';
792
        $metadata->propertiesMetadata['resourceMap']->dependency = 'resource_map';
793
        $metadata->propertiesMetadata['resourceMap']->isPublic = false;
794
795
        // TODO: Now we need to remove and change constructors
796
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
797
798
        $methodParameters = [];
799
        foreach ((new \ReflectionMethod($class, '__construct'))->getParameters() as $parameter) {
800
            $methodParameters[] = $parameter->getName();
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
801
        }
802
803 View Code Duplication
        if (in_array('path', $methodParameters, true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
804
            $metadata->methodsMetadata['__construct']->dependencies['path'] = $path;
805
        }
806
        if (in_array('resources', $methodParameters, true)) {
807
            $metadata->methodsMetadata['__construct']->dependencies['resources'] = 'resource_map';
808
        }
809 View Code Duplication
        if (in_array('system', $methodParameters, true)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
810
            $metadata->methodsMetadata['__construct']->dependencies['system'] = 'core';
811
        }
812
813
        $this->metadataCollection[$metadata->name] = $metadata;
814
815
        return $metadata->name;
816
    }
817
}
818