Completed
Push — master ( 5f4c45...906005 )
by Vitaly
06:23
created

Core::loadMetadata()   F

Complexity

Conditions 23
Paths 14880

Size

Total Lines 148
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 552

Importance

Changes 0
Metric Value
cc 23
eloc 71
c 0
b 0
f 0
nc 14880
nop 5
dl 0
loc 148
ccs 0
cts 0
cp 0
crap 552
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 samsonframework\container\Builder;
13
use samsonframework\container\ContainerBuilderInterface;
14
use samsonframework\container\ContainerInterface;
15
use samsonframework\container\metadata\ClassMetadata;
16
use samsonframework\container\metadata\MethodMetadata;
17
use samsonframework\container\metadata\PropertyMetadata;
18
use samsonframework\containerannotation\AnnotationClassResolver;
19
use samsonframework\containerannotation\AnnotationMetadataCollector;
20
use samsonframework\containerannotation\AnnotationMethodResolver;
21
use samsonframework\containerannotation\AnnotationPropertyResolver;
22
use samsonframework\containerannotation\AnnotationResolver;
23
use samsonframework\containerannotation\Injectable;
24
use samsonframework\containerannotation\InjectArgument;
25
use samsonframework\containerannotation\Service;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, samson\core\Service.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
26
use samsonframework\containercollection\attribute\ArrayValue;
27
use samsonframework\containercollection\attribute\ClassName;
28
use samsonframework\containercollection\attribute\Name;
29
use samsonframework\containercollection\attribute\Value;
30
use samsonframework\containercollection\CollectionClassResolver;
31
use samsonframework\containercollection\CollectionMethodResolver;
32
use samsonframework\containercollection\CollectionParameterResolver;
33
use samsonframework\containercollection\CollectionPropertyResolver;
34
use samsonframework\containerxml\XmlMetadataCollector;
35
use samsonframework\containerxml\XmlResolver;
36
use samsonframework\core\PreparableInterface;
37
use samsonframework\core\SystemInterface;
38
use samsonframework\resource\ResourceMap;
39
use samsonphp\config\Scheme;
40
use samsonphp\core\exception\CannotLoadModule;
41
use samsonphp\core\exception\ViewPathNotFound;
42
use samsonphp\core\Module;
43
use samsonphp\event\Event;
44
45
/**
46
 * SamsonPHP Core.
47
 *
48
 * @author Vitaly Iegorov <[email protected]>
49
 * @Service("core")
50
 */
