Completed
Push — master ( 8ce0e8...15dc1a )
by Vitaly
14:25
created

Core::composer()   D

Complexity

Conditions 12
Paths 144

Size

Total Lines 111
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 14
Bugs 3 Features 3
Metric Value
cc 12
eloc 66
c 14
b 3
f 3
nc 144
nop 1
dl 0
loc 111
ccs 0
cts 25
cp 0
crap 156
rs 4.6933

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\ClassName;
27
use samsonframework\containercollection\attribute\Name;
28
use samsonframework\containercollection\attribute\Value;
29
use samsonframework\containercollection\CollectionClassResolver;
30
use samsonframework\containercollection\CollectionMethodResolver;
31
use samsonframework\containercollection\CollectionParameterResolver;
32
use samsonframework\containercollection\CollectionPropertyResolver;
33
use samsonframework\containerxml\XmlMetadataCollector;
34
use samsonframework\containerxml\XmlResolver;
35
use samsonframework\core\PreparableInterface;
36
use samsonframework\core\SystemInterface;
37
use samsonframework\resource\ResourceMap;
38
use samsonphp\config\Scheme;
39
use samsonphp\core\exception\CannotLoadModule;
40
use samsonphp\core\exception\ViewPathNotFound;
41
use samsonphp\core\Module;
42
use samsonphp\event\Event;
43
44
/**
45
 * SamsonPHP Core.
46
 *
47
 * @author Vitaly Iegorov <[email protected]>
48
 * @Service("core")
49
 */
