Completed
Push — master ( d0b7e4...5f4c45 )
by Vitaly
09:11
created

Core::loadMetadata()   D

Complexity

Conditions 23
Paths 16

Size

Total Lines 141
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 552

Importance

Changes 0
Metric Value
cc 23
eloc 69
c 0
b 0
f 0
nc 16
nop 5
dl 0
loc 141
ccs 0
cts 0
cp 0
crap 552
rs 4.6303

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
            if (array_key_exists('samsonframework_precontainer', $parameters)) {
451
                $preModules[$moduleName] = $parameters;
452
                foreach (ResourceMap::get($modulePath)->classes as $className) {
453
                    $preClasses[str_replace(['\\', '/'], '_', $className)] = $className;
454
                }
455
            }
456
            $modulesToLoad[$moduleName] = $parameters;
457
        }
458
459
        // Create local module and set it as active
460
        $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...
461
462
        // TODO: This should be changed to one single logic
463
        // Require all local module model files
464
        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...
465
            // TODO: Why have to require once?
466
            require_once($model);
467
        }
468
469
        // Create all local modules
470
        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...
471
            // Require class into PHP
472
            require($controller);
473
474
            //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
475
476
            $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...
477
        }
478
479
        $localModulesPath = '../src';
480
        ResourceMap::get('cache');
481
        // TODO: Nested modules relation
482
        for ($i = 0; $i < 2; $i++) {
483
            $resourceMap = ResourceMap::get($localModulesPath);
484
485
            foreach ($resourceMap->modules as $moduleFile) {
486
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
487
                $modulePath = explode('/', $modulePath);
488
                $modulePath = $localModulesPath . '/' . $modulePath[1];
489
                $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...
490
                $modulesToLoad[$moduleName] = $parameters;
491
            }
492
        }
493
494
        $preMetadataCollection = [];
495
        foreach ($preModules as $moduleName => $parameters) {
496
            $preMetadataCollection[$moduleName] = $this->metadataCollection[$moduleName];
497
        }
498
499
        $configPath = $this->path() . 'app/config/config.xml';
500
        if (file_exists($configPath)) {
501
            $xmlConfigurator = new XmlResolver(new CollectionClassResolver([
502
                \samsonframework\containercollection\attribute\Scope::class,
503
                Name::class,
504
                ClassName::class,
505
                \samsonframework\containercollection\attribute\Service::class
506
            ]), new CollectionPropertyResolver([
507
                ClassName::class,
508
                Value::class
509
            ]), new CollectionMethodResolver([], new CollectionParameterResolver([
510
                ClassName::class,
511
                Value::class,
512
                ArrayValue::class,
513
                \samsonframework\containercollection\attribute\Service::class
514
            ])));
515
516
            $xmlCollector = new XmlMetadataCollector($xmlConfigurator);
517
518
            $preMetadataCollection = $xmlCollector->collect(file_get_contents($configPath), $preMetadataCollection);
519
        }
520
521
        $preContainer = $this->loadMetadata($preMetadataCollection, $preModules, $preClasses, 'ContainerPreLoad');
522
        /** @var ContainerInterface container */
523
        $this->container = $this->loadMetadata($this->metadataCollection, $modulesToLoad, $this->classes, 'Container', $preContainer);
524
525
        $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...
526
527
        return $this;
528
    }
529
530
    /**
531
     * Load module from path to core.
532
     *
533
     * @param string $path       Path for module loading
534
     * @param array  $parameters Collection of loading parameters
535
     *
536
     * @return string module name
537
     * @throws \samsonphp\core\exception\CannotLoadModule
538
     */
539
    public function load($path, $parameters = array())
540
    {
541
        $name = '';
542
        // Check path
543
        if (file_exists($path)) {
544
545
            /** @var ResourceMap $resourceMap Gather all resources from path */
546
            $resourceMap = ResourceMap::get($path);
547
548
            foreach ($resourceMap->classes as $classPath => $className) {
549
                $this->classes[str_replace(['\\', '/'], '_', $className)] = $className;
550
            }
551
552
            if (isset($resourceMap->module[0])) {
553
                /** @var string $controllerPath Path to module controller file */
554
                $controllerPath = $resourceMap->module[1];
555
556
                /** @var string $moduleClass Name of module controller class to load */
557
                $moduleClass = $resourceMap->module[0];
558 7
559
                // Require module controller class into PHP
560
                if (file_exists($controllerPath)) {
561 7
                    require_once($controllerPath);
562
                }
563
564 7
                // TODO: this should be done via composer autoload file field
565
                // Iterate all function-style controllers and require them
566
                foreach ($resourceMap->controllers as $controller) {
567 7
                    require_once($controller);
568
                }
569
570 7
                $reflection = new \ReflectionClass($moduleClass);
571
                $name = $reflection->getDefaultProperties();
572
                $name = $this->createMetadata($moduleClass, $name['id'] ?? $moduleClass, $path);
573 7
574
                $this->classes[$name] = $moduleClass;
575
576 7
                /*$this->initModule(
577 7
                    new $moduleClass($path, $resourceMap, $this),
578
                    $parameters
579
                );*/
580
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
581
                $name = $this->createMetadata(VirtualModule::class, $parameters['module_id'], $path);
582
            }
583
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
584
        } else {
585
            throw new CannotLoadModule($path);
586
        }
587
588
        return $name;
589
    }