51
class Core implements SystemInterface
52
{
53
    /** @deprecated Standard algorithm for view rendering */
54
    const RENDER_STANDART = 1;
55
    /** @deprecated View rendering algorithm from array of view variables */
56
    const RENDER_VARIABLE = 3;
57
    /** @deprecated @var  ResourceMap Current web-application resource map */
58
    public $map;
59
    /** @deprecated @var string Path to current web-application */
60
    public $system_path = __SAMSON_CWD__;
61
62
    /* Rendering models */
63
    /** @deprecated @var string View path loading mode */
64
    public $render_mode = self::RENDER_STANDART;
65
    /** @var ContainerInterface */
66
    protected $container;
67
    /** @var ClassMetadata[] */
68
    protected $metadataCollection = [];
69
    /** @var ContainerBuilderInterface */
70
    protected $builder;
71
    /** @var string Current system environment */
72
    protected $environment;
73
    protected $classes = [];
74
    /** @var Module Pointer to current active module */
75
    protected $active = null;
76
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
77
    protected $async = false;
78
    /** @var string Path to main system template */
79
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
80
81
    /**
82
     * Core constructor.
83
     *
84
     * @param ContainerBuilderInterface $builder Container builder
85
     * @param ResourceMap|null $map system resources
86
     * @InjectArgument(builder="\samsonframework\container\ContainerBuilderInterface")
87
     * @InjectArgument(builder="\samsonframework\core\ResourceInterface")
88
     */
89
    public function __construct(ContainerBuilderInterface $builder, ResourceMap $map)
0 ignored issues
show
Unused Code introduced by
The parameter $map is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
90
    {
91
        $this->builder = $builder;
92
        // Get correct web-application path
93
        $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...
94
95
        // Get web-application resource map
96
        $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...
97
98
        // Temporary add template worker
99
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
100
101
        // Fire core creation event
102
        Event::fire('core.created', array(&$this));
103
104
        // Signal core configure event
105
        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...
106
    }
107
108
    /**
109
     * Generic wrap for Event system subscription.
110 1
     * @see \samson\core\\samsonphp\event\Event::subscribe()
111
     *
112
     * @param string   $key     Event identifier
113
     * @param callable $handler Event handler
114
     * @param array    $params  Event parameters
115
     *
116
     * @return $this Chaining
117
     */
118
    public function subscribe($key, $handler, $params = array())
119
    {
120
        Event::subscribe($key, $handler, $params);
121 1
122
        return $this;
123
    }
124
125
    /**
126
     * Change current system working environment or receive
127
     * current system enviroment if no arguments are passed.
128
     *
129
     * @param string $environment Environment identifier
130
     *
131
     * TODO: Function has two different logics - needs to be changed!
132
     * @return $this|string Chaining or current system environment
133
     */
134
    public function environment($environment = Scheme::BASE)
135
    {
136
        if (func_num_args() !== 0) {
137
            $this->environment = $environment;
138
139
            // Signal core environment change
140
            Event::signal('core.environment.change', array($environment, &$this));
141
            return $this;
142
        }
143
144
        return $this->environment;
145
    }
146
147
    /**
148
     * Generate special response header triggering caching mechanisms
149
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
150
     * @param string $accessibility Cache-control accessibility value(default public)
151
     */
152
    public function cached($cacheLife = 3600, $accessibility = 'public')
153
    {
154
        static $cached;
155
        // Protect sending cached headers once
156
        if (!isset($cached) or $cached !== true) {
157
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
158
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
159
            header('Pragma: cache');
160
161
            $cached = true;
162
        }
163
    }
164
165
    /**
166
     * Set asynchronous mode.
167
     * This mode will not output template and will just path everything that
168
     * was outputted to client.
169
     *
170
     * @param bool $async True to switch to asynchronous output mode
171
     *
172
     * @return $this Chaining
173
     */
174
    public function async($async)
175
    {
176
        $this->async = $async;
177
178
        return $this;
179
    }
180
181
    /**    @see iModule::active() */
182
    public function &active(&$module = null)
183
    {
184
        // Сохраним старый текущий модуль
185
        $old = &$this->active;
186
187
        // Если передано значение модуля для установки как текущий - проверим и установим его
188
        if (isset($module)) {
189
            $this->active = &$module;
190
        }
191
192
        // Вернем значение текущего модуля
193
        return $old;
194
    }
195
196
    /**
197
     * Retrieve module instance by identifier.
198
     *
199
     * @param string|null $module Module identifier
200
     *
201
     * @return null|Module Found or active module
202
     */
203
    public function &module($module = null)
204
    {
205
        $return = null;
206
207
        // Ничего не передано - вернем текущуй модуль системы
208
        if (!isset($module) && isset($this->active)) {
209
            $return = &$this->active;
210
        } elseif (is_object($module)) {
211
            $return = &$module;
212
        } elseif (is_string($module)) {
213
            $return = $this->container->get($module);
214
        }
215
216
        // Ничего не получилось вернем ошибку
217
        if ($return === null) {
218
            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...
219
        }
220
221
        return $return;
222
    }
223
224
    /**
225
     * Unload module from core.
226
     *
227
     * @param string $moduleID Module identifier
228
     */
229
    public function unload($moduleID)
230
    {
231
        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...
232
            unset($this->module_stack[$moduleID]);
233
        }
234
    }
235
236
    /**
237
     * Insert generic html template tags and data
238
     *
239
     * @param string $templateHtml Generated HTML
240
     *
241
     * @deprecated Must be moved to a new HTML output object
242
     * @return mixed Changed HTML template
243
     */
244
    public function generateTemplate(&$templateHtml)
245
    {
246
        // Добавим путь к ресурсам для браузера
247
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
248
        // Добавим отметку времени для JavaScript
249
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
250
251
        // Добавим поддержку HTML для старых IE
252
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
253
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
254
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
255
        $headHtml .= "\n" . '<![endif]-->';
256
257
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
258
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
259
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
260
261
        // Вставим указатель JavaScript ресурсы в конец HTML документа
262
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
263
264 2
        return $templateHtml;
265
    }