50
class Core implements SystemInterface
51
{
52
    /** @deprecated Standard algorithm for view rendering */
53
    const RENDER_STANDART = 1;
54
    /** @deprecated View rendering algorithm from array of view variables */
55
    const RENDER_VARIABLE = 3;
56
    /** @deprecated @var  ResourceMap Current web-application resource map */
57
    public $map;
58
    /** @deprecated @var string Path to current web-application */
59
    public $system_path = __SAMSON_CWD__;
60
61
    /* Rendering models */
62
    /** @deprecated @var string View path loading mode */
63
    public $render_mode = self::RENDER_STANDART;
64
    /** @var ContainerInterface */
65
    protected $container;
66
    /** @var ClassMetadata[] */
67
    protected $metadataCollection = [];
68
    /** @var ContainerBuilderInterface */
69
    protected $builder;
70
    /** @var string Current system environment */
71
    protected $environment;
72
    protected $classes = [];
73
    /** @var Module Pointer to current active module */
74
    protected $active = null;
75
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
76
    protected $async = false;
77
    /** @var string Path to main system template */
78
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
79
80
    /**
81
     * Core constructor.
82
     *
83
     * @param ContainerBuilderInterface $builder Container builder
84
     * @param ResourceMap|null $map system resources
85
     * @InjectArgument(builder="\samsonframework\container\ContainerBuilderInterface")
86
     * @InjectArgument(builder="\samsonframework\core\ResourceInterface")
87
     */
88
    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...
89
    {
90
        $this->builder = $builder;
91
        // Get correct web-application path
92
        $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...
93
94
        // Get web-application resource map
95
        $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...
96
97
        // Temporary add template worker
98
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
99
100
        // Fire core creation event
101
        Event::fire('core.created', array(&$this));
102
103
        // Signal core configure event
104
        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...
105
    }
106
107
    /**
108
     * Generic wrap for Event system subscription.
109
     * @see \samson\core\\samsonphp\event\Event::subscribe()
110 1
     *
111
     * @param string   $key     Event identifier
112
     * @param callable $handler Event handler
113
     * @param array    $params  Event parameters
114
     *
115
     * @return $this Chaining
116
     */
117
    public function subscribe($key, $handler, $params = array())
118
    {
119
        Event::subscribe($key, $handler, $params);
120
121 1
        return $this;
122
    }
123
124
    /**
125
     * Change current system working environment or receive
126
     * current system enviroment if no arguments are passed.
127
     *
128
     * @param string $environment Environment identifier
129
     *
130
     * TODO: Function has two different logics - needs to be changed!
131
     * @return $this|string Chaining or current system environment
132
     */
133
    public function environment($environment = Scheme::BASE)
134
    {
135
        if (func_num_args() !== 0) {
136
            $this->environment = $environment;
137
138
            // Signal core environment change
139
            Event::signal('core.environment.change', array($environment, &$this));
140
            return $this;
141
        }
142
143
        return $this->environment;
144
    }
145
146
    /**
147
     * Generate special response header triggering caching mechanisms
148
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
149
     * @param string $accessibility Cache-control accessibility value(default public)
150
     */
151
    public function cached($cacheLife = 3600, $accessibility = 'public')
152
    {
153
        static $cached;
154
        // Protect sending cached headers once
155
        if (!isset($cached) or $cached !== true) {
156
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
157
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
158
            header('Pragma: cache');
159
160
            $cached = true;
161
        }
162
    }
163
164
    /**
165
     * Set asynchronous mode.
166
     * This mode will not output template and will just path everything that
167
     * was outputted to client.
168
     *
169
     * @param bool $async True to switch to asynchronous output mode
170
     *
171
     * @return $this Chaining
172
     */
173
    public function async($async)
174
    {
175
        $this->async = $async;
176
177
        return $this;
178
    }
179
180
    /**    @see iModule::active() */
181
    public function &active(&$module = null)
182
    {
183
        // Сохраним старый текущий модуль
184
        $old = &$this->active;
185
186
        // Если передано значение модуля для установки как текущий - проверим и установим его
187
        if (isset($module)) {
188
            $this->active = &$module;
189
        }
190
191
        // Вернем значение текущего модуля
192
        return $old;
193
    }
194
195
    /**
196
     * Retrieve module instance by identifier.
197
     *
198
     * @param string|null $module Module identifier
199
     *
200
     * @return null|Module Found or active module
201
     */
202
    public function &module($module = null)
203
    {
204
        $return = null;
205
206
        // Ничего не передано - вернем текущуй модуль системы
207
        if (!isset($module) && isset($this->active)) {
208
            $return = &$this->active;
209
        } elseif (is_object($module)) {
210
            $return = &$module;
211
        } elseif (is_string($module)) {
212
            $return = $this->container->get($module);
213
        }
214
215
        // Ничего не получилось вернем ошибку
216
        if ($return === null) {
217
            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...
218
        }
219
220
        return $return;
221
    }
222
223
    /**
224
     * Unload module from core.
225
     *
226
     * @param string $moduleID Module identifier
227
     */
228
    public function unload($moduleID)
229
    {
230
        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...
231
            unset($this->module_stack[$moduleID]);
232
        }
233
    }
234
235
    /**
236
     * Insert generic html template tags and data
237
     *
238
     * @param string $templateHtml Generated HTML
239
     *
240
     * @deprecated Must be moved to a new HTML output object
241
     * @return mixed Changed HTML template
242
     */
243
    public function generateTemplate(&$templateHtml)
244
    {
245
        // Добавим путь к ресурсам для браузера
246
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
247
        // Добавим отметку времени для JavaScript
248
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
249
250
        // Добавим поддержку HTML для старых IE
251
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
252
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
253
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
254
        $headHtml .= "\n" . '<![endif]-->';
255
256
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
257
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
258
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
259
260
        // Вставим указатель JavaScript ресурсы в конец HTML документа
261
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
262
263
        return $templateHtml;
264 2
    }
265
266
    /**
267
     * Start SamsonPHP framework.
268
     *
269
     * @param string $default Default module identifier
270
     *
271 2
     * @throws ViewPathNotFound
272
     */
273
    public function start($default)