590
591
    //[PHPCOMPRESSOR(remove,start)]
592
593
    protected function createMetadata($class, $name, $path, $scope = 'module')
594
    {
595
        $metadata = new ClassMetadata();
596
        $class = ltrim($class, '\\');
597
        $name = strtolower(ltrim($name, '\\'));
598
        $metadata->className = $class;
599
        $metadata->name = str_replace(['\\', '/'], '_', $name ?? $class);
600
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
601
        $metadata->scopes[] = $scope;
602
        $metadata->propertiesMetadata['system'] = new PropertyMetadata($metadata);
603
        $metadata->propertiesMetadata['system']->name = 'system';
604
        $metadata->propertiesMetadata['system']->dependency = __CLASS__;
605
        $metadata->propertiesMetadata['system']->isPublic = false;
606
        $metadata->propertiesMetadata['resourceMap'] = new PropertyMetadata($metadata);
607
        $metadata->propertiesMetadata['resourceMap']->name = 'resourceMap';
608
        $metadata->propertiesMetadata['resourceMap']->dependency = ResourceMap::class;
609
        $metadata->propertiesMetadata['resourceMap']->isPublic = false;
610
611
        // TODO: Now we need to remove and change constructors
612
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
613
614
        // Iterate constructor arguments to preserve arguments order and inject dependencies
615
        foreach ((new \ReflectionMethod($class, '__construct'))->getParameters() as $parameter) {
616
            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...
617
                $metadata->methodsMetadata['__construct']->dependencies['path'] = $path;
618
            } 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...
619
                $metadata->methodsMetadata['__construct']->dependencies['resources'] = ResourceMap::class;
620
            } 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...
621
                $metadata->methodsMetadata['__construct']->dependencies['system'] = '\\' . SystemInterface::class;
622
            } elseif (!$parameter->isOptional()) {
623
                $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...
624
            }
625
        }
626
627
        $this->metadataCollection[$metadata->name] = $metadata;
628
629
        return $metadata->name;
630
    }
631
632
    /** @see iCore::path() */
633
    public function path($path = null)
634
    {
635
        // Если передан аргумент
636
        if (func_num_args()) {
637
            // Сформируем новый относительный путь к главному шаблону системы
638
            $this->template_path = $path . $this->template_path;
639
640
            // Сохраним относительный путь к Веб-приложению
641
            $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...
642
643
            // Продолжил цепирование
644
            return $this;
645
        }
646
647
        // Вернем текущее значение
648
        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...
649
    }
650
651
    /**
652
     * @param ClassMetadata[] $metadataCollection
653
     * @param array $modulesToLoad
654
     * @param array $classes
655
     * @param string $containerName
656
     * @param ContainerInterface $parentContainer
657
     * @return ContainerInterface
658
     */
659
    protected function loadMetadata(array $metadataCollection, array $modulesToLoad, array $classes, $containerName = 'Container', ContainerInterface $parentContainer = null) : ContainerInterface
660
    {
661
        static $implementsByAlias;
662
        static $serviceAliasesByClass;
663
664
        $containerPath = $this->path().'www/cache/'.$containerName.'.php';
665
        if (!file_exists($containerPath)) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
666
667
668
            // Load annotation and parse classes
669
            new Injectable();
670
            new InjectArgument(['var' => 'type']);
671
            new Service(['value' => '']);
672
673
            $reader = new AnnotationReader();
674
            $resolver = new AnnotationResolver(
675
                new AnnotationClassResolver($reader),
676
                new AnnotationPropertyResolver($reader),
677
                new AnnotationMethodResolver($reader)
678
            );
679
680
            $annotationCollector = new AnnotationMetadataCollector($resolver);
681
            $metadataCollection = $annotationCollector->collect($classes, $metadataCollection);
682
683
            // Regroup classes metadata by class name instead of alias
684
            $classesMetadata = [];
685
            foreach ($metadataCollection as $alias => $classMetadata) {
686
                $classesMetadata[$classMetadata->className] = $classMetadata;
687
            }
688
689
            // Gather all interface implementations
690
            $implementsByAlias = $implementsByAlias ?? [];
691
            foreach (get_declared_classes() as $class) {
692
                $classImplements = class_implements($class);
693
                foreach (get_declared_interfaces() as $interface) {
694
                    if (in_array($interface, $classImplements, true)) {
695
                        if (array_key_exists($class, $classesMetadata)) {
696
                            $implementsByAlias[strtolower('\\' . $interface)][] = $classesMetadata[$class]->name;
697
                        }
698
                    }
699
                }
700
            }
701
702
            // Gather all class implementations
703
            $serviceAliasesByClass = $serviceAliasesByClass ?? [];
704
            foreach (get_declared_classes() as $class) {
705
                if (array_key_exists($class, $classesMetadata)) {
706
                    $serviceAliasesByClass[strtolower('\\' . $class)][] = $classesMetadata[$class]->name;
707
                }
708
            }
709
710
            /**
711
             * TODO: now we need to implement not forcing to load fixed dependencies into modules
712
             * to give ability to change constructors and inject old variable into properties
713
             * and them after refactoring remove them. With this we can only specify needed dependencies
714
             * in new modules, and still have old ones working.
715
             */
716
717
            foreach ($metadataCollection as $alias => $metadata) {
718
                foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
719
                    if (is_string($propertyMetadata->dependency)) {
720
                        $dependency = strtolower($propertyMetadata->dependency);
721
                        if (array_key_exists($dependency, $implementsByAlias)) {
722
                            $propertyMetadata->dependency = $implementsByAlias[$dependency][0];
723
                        } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
724
                            $propertyMetadata->dependency = $serviceAliasesByClass[$dependency][0];
725
                        } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
726
727
                        }
728
                    }