266
267
    /**
268
     * Start SamsonPHP framework.
269
     *
270
     * @param string $default Default module identifier
271 2
     *
272
     * @throws ViewPathNotFound
273
     */
274 2
    public function start($default)
275
    {
276
        // TODO: Change ExternalModule::init() signature
277
        // Fire core started event
278 2
        Event::fire('core.started');
279
280 2
        // TODO: Does not see why it should be here
281
        // Set main template path
282 2
        $this->template($this->template_path);
283 2
284
        // Security layer
285
        $securityResult = true;
286 2
        // Fire core security event
287
        Event::fire('core.security', array(&$this, &$securityResult));
288 2
289
        /** @var mixed $result External route controller action result */
290 2
        $result = false;
291
292
        // If we have passed security application layer
293
        if ($securityResult) {
294
            // Fire core routing event - go to routing application layer
295 2
            Event::signal('core.routing', array(&$this, &$result, $default));
296
        }
297
298
        // If no one has passed back routing callback
299
        if (!isset($result) || $result === false) {
300
            // Fire core e404 - routing failed event
301
            $result = Event::signal('core.e404', array(url()->module, url()->method));
302
        }
303
304
        // Response
305
        $output = '';
306
307
        // If this is not asynchronous response and controller has been executed
308
        if (!$this->async && ($result !== false)) {
309
            // Store module data
310
            $data = $this->active->toView();
311
312
            // Render main template
313
            $output = $this->render($this->template_path, $data);
314
315
            // Fire after render event
316
            Event::fire('core.rendered', array(&$output));
317
        }
318
319
        // Output results to client
320 2
        echo $output;
321
322
        // Fire ended event
323 2
        Event::fire('core.ended', array(&$output));
324
    }
325
326 2
    /**	@see iCore::template() */
327
    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...
328
    {
329 2
        // Если передан аргумент
330
        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...
331
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
332 2
        }
333
334
        // Аргументы не переданы - вернем текущий путь к шаблону системы
335 2
        return $this->template_path;
336
    }
337
338 2
    /**
339
     * Render file to a buffer.
340
     *
341
     * @param string $view Path to file
342
     * @param array  $data Collection of variables to path to file
343
     *
344
     * @return string Rendered file contents
345
     * @throws ViewPathNotFound
346
     */
347
    public function render($view, $data = array())
348
    {
349
        // TODO: Make rendering as external system, to split up these 3 rendering options
350
351
        // Объявить ассоциативный массив переменных в данном контексте
352 7
        if (is_array($data)) {
353
            extract($data);
354 7
        }
355
356 7
        // Начать вывод в буффер
357
        ob_start();
358
359
        // Path to another template view, by default we are using default template folder path,
360
        // for meeting first condition
361
        $templateView = $view;
362
363
        if (locale() != SamsonLocale::DEF) {
364
            // Modify standard view path with another template
365
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
366
        }
367
368
        // Depending on core view rendering model
369
        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...
370
            // Standard algorithm for view rendering
371
            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...
372
                // Trying to find another template path, by default it's an default template path
373
                if (file_exists($templateView)) {
374
                    include($templateView);
375
                } elseif (file_exists($view)) {
376
                    // If another template wasn't found - we will use default template path
377
                    include($view);
378
                } else { // Error no template view was found
379
                    throw(new ViewPathNotFound($view));
380
                }
381
                break;
382
383
            // View rendering algorithm from array of view variables
384
            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...
385
                // Collection of views
386
                $views = &$GLOBALS['__compressor_files'];
387
                // Trying to find another template path, by default it's an default template path
388
                if (isset($views[$templateView])) {
389
                    eval(' ?>' . $views[$templateView] . '<?php ');
390
                } elseif (isset($views[$view])) {
391
                    // If another template wasn't found - we will use default template path
392
                    eval(' ?>' . $views[$view] . '<?php ');
393
                } else { // Error no template view was found
394
                    throw(new ViewPathNotFound($view));
395
                }
396
                break;
397
        }
398
399
        // Получим данные из буффера вывода
400
        $html = ob_get_contents();
401
402
        // Очистим буффер
403 2
        ob_end_clean();
404
405
        // Fire core render event
406 2
        Event::fire('core.render', array(&$html, &$data, &$this->active));