274 2
    {
275
        // TODO: Change ExternalModule::init() signature
276
        // Fire core started event
277
        Event::fire('core.started');
278 2
279
        // TODO: Does not see why it should be here
280 2
        // Set main template path
281
        $this->template($this->template_path);
282 2
283 2
        // Security layer
284
        $securityResult = true;
285
        // Fire core security event
286 2
        Event::fire('core.security', array(&$this, &$securityResult));
287
288 2
        /** @var mixed $result External route controller action result */
289
        $result = false;
290 2
291
        // If we have passed security application layer
292
        if ($securityResult) {
293
            // Fire core routing event - go to routing application layer
294
            Event::signal('core.routing', array(&$this, &$result, $default));
295 2
        }
296
297
        // If no one has passed back routing callback
298
        if (!isset($result) || $result === false) {
299
            // Fire core e404 - routing failed event
300
            $result = Event::signal('core.e404', array(url()->module, url()->method));
301
        }
302
303
        // Response
304
        $output = '';
305
306
        // If this is not asynchronous response and controller has been executed
307
        if (!$this->async && ($result !== false)) {
308
            // Store module data
309
            $data = $this->active->toView();
310
311
            // Render main template
312
            $output = $this->render($this->template_path, $data);
313
314
            // Fire after render event
315
            Event::fire('core.rendered', array(&$output));
316
        }
317
318
        // Output results to client
319
        echo $output;
320 2
321
        // Fire ended event
322
        Event::fire('core.ended', array(&$output));
323 2
    }
324
325
    /**	@see iCore::template() */
326 2
    public function template( $template = NULL, $absolutePath = false )
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between opening bracket and argument "$template"; 1 found
Loading history...
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected null, but found NULL.
Loading history...
Coding Style introduced by
Expected 0 spaces between argument "$absolutePath" and closing bracket; 1 found
Loading history...
327
    {
328
        // Если передан аргумент
329 2
        if( func_num_args() ){
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
Coding Style introduced by
Expected 0 spaces before closing bracket; 1 found
Loading history...
330
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
331
        }
332 2
333
        // Аргументы не переданы - вернем текущий путь к шаблону системы
334
        return $this->template_path;
335 2
    }
336
337
    /**
338 2
     * Render file to a buffer.
339
     *
340
     * @param string $view Path to file
341
     * @param array  $data Collection of variables to path to file
342
     *
343
     * @return string Rendered file contents
344
     * @throws ViewPathNotFound
345
     */
346
    public function render($view, $data = array())
347
    {
348
        // TODO: Make rendering as external system, to split up these 3 rendering options
349
350
        // Объявить ассоциативный массив переменных в данном контексте
351
        if (is_array($data)) {
352 7
            extract($data);
353
        }
354 7
355
        // Начать вывод в буффер
356 7
        ob_start();
357
358
        // Path to another template view, by default we are using default template folder path,
359
        // for meeting first condition
360
        $templateView = $view;
361
362
        if (locale() != SamsonLocale::DEF) {
363
            // Modify standard view path with another template
364
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
365
        }
366
367
        // Depending on core view rendering model
368
        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...
369
            // Standard algorithm for view rendering
370
            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...
371
                // Trying to find another template path, by default it's an default template path
372
                if (file_exists($templateView)) {
373
                    include($templateView);
374
                } elseif (file_exists($view)) {
375
                    // If another template wasn't found - we will use default template path
376
                    include($view);
377
                } else { // Error no template view was found
378
                    throw(new ViewPathNotFound($view));
379
                }
380
                break;
381
382
            // View rendering algorithm from array of view variables
383
            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...
384
                // Collection of views
385
                $views = &$GLOBALS['__compressor_files'];
386
                // Trying to find another template path, by default it's an default template path
387
                if (isset($views[$templateView])) {
388
                    eval(' ?>' . $views[$templateView] . '<?php ');
389
                } elseif (isset($views[$view])) {
390
                    // If another template wasn't found - we will use default template path
391
                    eval(' ?>' . $views[$view] . '<?php ');
392
                } else { // Error no template view was found
393
                    throw(new ViewPathNotFound($view));
394
                }
395
                break;
396
        }
397
398
        // Получим данные из буффера вывода
399
        $html = ob_get_contents();
400
401
        // Очистим буффер