729
                }
730
                foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
731
                    foreach ($methodMetadata->dependencies as $argument => $dependency) {
732
                        if (is_string($dependency)) {
733
                            $dependency = strtolower($dependency);
734
                            if (array_key_exists($dependency, $implementsByAlias)) {
735
                                $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
736
                                //$methodMetadata->parametersMetadata[$argument]->dependency = $implementsByAlias[$dependency][0];
737
                            } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
738
                                $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0];
739
                                //$methodMetadata->parametersMetadata[$argument]->dependency = $serviceAliasesByClass[$dependency][0];
740
                            } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
741
742
                            }
743
                        }
744
                    }
745
                }
746
            }
747
748
            // Generate XML configs
749
            //(new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_');
750
751
            // Load container class
752
            file_put_contents($containerPath,
753
                $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...
754
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
755
        }
756
757
        require_once($containerPath);
758
759
760
        // Inject current core into container
761
        /** @var ContainerInterface $container */
762
        $this->container = new $containerName();
763
        $containerReflection = new \ReflectionClass(get_class($this->container));
764
        $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES);
765
        $serviceProperty->setAccessible(true);
766
        $containerServices = $serviceProperty->getValue($this->container);
767
        $containerServices['core'] = $this;
768
        $serviceProperty->setValue($this->container, $containerServices);
769
        $serviceProperty->setAccessible(false);
770
        if ($parentContainer !== null) {
771
            $this->container->delegate($parentContainer);
772
        }
773
774
        foreach ($modulesToLoad as $identifier => $parameters) {
775
            $instance = $this->container->get($identifier);
776
777
            // Set composer parameters
778
            $instance->composerParameters = $parameters;
779
780
            // TODO: Change event signature to single approach
781
            // Fire core module load event
782
            Event::fire('core.module_loaded', [$identifier, &$instance]);
783
784
            // Signal core module configure event
785
            Event::signal('core.module.configure', [&$instance, $identifier]);
786
787
            if ($instance instanceof PreparableInterface) {
788
                // Call module preparation handler
789
                if (!$instance->prepare()) {
790
                    //throw new \Exception($identifier.' - Module preparation stage failed');
791
                }
792
            }
793
794
            // Try to set module parent module
795
            $instance->parent = $this->getClassParentModule(get_parent_class($instance));
796
        }
797
798
        return $this->container;
799
    }
800
801
    /**
802
     * Find parent module by OOP class inheritance.
803
     *
804
     * @param string $className Class name for searching parent modules
805
     * @param array  $ignoredClasses Collection of ignored classes
806
     *
807
     * @return null|mixed Parent service instance if present
808
     */
809
    protected function getClassParentModule(
810
        $className,
811
        array $ignoredClasses = [ExternalModule::class, CompressableExternalModule::class, Service::class, CompressableService::class]
812
    ) {
813
        // Skip ignored class names
814
        if (!in_array($className, $ignoredClasses, true)) {
815
            // Iterate loaded services
816
            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...
817
                if (get_class($service) === $className) {
818
                    return $service;
819
                }
820
            }
821
        }
822
823
        return null;
824
    }
825
    //[PHPCOMPRESSOR(remove,end)]
826
827
    /** @return \Container Get system container */
828
    public function getContainer()
829
    {
830
        return $this->container;
831
    }
832
833
    /** Магический метод для десериализации объекта */
834
    public function __wakeup()
835
    {
836
        $this->active = &$this->module_stack['local'];
837
    }
838
839
    /** Магический метод для сериализации объекта */
840
    public function __sleep()
841
    {
842
        return array('module_stack', 'render_mode');
843
    }
844
}
845