407
408
        ////elapsed('End rendering '.$__view);
409 2
        return $html;
410 2
    }
411 2
412
    /**
413
     * Load system from composer.json
414 2
     * @param string $dependencyFilePath Path to dependencies file
415
     * @return $this Chaining
416
     */
417
    public function composer($dependencyFilePath = null)
418
    {
419
        $composerModules = array();
420
421
        Event::fire(
422
            'core.composer.create',
423
            array(
424
                &$composerModules,
425
                isset($dependencyFilePath) ? $dependencyFilePath : $this->system_path,
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...
426
                array(
427
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'),
428
                    'ignoreKey' => 'samson_module_ignore',
429
                    'includeKey' => 'samson_module_include'
430
                )
431
            )
432
        );
433
434
        $modulesToLoad = [];
435
        $preClasses = [
436
            str_replace(['\\', '/'], '_', __CLASS__) => __CLASS__,
437
            str_replace(['\\', '/'], '_', ResourceMap::class) => ResourceMap::class
438
        ];
439
//        $this->classes = array_merge($this->classes, $preClasses);
440
        $preModules = [];
441
442
        // Iterate requirements
443
        foreach ($composerModules as $requirement => $parameters) {
444
            $modulePath = __SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement;
445
            $moduleName = $this->load($modulePath,
446
            array_merge(
447
                is_array($parameters) ? $parameters : array($parameters),
448
                array('module_id' => $requirement)
449
            ));
450
451
            // Gather pre container modules and their classes
452
            if (array_key_exists('samsonframework_precontainer', $parameters)) {
453
                $preModules[$moduleName] = $parameters;
454
455
                // Add module classes
456
                foreach (ResourceMap::get($modulePath)->modules as $module) {
457
                    $preClasses[str_replace(['\\', '/'], '_', $module[0])] = $module[0];
458
                }
459
460
                // Add other module classes
461
                foreach (ResourceMap::get($modulePath)->classes as $className) {
462
                    $preClasses[str_replace(['\\', '/'], '_', $className)] = $className;
463
                }
464
            }
465
466
467
            $modulesToLoad[$moduleName] = $parameters;
468
        }
469
470
        // Create local module and set it as active
471
        $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...
472
473
        // TODO: This should be changed to one single logic
474
        // Require all local module model files
475
        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...
476
            // TODO: Why have to require once?
477
            require_once($model);
478
        }
479
480
        // Create all local modules
481
        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...
482
            // Require class into PHP
483
            require($controller);
484
485
            //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
486
487
            $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...
488
        }
489
490
        $localModulesPath = '../src';
491
        ResourceMap::get('cache');
492
        // TODO: Nested modules relation
493
        for ($i = 0; $i < 2; $i++) {
494
            $resourceMap = ResourceMap::get($localModulesPath);
495
496
            foreach ($resourceMap->modules as $moduleFile) {
497
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
498
                $modulePath = explode('/', $modulePath);
499
                $modulePath = $localModulesPath . '/' . $modulePath[1];
500
                $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 443. 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...
501
                $modulesToLoad[$moduleName] = $parameters;
502
            }
503
        }
504
505
        $preMetadataCollection = [];
506
        foreach ($preModules as $moduleName => $parameters) {
507
            $preMetadataCollection[$moduleName] = $this->metadataCollection[$moduleName];
508
        }
509
510
        $configPath = $this->path() . 'app/config/config.xml';
511
        if (file_exists($configPath)) {
512
            $xmlConfigurator = new XmlResolver(new CollectionClassResolver([
513
                \samsonframework\containercollection\attribute\Scope::class,
514
                Name::class,
515
                ClassName::class,
516
                \samsonframework\containercollection\attribute\Service::class
517
            ]), new CollectionPropertyResolver([
518
                ClassName::class,
519
                Value::class
520
            ]), new CollectionMethodResolver([], new CollectionParameterResolver([
521
                ClassName::class,
522
                Value::class,
523
                ArrayValue::class,
524
                \samsonframework\containercollection\attribute\Service::class
525
            ])));
526
527
            $xmlCollector = new XmlMetadataCollector($xmlConfigurator);
528
529
            $preMetadataCollection = $xmlCollector->collect(file_get_contents($configPath), $preMetadataCollection);
530
        }