402
        ob_end_clean();
403 2
404
        // Fire core render event
405
        Event::fire('core.render', array(&$html, &$data, &$this->active));
406 2
407
        ////elapsed('End rendering '.$__view);
408
        return $html;
409 2
    }
410 2
411 2
    /**
412
     * Load system from composer.json
413
     * @param string $dependencyFilePath Path to dependencies file
414 2
     * @return $this Chaining
415
     */
416
    public function composer($dependencyFilePath = null)
417
    {
418
        $composerModules = array();
419
420
        Event::fire(
421
            'core.composer.create',
422
            array(
423
                &$composerModules,
424
                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...
425
                array(
426
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'),
427
                    'ignoreKey' => 'samson_module_ignore',
428
                    'includeKey' => 'samson_module_include'
429
                )
430
            )
431
        );
432
433
        $modulesToLoad = [];
434
        $preClasses = [
435
            str_replace(['\\', '/'], '_', __CLASS__) => __CLASS__,
436
            str_replace(['\\', '/'], '_', ResourceMap::class) => ResourceMap::class
437
        ];
438
//        $this->classes = array_merge($this->classes, $preClasses);
439
        $preModules = [];
440
441
        // Iterate requirements
442
        foreach ($composerModules as $requirement => $parameters) {
443
            $modulePath = __SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement;
444
            $moduleName = $this->load($modulePath,
445
            array_merge(
446
                is_array($parameters) ? $parameters : array($parameters),
447
                array('module_id' => $requirement)
448
            ));
449
            if (array_key_exists('samsonframework_precontainer', $parameters)) {
450
                $preModules[$moduleName] = $parameters;
451
                foreach (ResourceMap::get($modulePath)->classes as $className) {
452
                    $preClasses[str_replace(['\\', '/'], '_', $className)] = $className;
453
                }
454
            }
455
            $modulesToLoad[$moduleName] = $parameters;
456
        }
457
458
        // Create local module and set it as active
459
        $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...
460
461
        // TODO: This should be changed to one single logic
462
        // Require all local module model files
463
        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...
464
            // TODO: Why have to require once?
465
            require_once($model);
466
        }
467
468
        // Create all local modules
469
        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...
470
            // Require class into PHP
471
            require($controller);
472
473
            //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
474
475
            $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...
476
        }
477
478
        $localModulesPath = '../src';
479
        ResourceMap::get('cache');
480
        // TODO: Nested modules relation
481
        for ($i = 0; $i < 2; $i++) {
482
            $resourceMap = ResourceMap::get($localModulesPath);
483
484
            foreach ($resourceMap->modules as $moduleFile) {
485
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
486
                $modulePath = explode('/', $modulePath);
487
                $modulePath = $localModulesPath . '/' . $modulePath[1];
488
                $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 442. 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...
489
                $modulesToLoad[$moduleName] = $parameters;
490
            }
491
        }
492
493
        $preMetadataCollection = [];
494
        foreach ($preModules as $moduleName => $parameters) {
495
            $preMetadataCollection[$moduleName] = $this->metadataCollection[$moduleName];
496
        }
497
498
        $configPath = $this->path() . 'app/config/config.xml';
499
        if (file_exists($configPath)) {
500
            $xmlConfigurator = new XmlResolver(new CollectionClassResolver([
501
                \samsonframework\containercollection\attribute\Scope::class,
502
                Name::class,
503
                ClassName::class,
504
                \samsonframework\containercollection\attribute\Service::class
505
            ]), new CollectionPropertyResolver([
506
                ClassName::class,
507
                Value::class
508
            ]), new CollectionMethodResolver([], new CollectionParameterResolver([
509
                ClassName::class,
510
                Value::class,
511
                \samsonframework\containercollection\attribute\Service::class
512
            ])));
513
514
            $xmlCollector = new XmlMetadataCollector($xmlConfigurator);
515
516
            $preMetadataCollection = $xmlCollector->collect(file_get_contents($configPath), $preMetadataCollection);
517
        }
