Completed
Push — master ( b63afe...96ad3a )
by
unknown
10:09
created

Core::loadMetadata()   F

Complexity

Conditions 19
Paths 2520

Size

Total Lines 115
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 222.519

Importance

Changes 0
Metric Value
cc 19
eloc 58
c 0
b 0
f 0
nc 2520
nop 3
dl 0
loc 115
ccs 8
cts 46
cp 0.1739
crap 222.519
rs 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php 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['\\'.$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['\\' . $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
            foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
607
                if (array_key_exists($propertyMetadata->dependency, $implementsByAlias)) {
608
                    $propertyMetadata->dependency = $implementsByAlias[$propertyMetadata->dependency][0];
609
                } elseif (array_key_exists($propertyMetadata->dependency, $serviceAliasesByClass)) {
610
                    $propertyMetadata->dependency = $serviceAliasesByClass[$propertyMetadata->dependency][0];
611
                } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
612
613
                }
614
            }
615
            foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
616
                foreach ($methodMetadata->dependencies as $argument => $dependency) {
617
                    if (array_key_exists($dependency, $implementsByAlias)) {
618
                        $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
619
                    } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
620
                        $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0];
621
                    } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
622
623
                    }
624
                }
625
            }
626
        }
627
628
        // Generate XML configs
629
        (new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_');
630
631
        // Load container class
632
        $containerPath = $this->path().'www/cache/Container.php';
633
        file_put_contents($containerPath, $this->builder->build($metadataCollection));
634
        require_once($containerPath);
635
636
        // Inject current core into container
637
        $this->container = new \Container();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Container() of type object<Container> is incompatible with the declared type object<samsonframework\di\ContainerInterface> of property $container.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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