531
532
        $preContainer = $this->loadMetadata($preMetadataCollection, $preModules, $preClasses, 'ContainerPreLoad');
533
        /** @var ContainerInterface container */
534
        $this->container = $this->loadMetadata($this->metadataCollection, $modulesToLoad, $this->classes, 'Container', $preContainer);
535
536
        $this->active = $this->container->getLocal();
0 ignored issues
show
Bug introduced by
The method getLocal() does not seem to exist on object<samsonframework\c...ner\ContainerInterface>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
537
538
        return $this;
539
    }
540
541
    /**
542
     * Load module from path to core.
543
     *
544
     * @param string $path       Path for module loading
545
     * @param array  $parameters Collection of loading parameters
546
     *
547
     * @return string module name
548
     * @throws \samsonphp\core\exception\CannotLoadModule
549
     */
550
    public function load($path, $parameters = array())
551
    {
552
        $name = '';
553
        // Check path
554
        if (file_exists($path)) {
555
556
            /** @var ResourceMap $resourceMap Gather all resources from path */
557
            $resourceMap = ResourceMap::get($path);
558 7
559
            foreach ($resourceMap->classes as $classPath => $className) {
560
                $this->classes[str_replace(['\\', '/'], '_', $className)] = $className;
561 7
            }
562
563
            if (isset($resourceMap->module[0])) {
564 7
                /** @var string $controllerPath Path to module controller file */
565
                $controllerPath = $resourceMap->module[1];
566
567 7
                /** @var string $moduleClass Name of module controller class to load */
568
                $moduleClass = $resourceMap->module[0];
569
570 7
                // Require module controller class into PHP
571
                if (file_exists($controllerPath)) {
572
                    require_once($controllerPath);
573 7
                }
574
575
                // TODO: this should be done via composer autoload file field
576 7
                // Iterate all function-style controllers and require them
577 7
                foreach ($resourceMap->controllers as $controller) {
578
                    require_once($controller);
579
                }
580
581
                $reflection = new \ReflectionClass($moduleClass);
582
                $name = $reflection->getDefaultProperties();
583
                $name = $this->createMetadata($moduleClass, $name['id'] ?? $moduleClass, $path);
584
585
                $this->classes[$name] = $moduleClass;
586
587
                /*$this->initModule(
588
                    new $moduleClass($path, $resourceMap, $this),
589
                    $parameters
590
                );*/
591
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
592
                $name = $this->createMetadata(VirtualModule::class, $parameters['module_id'], $path);
593
            }
594
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
595
        } else {
596
            throw new CannotLoadModule($path);
597
        }
598
599
        return $name;
600
    }
601
602
    //[PHPCOMPRESSOR(remove,start)]
603
604
    protected function createMetadata($class, $name, $path, $scope = 'module')