518
519
        $preContainer = $this->loadMetadata($preMetadataCollection, $preModules, $preClasses, 'ContainerPreLoad');
520
        /** @var ContainerInterface container */
521
        $this->container = $this->loadMetadata($this->metadataCollection, $modulesToLoad, $this->classes, 'Container', $preContainer);
522
523
        $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...
524
525
        return $this;
526
    }
527
528
    /**
529
     * Load module from path to core.
530
     *
531
     * @param string $path       Path for module loading
532
     * @param array  $parameters Collection of loading parameters
533
     *
534
     * @return string module name
535
     * @throws \samsonphp\core\exception\CannotLoadModule
536
     */
537
    public function load($path, $parameters = array())
538
    {
539
        $name = '';
540
        // Check path
541
        if (file_exists($path)) {
542
543
            /** @var ResourceMap $resourceMap Gather all resources from path */
544
            $resourceMap = ResourceMap::get($path);
545
546
            foreach ($resourceMap->classes as $classPath => $className) {
547
                $this->classes[str_replace(['\\', '/'], '_', $className)] = $className;
548
            }
549
550
            if (isset($resourceMap->module[0])) {
551
                /** @var string $controllerPath Path to module controller file */
552
                $controllerPath = $resourceMap->module[1];
553
554
                /** @var string $moduleClass Name of module controller class to load */
555
                $moduleClass = $resourceMap->module[0];
556
557
                // Require module controller class into PHP
558 7
                if (file_exists($controllerPath)) {
559
                    require_once($controllerPath);
560
                }
561 7
562
                // TODO: this should be done via composer autoload file field
563
                // Iterate all function-style controllers and require them
564 7
                foreach ($resourceMap->controllers as $controller) {
565
                    require_once($controller);
566
                }
567 7
568
                $reflection = new \ReflectionClass($moduleClass);
569
                $name = $reflection->getDefaultProperties();
570 7
                $name = $this->createMetadata($moduleClass, $name['id'] ?? $moduleClass, $path);
571
572
                $this->classes[$name] = $moduleClass;
573 7
574
                /*$this->initModule(
575
                    new $moduleClass($path, $resourceMap, $this),
576 7
                    $parameters
577 7
                );*/
578
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
579
                $name = $this->createMetadata(VirtualModule::class, $parameters['module_id'], $path);
580
            }
581
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
582
        } else {
583
            throw new CannotLoadModule($path);
584
        }
585
586
        return $name;
587
    }
588
589
    //[PHPCOMPRESSOR(remove,start)]
590
591
    protected function createMetadata($class, $name, $path, $scope = 'module')
592
    {
593
        $metadata = new ClassMetadata();
594
        $class = ltrim($class, '\\');
595
        $name = strtolower(ltrim($name, '\\'));
596
        $metadata->className = $class;
597
        $metadata->name = str_replace(['\\', '/'], '_', $name ?? $class);
598
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
599
        $metadata->scopes[] = $scope;
600
        $metadata->propertiesMetadata['system'] = new PropertyMetadata($metadata);
601
        $metadata->propertiesMetadata['system']->name = 'system';
602
        $metadata->propertiesMetadata['system']->dependency = __CLASS__;
603
        $metadata->propertiesMetadata['system']->isPublic = false;
604
        $metadata->propertiesMetadata['resourceMap'] = new PropertyMetadata($metadata);
605
        $metadata->propertiesMetadata['resourceMap']->name = 'resourceMap';
606
        $metadata->propertiesMetadata['resourceMap']->dependency = ResourceMap::class;
607
        $metadata->propertiesMetadata['resourceMap']->isPublic = false;
608
609
        // TODO: Now we need to remove and change constructors
610
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
611
612
        // Iterate constructor arguments to preserve arguments order and inject dependencies
613
        foreach ((new \ReflectionMethod($class, '__construct'))->getParameters() as $parameter) {
614
            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...
615
                $metadata->methodsMetadata['__construct']->dependencies['path'] = $path;
616
            } 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...
617
                $metadata->methodsMetadata['__construct']->dependencies['resources'] = ResourceMap::class;
618
            } 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...
