Completed
Push — master ( 884f37...c91013 )
by
unknown
10:12
created

Core::load()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 51
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

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

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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