605
    {
606
        $metadata = new ClassMetadata();
607
        $class = ltrim($class, '\\');
608
        $name = strtolower(ltrim($name, '\\'));
609
        $metadata->className = $class;
610
        $metadata->name = str_replace(['\\', '/'], '_', $name ?? $class);
611
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
612
        $metadata->scopes[] = $scope;
613
        $metadata->propertiesMetadata['system'] = new PropertyMetadata($metadata);
614
        $metadata->propertiesMetadata['system']->name = 'system';
615
        $metadata->propertiesMetadata['system']->dependency = __CLASS__;
616
        $metadata->propertiesMetadata['system']->isPublic = false;
617
        $metadata->propertiesMetadata['resourceMap'] = new PropertyMetadata($metadata);
618
        $metadata->propertiesMetadata['resourceMap']->name = 'resourceMap';
619
        $metadata->propertiesMetadata['resourceMap']->dependency = ResourceMap::class;
620
        $metadata->propertiesMetadata['resourceMap']->isPublic = false;
621
622
        // TODO: Now we need to remove and change constructors
623
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
624
625
        // Iterate constructor arguments to preserve arguments order and inject dependencies
626
        foreach ((new \ReflectionMethod($class, '__construct'))->getParameters() as $parameter) {
627
            if ($parameter->getName() === 'path') {
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
628
                $metadata->methodsMetadata['__construct']->dependencies['path'] = $path;
629
            } elseif ($parameter->getName() === 'resources') {
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
630
                $metadata->methodsMetadata['__construct']->dependencies['resources'] = ResourceMap::class;
631
            } elseif ($parameter->getName() === 'system') {
0 ignored issues
show
Bug introduced by
Consider using $parameter->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
632
                $metadata->methodsMetadata['__construct']->dependencies['system'] = '\\' . SystemInterface::class;
633
            } elseif (!$parameter->isOptional()) {
634
                $metadata->methodsMetadata['__construct']->dependencies[$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...
635
            }
636
        }
637
638
        $this->metadataCollection[$metadata->name] = $metadata;
639
640
        return $metadata->name;
641
    }
642
643
    /** @see iCore::path() */
644
    public function path($path = null)
645
    {
646
        // Если передан аргумент
647
        if (func_num_args()) {
648
            // Сформируем новый относительный путь к главному шаблону системы
649
            $this->template_path = $path . $this->template_path;
650
651
            // Сохраним относительный путь к Веб-приложению
652
            $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...
653
654
            // Продолжил цепирование
655
            return $this;
656
        }
657
658
        // Вернем текущее значение
659
        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...
660
    }
661
662
    /**
663
     * @param ClassMetadata[] $metadataCollection
664
     * @param array $modulesToLoad
665
     * @param array $classes
666
     * @param string $containerName
667
     * @param ContainerInterface $parentContainer
668
     * @return ContainerInterface
669
     */
670
    protected function loadMetadata(array $metadataCollection, array $modulesToLoad, array $classes, $containerName = 'Container', ContainerInterface $parentContainer = null) : ContainerInterface
671
    {
672
        static $implementsByAlias;
673
        static $serviceAliasesByClass;
674
675
        $containerPath = $this->path().'www/cache/'.$containerName.'.php';
676
        //if (!file_exists($containerPath)) {
677
678
679
            // Load annotation and parse classes
680
            new Injectable();
681
            new InjectArgument(['var' => 'type']);
682
            new Service(['value' => '']);
683
684
            $reader = new AnnotationReader();
685
            $resolver = new AnnotationResolver(
686
                new AnnotationClassResolver($reader),
687
                new AnnotationPropertyResolver($reader),
688
                new AnnotationMethodResolver($reader)
689
            );
690
691
            $annotationCollector = new AnnotationMetadataCollector($resolver);
692
693
694
            // Rewrite collection by entity name
695
            $newMetadataCollection = [];
696
            foreach ($annotationCollector->collect($classes, $metadataCollection) as $metadata) {
697
                $newMetadataCollection[$metadata->name] = $metadata;
698
            }
699
            $metadataCollection = $newMetadataCollection;
700
701
            // Regroup classes metadata by class name instead of alias
702
            $classesMetadata = [];
703
            foreach ($metadataCollection as $alias => $classMetadata) {
704
                $classesMetadata[$classMetadata->className] = $classMetadata;
705
            }
706
707
            // Gather all interface implementations
708
            $implementsByAlias = $implementsByAlias ?? [];
709
            foreach (get_declared_classes() as $class) {
710
                $classImplements = class_implements($class);
711
                foreach (get_declared_interfaces() as $interface) {
712
                    if (in_array($interface, $classImplements, true)) {
713
                        if (array_key_exists($class, $classesMetadata)) {
714
                            $implementsByAlias[strtolower('\\' . $interface)][] = $classesMetadata[$class]->name;
715
                        }
716
                    }
717
                }
718
            }
719
720
            // Gather all class implementations
721
            $serviceAliasesByClass = $serviceAliasesByClass ?? [];
722
            foreach (get_declared_classes() as $class) {
723
                if (array_key_exists($class, $classesMetadata)) {
724
                    $serviceAliasesByClass[strtolower('\\' . $class)][] = $classesMetadata[$class]->name;
725
                }
726
            }
727
728
            /**
729
             * TODO: now we need to implement not forcing to load fixed dependencies into modules
730
             * to give ability to change constructors and inject old variable into properties
731
             * and them after refactoring remove them. With this we can only specify needed dependencies
732
             * in new modules, and still have old ones working.
733
             */
734
735
            foreach ($metadataCollection as $alias => $metadata) {
736
                foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
737
                    if (is_string($propertyMetadata->dependency)) {
738
                        $dependency = strtolower($propertyMetadata->dependency);
739
                        if (array_key_exists($dependency, $implementsByAlias)) {
740
                            $propertyMetadata->dependency = $implementsByAlias[$dependency][0];
741
                        } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
742
                            $propertyMetadata->dependency = $serviceAliasesByClass[$dependency][0];
743
                        } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
744
745
                        }
746
                    }
747
                }