619
                $metadata->methodsMetadata['__construct']->dependencies['system'] = '\\' . SystemInterface::class;
620
            } elseif (!$parameter->isOptional()) {
621
                $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...
622
            }
623
        }
624
625
        $this->metadataCollection[$metadata->name] = $metadata;
626
627
        return $metadata->name;
628
    }
629
630
    /** @see iCore::path() */
631
    public function path($path = null)
632
    {
633
        // Если передан аргумент
634
        if (func_num_args()) {
635
            // Сформируем новый относительный путь к главному шаблону системы
636
            $this->template_path = $path . $this->template_path;
637
638
            // Сохраним относительный путь к Веб-приложению
639
            $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...
640
641
            // Продолжил цепирование
642
            return $this;
643
        }
644
645
        // Вернем текущее значение
646
        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...
647
    }
648
649
    /**
650
     * @param ClassMetadata[] $metadataCollection
651
     * @param array $modulesToLoad
652
     * @param array $classes
653
     * @param string $containerName
654
     * @param ContainerInterface $parentContainer
655
     * @return ContainerInterface
656
     */
657
    protected function loadMetadata(array $metadataCollection, array $modulesToLoad, array $classes, $containerName = 'Container', ContainerInterface $parentContainer = null) : ContainerInterface
658
    {
659
        static $implementsByAlias;
660
        static $serviceAliasesByClass;
661
662
        $containerPath = $this->path().'www/cache/'.$containerName.'.php';
663
//        if (!file_exists($containerPath)) {
664
665
666
            // Load annotation and parse classes
667
            new Injectable();
668
            new InjectArgument(['var' => 'type']);
669
            new Service(['value' => '']);
670
671
            $reader = new AnnotationReader();
672
            $resolver = new AnnotationResolver(
673
                new AnnotationClassResolver($reader),
674
                new AnnotationPropertyResolver($reader),
675
                new AnnotationMethodResolver($reader)
676
            );
677
678
            $annotationCollector = new AnnotationMetadataCollector($resolver);
679
            $metadataCollection = $annotationCollector->collect($classes, $metadataCollection);
680
681
            // Regroup classes metadata by class name instead of alias
682
            $classesMetadata = [];
683
            foreach ($metadataCollection as $alias => $classMetadata) {
684
                $classesMetadata[$classMetadata->className] = $classMetadata;
685
            }
686
687
            // Gather all interface implementations
688
            $implementsByAlias = $implementsByAlias ?? [];
689
            foreach (get_declared_classes() as $class) {
690
                $classImplements = class_implements($class);
691
                foreach (get_declared_interfaces() as $interface) {
692
                    if (in_array($interface, $classImplements, true)) {
693
                        if (array_key_exists($class, $classesMetadata)) {
694
                            $implementsByAlias[strtolower('\\' . $interface)][] = $classesMetadata[$class]->name;
695
                        }
696
                    }
697
                }
698
            }
699
700
            // Gather all class implementations
701
            $serviceAliasesByClass = $serviceAliasesByClass ?? [];
702
            foreach (get_declared_classes() as $class) {
703
                if (array_key_exists($class, $classesMetadata)) {
704
                    $serviceAliasesByClass[strtolower('\\' . $class)][] = $classesMetadata[$class]->name;
705
                }
706
            }
707
708
            /**
709
             * TODO: now we need to implement not forcing to load fixed dependencies into modules
710
             * to give ability to change constructors and inject old variable into properties
711
             * and them after refactoring remove them. With this we can only specify needed dependencies
712
             * in new modules, and still have old ones working.
713
             */
714
715
            foreach ($metadataCollection as $alias => $metadata) {
716 View Code Duplication
                foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
717
                    $dependency = strtolower($propertyMetadata->dependency);
718
                    if (array_key_exists($dependency, $implementsByAlias)) {
719
                        $propertyMetadata->dependency = $implementsByAlias[$dependency][0];
720
                    } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
721
                        $propertyMetadata->dependency = $serviceAliasesByClass[$dependency][0];
722
                    } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
723
724
                    }