748
                foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
749
                    foreach ($methodMetadata->dependencies as $argument => $dependency) {
750
                        if (is_string($dependency)) {
751
                            $dependency = strtolower($dependency);
752
                            if (array_key_exists($dependency, $implementsByAlias)) {
753
                                $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
754
                                //$methodMetadata->parametersMetadata[$argument]->dependency = $implementsByAlias[$dependency][0];
755
                            } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
756
                                $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0];
757
                                //$methodMetadata->parametersMetadata[$argument]->dependency = $serviceAliasesByClass[$dependency][0];
758
                            } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
759
760
                            }
761
                        }
762
                    }
763
                }
764
            }
765
766
            // Generate XML configs
767
            //(new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_');
768
769
            // Load container class
770
            file_put_contents($containerPath,
771
                $this->builder->build($metadataCollection, $containerName, '', $parentContainer));
0 ignored issues
show
Unused Code introduced by
The call to ContainerBuilderInterface::build() has too many arguments starting with $parentContainer.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
772
773
        //}
774
775
        require_once($containerPath);
776
777
778
        // Inject current core into container
779
        /** @var ContainerInterface $container */
780
        $this->container = new $containerName();
781
        $containerReflection = new \ReflectionClass(get_class($this->container));
782
        $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES);
783
        $serviceProperty->setAccessible(true);
784
        $containerServices = $serviceProperty->getValue($this->container);
785
        $containerServices['core'] = $this;
786
        $serviceProperty->setValue($this->container, $containerServices);
787
        $serviceProperty->setAccessible(false);
788
        if ($parentContainer !== null) {
789
            $this->container->delegate($parentContainer);
790
        }
791
792
        foreach ($modulesToLoad as $identifier => $parameters) {
793
            $instance = $this->container->get($identifier);
794
795
            // Set composer parameters
796
            $instance->composerParameters = $parameters;
797
798
            // TODO: Change event signature to single approach
799
            // Fire core module load event
800
            Event::fire('core.module_loaded', [$identifier, &$instance]);
801
802
            // Signal core module configure event
803
            Event::signal('core.module.configure', [&$instance, $identifier]);
804
805
            if ($instance instanceof PreparableInterface) {
806
                // Call module preparation handler
807
                if (!$instance->prepare()) {
808
                    //throw new \Exception($identifier.' - Module preparation stage failed');
809
                }
810
            }
811
812
            // Try to set module parent module
813
            $instance->parent = $this->getClassParentModule(get_parent_class($instance));
814
        }
815
816
        return $this->container;
817
    }
818
819
    /**
820
     * Find parent module by OOP class inheritance.
821
     *
822
     * @param string $className Class name for searching parent modules
823
     * @param array  $ignoredClasses Collection of ignored classes
824
     *
825
     * @return null|mixed Parent service instance if present
826
     */
827
    protected function getClassParentModule(
828
        $className,
829
        array $ignoredClasses = [ExternalModule::class, CompressableExternalModule::class, Service::class, CompressableService::class]
830
    ) {
831
        // Skip ignored class names
832
        if (!in_array($className, $ignoredClasses, true)) {
833
            // Iterate loaded services
834
            foreach ($this->getContainer()->getServices('module') as $service) {
0 ignored issues
show
Unused Code introduced by
The call to ContainerInterface::getServices() has too many arguments starting with 'module'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
835
                if (get_class($service) === $className) {
836
                    return $service;
837
                }
838
            }
839
        }
840
841
        return null;
842
    }
843
    //[PHPCOMPRESSOR(remove,end)]
844
845
    /** @return \Container Get system container */
846
    public function getContainer()
847
    {
848
        return $this->container;
849
    }
850
851
    /** Магический метод для десериализации объекта */
852
    public function __wakeup()
853
    {
854
        $this->active = &$this->module_stack['local'];
855
    }
856
857
    /** Магический метод для сериализации объекта */
858
    public function __sleep()
859
    {
860
        return array('module_stack', 'render_mode');
861
    }
862
}
863