725
                }
726
                foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
727 View Code Duplication
                    foreach ($methodMetadata->dependencies as $argument => $dependency) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
728
                        $dependency = strtolower($dependency);
729
                        if (array_key_exists($dependency, $implementsByAlias)) {
730
                            $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
731
                            //$methodMetadata->parametersMetadata[$argument]->dependency = $implementsByAlias[$dependency][0];
732
                        } elseif (array_key_exists($dependency, $serviceAliasesByClass)) {
733
                            $methodMetadata->dependencies[$argument] = $serviceAliasesByClass[$dependency][0];
734
                            //$methodMetadata->parametersMetadata[$argument]->dependency = $serviceAliasesByClass[$dependency][0];
735
                        } else {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
736
737
                        }
738
                    }
739
                }
740
            }
741
742
            // Generate XML configs
743
            //(new XMLBuilder())->buildXMLConfig($metadataCollection, getcwd().'/cache/config_');
744
745
            // Load container class
746
            file_put_contents($containerPath,
747
                $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...
748
749
//        }
750
751
        require_once($containerPath);
752
753
754
        // Inject current core into container
755
        /** @var ContainerInterface $container */
756
        $this->container = new $containerName();
757
        $containerReflection = new \ReflectionClass(get_class($this->container));
758
        $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES);
759
        $serviceProperty->setAccessible(true);
760
        $containerServices = $serviceProperty->getValue($this->container);
761
        $containerServices['core'] = $this;
762
        $serviceProperty->setValue($this->container, $containerServices);
763
        $serviceProperty->setAccessible(false);
764
        if ($parentContainer !== null) {
765
            $this->container->delegate($parentContainer);
766
        }
767
768
        foreach ($modulesToLoad as $identifier => $parameters) {
769
            $instance = $this->container->get($identifier);
770
771
            // Set composer parameters
772
            $instance->composerParameters = $parameters;
773
774
            // TODO: Change event signature to single approach
775
            // Fire core module load event
776
            Event::fire('core.module_loaded', [$identifier, &$instance]);
777
778
            // Signal core module configure event
779
            Event::signal('core.module.configure', [&$instance, $identifier]);
780
781
            if ($instance instanceof PreparableInterface) {
782
                // Call module preparation handler
783
                if (!$instance->prepare()) {
784
                    //throw new \Exception($identifier.' - Module preparation stage failed');
785
                }
786
            }
787
788
            // Try to set module parent module
789
            $instance->parent = $this->getClassParentModule(get_parent_class($instance));
790
        }
791
792
        return $this->container;
793
    }
794
795
    /**
796
     * Find parent module by OOP class inheritance.
797
     *
798
     * @param string $className Class name for searching parent modules
799
     * @param array  $ignoredClasses Collection of ignored classes
800
     *
801
     * @return null|mixed Parent service instance if present
802
     */
803
    protected function getClassParentModule(
804
        $className,
805
        array $ignoredClasses = [ExternalModule::class, CompressableExternalModule::class, Service::class, CompressableService::class]
806
    ) {
807
        // Skip ignored class names
808
        if (!in_array($className, $ignoredClasses, true)) {
809
            // Iterate loaded services
810
            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...
811
                if (get_class($service) === $className) {
812
                    return $service;
813
                }
814
            }
815
        }
816
817
        return null;
818
    }
819
    //[PHPCOMPRESSOR(remove,end)]
820
821
    /** @return \Container Get system container */
822
    public function getContainer()
823
    {
824
        return $this->container;
825
    }
826
827
    /** Магический метод для десериализации объекта */
828
    public function __wakeup()
829
    {
830
        $this->active = &$this->module_stack['local'];
831
    }
832
833
    /** Магический метод для сериализации объекта */
834
    public function __sleep()
835
    {
836
        return array('module_stack', 'render_mode');
837
    }
838
}
839