Completed
Push — master ( 55da2e...a88d11 )
by Vitaly
16:26 queued 06:25
created

Core::buildXMLConfig()   C

Complexity

Conditions 12
Paths 60

Size

Total Lines 62
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 0
Metric Value
cc 12
eloc 43
c 0
b 0
f 0
nc 60
nop 3
dl 0
loc 62
ccs 0
cts 10
cp 0
crap 156
rs 6.2072

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\metadata\ClassMetadata;
14
use samsonframework\container\metadata\MethodMetadata;
15
use samsonframework\container\metadata\PropertyMetadata;
16
use samsonframework\containerannotation\AnnotationClassResolver;
17
use samsonframework\containerannotation\AnnotationMetadataCollector;
18
use samsonframework\containerannotation\AnnotationMethodResolver;
19
use samsonframework\containerannotation\AnnotationPropertyResolver;
20
use samsonframework\containerannotation\AnnotationResolver;
21
use samsonframework\containerannotation\Inject;
22
use samsonframework\containerannotation\Injectable;
23
use samsonframework\containerannotation\InjectArgument;
24
use samsonframework\core\PreparableInterface;
25
use samsonframework\core\SystemInterface;
26
use samsonframework\di\ContainerInterface;
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
 */
40
class Core implements SystemInterface
41
{
42
    /** @var ContainerInterface */
43
    protected $container;
44
45
    /** @var ClassMetadata[] */
46
    protected $metadataCollection = [];
47
48
    /** @var ContainerBuilderInterface */
49
    protected $builder;
50
51
    /** @var string Current system environment */
52
    protected $environment;
53
54
    /* Rendering models */
55
    /** @deprecated Standard algorithm for view rendering */
56
    const RENDER_STANDART = 1;
57
    /** @deprecated View rendering algorithm from array of view variables */
58
    const RENDER_VARIABLE = 3;
59
60
    /** @deprecated @var  ResourceMap Current web-application resource map */
61
    public $map;
62
   
63
    /** @deprecated @var string Path to current web-application */
64
    public $system_path = __SAMSON_CWD__;
65
    /** @deprecated @var string View path loading mode */
66
    public $render_mode = self::RENDER_STANDART;
67
    /** @var Module Pointer to current active module */
68
    protected $active = null;
69
    /** @var bool Flag for outputting layout template, used for asynchronous requests */
70
    protected $async = false;
71
    /** @var string Path to main system template */
72
    protected $template_path = __SAMSON_DEFAULT_TEMPLATE;
73
74
    /** @return \Container Get system container */
75
    public function getContainer()
76
    {
77
        return $this->container;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->container; (samsonframework\di\ContainerInterface) is incompatible with the return type declared by the interface samsonframework\core\SystemInterface::getContainer of type samsonframework\container\ContainerInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
78
    }
79
80
    /**
81
     * Core constructor.
82
     *
83
     * @param ContainerBuilderInterface $builder Container builder
84
     * @param ResourceMap|null $map system resources
85
     */
86
    public function __construct(ContainerBuilderInterface $builder, ResourceMap $map = null)
87
    {
88
        $this->builder = $builder;
89
90
        if (!isset($map)) {
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
        } else { // Use data from passed map
97
            $this->map = $map;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$map has been deprecated with message: @var ResourceMap Current web-application resource map

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
98
            $this->system_path = $map->entryPoint;
0 ignored issues
show
Deprecated Code introduced by
The property samson\core\Core::$system_path has been deprecated with message: @var string Path to current web-application

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
99
        }
100
101
        // Temporary add template worker
102
        $this->subscribe('core.rendered', array($this, 'generateTemplate'));
103
104
        // Fire core creation event
105
        Event::fire('core.created', array(&$this));
106
107
        // Signal core configure event
108
        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...
109
    }
110 1
111
    /**
112
     * Generic wrap for Event system subscription.
113
     * @see \samson\core\\samsonphp\event\Event::subscribe()
114
     *
115
     * @param string   $key     Event identifier
116
     * @param callable $handler Event handler
117
     * @param array    $params  Event parameters
118
     *
119
     * @return $this Chaining
120
     */
121 1
    public function subscribe($key, $handler, $params = array())
122
    {
123
        Event::subscribe($key, $handler, $params);
124
125
        return $this;
126
    }
127
128
    /**
129
     * Change current system working environment or receive
130
     * current system enviroment if no arguments are passed.
131
     *
132
     * @param string $environment Environment identifier
133
     *
134
     * TODO: Function has two different logics - needs to be changed!
135
     * @return $this|string Chaining or current system environment
136
     */
137
    public function environment($environment = Scheme::BASE)
138
    {
139
        if (func_num_args() !== 0) {
140
            $this->environment = $environment;
141
142
            // Signal core environment change
143
            Event::signal('core.environment.change', array($environment, &$this));
144
            return $this;
145
        }
146
147
        return $this->environment;
148
    }
149
150
    /**
151
     * Generate special response header triggering caching mechanisms
152
     * @param int $cacheLife Amount of seconds for cache(default 3600 - 1 hour)
153
     * @param string $accessibility Cache-control accessibility value(default public)
154
     */
155
    public function cached($cacheLife = 3600, $accessibility = 'public')
156
    {
157
        static $cached;
158
        // Protect sending cached headers once
159
        if (!isset($cached) or $cached !== true) {
160
            header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $cacheLife));
161
            header('Cache-Control: ' . $accessibility . ', max-age=' . $cacheLife);
162
            header('Pragma: cache');
163
164
            $cached = true;
165
        }
166
    }
167
168
    /**
169
     * Set asynchronous mode.
170
     * This mode will not output template and will just path everything that
171
     * was outputted to client.
172
     *
173
     * @param bool $async True to switch to asynchronous output mode
174
     *
175
     * @return $this Chaining
176
     */
177
    public function async($async)
178
    {
179
        $this->async = $async;
180
181
        return $this;
182
    }
183
184
    /** @see iCore::path() */
185
    public function path($path = null)
186
    {
187
        // Если передан аргумент
188
        if (func_num_args()) {
189
            // Сформируем новый относительный путь к главному шаблону системы
190
            $this->template_path = $path . $this->template_path;
191
192
            // Сохраним относительный путь к Веб-приложению
193
            $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...
194
195
            // Продолжил цепирование
196
            return $this;
197
        }
198
199
        // Вернем текущее значение
200
        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...
201
    }
202
203
    /**    @see iModule::active() */
204
    public function &active(&$module = null)
205
    {
206
        // Сохраним старый текущий модуль
207
        $old = &$this->active;
208
209
        // Если передано значение модуля для установки как текущий - проверим и установим его
210
        if (isset($module)) {
211
            $this->active = &$module;
212
        }
213
214
        // Вернем значение текущего модуля
215
        return $old;
216
    }
217
218
    /**
219
     * Retrieve module instance by identifier.
220
     *
221
     * @param string|null $module Module identifier
222
     *
223
     * @return null|Module Found or active module
224
     */
225
    public function &module($module = null)
226
    {
227
        $return = null;
228
229
        // Ничего не передано - вернем текущуй модуль системы
230
        if (!isset($module) && isset($this->active)) {
231
            $return = &$this->active;
232
        } elseif (is_object($module)) {
233
            $return = &$module;
234
        } elseif (is_string($module)) {
235
            $return = $this->container->get($module);
236
        }
237
238
        // Ничего не получилось вернем ошибку
239
        if ($return === null) {
240
            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...
241
        }
242
243
        return $return;
244
    }
245
246
    /**
247
     * Unload module from core.
248
     *
249
     * @param string $moduleID Module identifier
250
     */
251
    public function unload($moduleID)
252
    {
253
        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...
254
            unset($this->module_stack[$moduleID]);
255
        }
256
    }
257
258
    /**
259
     * Insert generic html template tags and data
260
     *
261
     * @param string $templateHtml Generated HTML
262
     *
263
     * @deprecated Must be moved to a new HTML output object
264 2
     * @return mixed Changed HTML template
265
     */
266
    public function generateTemplate(&$templateHtml)
267
    {
268
        // Добавим путь к ресурсам для браузера
269
        $headHtml = "\n" . '<base href="' . url()->base() . '">';
270
        // Добавим отметку времени для JavaScript
271 2
        $headHtml .= "\n" . '<script type="text/javascript">var __SAMSONPHP_STARTED = new Date().getTime();</script>';
272
273
        // Добавим поддержку HTML для старых IE
274 2
        $headHtml .= "\n" . '<!--[if lt IE 9]>';
275
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>';
276
        $headHtml .= "\n" . '<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>';
277
        $headHtml .= "\n" . '<![endif]-->';
278 2
279
        // Выполним вставку главного тега <base> от которого зависят все ссылки документа
280 2
        // также подставим МЕТА-теги для текущего модуля и сгенерированный минифицированный CSS
281
        $templateHtml = str_ireplace('<head>', '<head>' . $headHtml, $templateHtml);
282 2
283 2
        // Вставим указатель JavaScript ресурсы в конец HTML документа
284
        $templateHtml = str_ireplace('</html>', '</html>' . __SAMSON_COPYRIGHT, $templateHtml);
285
286 2
        return $templateHtml;
287
    }
288 2
289
    /**
290 2
     * Start SamsonPHP framework.
291
     *
292
     * @param string $default Default module identifier
293
     *
294
     * @throws ViewPathNotFound
295 2
     */
296
    public function start($default)
297
    {
298
        // TODO: Change ExternalModule::init() signature
299
        // Fire core started event
300
        Event::fire('core.started');
301
302
        // TODO: Does not see why it should be here
303
        // Set main template path
304
        $this->template($this->template_path);
305
306
        // Security layer
307
        $securityResult = true;
308
        // Fire core security event
309
        Event::fire('core.security', array(&$this, &$securityResult));
310
311
        /** @var mixed $result External route controller action result */
312
        $result = false;
313
314
        // If we have passed security application layer
315
        if ($securityResult) {
316
            // Fire core routing event - go to routing application layer
317
            Event::signal('core.routing', array(&$this, &$result, $default));
318
        }
319
320 2
        // If no one has passed back routing callback
321
        if (!isset($result) || $result === false) {
322
            // Fire core e404 - routing failed event
323 2
            $result = Event::signal('core.e404', array(url()->module, url()->method));
324
        }
325
326 2
        // Response
327
        $output = '';
328
329 2
        // If this is not asynchronous response and controller has been executed
330
        if (!$this->async && ($result !== false)) {
331
            // Store module data
332 2
            $data = $this->active->toView();
333
334
            // Render main template
335 2
            $output = $this->render($this->template_path, $data);
336
337
            // Fire after render event
338 2
            Event::fire('core.rendered', array(&$output));
339
        }
340
341
        // Output results to client
342
        echo $output;
343
344
        // Fire ended event
345
        Event::fire('core.ended', array(&$output));
346
    }
347
348
    /**	@see iCore::template() */
349
    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...
350
    {
351
        // Если передан аргумент
352 7
        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...
353
            $this->template_path = ($absolutePath)?$template:$this->active->path().$template;
354 7
        }
355
356 7
        // Аргументы не переданы - вернем текущий путь к шаблону системы
357
        return $this->template_path;
358
    }
359
360
    /**
361
     * Render file to a buffer.
362
     *
363
     * @param string $view Path to file
364
     * @param array  $data Collection of variables to path to file
365
     *
366
     * @return string Rendered file contents
367
     * @throws ViewPathNotFound
368
     */
369
    public function render($view, $data = array())
370
    {
371
        // TODO: Make rendering as external system, to split up these 3 rendering options
372
373
        // Объявить ассоциативный массив переменных в данном контексте
374
        if (is_array($data)) {
375
            extract($data);
376
        }
377
378
        // Начать вывод в буффер
379
        ob_start();
380
381
        // Path to another template view, by default we are using default template folder path,
382
        // for meeting first condition
383
        $templateView = $view;
384
385
        if (locale() != SamsonLocale::DEF) {
386
            // Modify standard view path with another template
387
            $templateView = str_replace(__SAMSON_VIEW_PATH, __SAMSON_VIEW_PATH . locale() . '/', $templateView);
388
        }
389
390
        // Depending on core view rendering model
391
        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...
392
            // Standard algorithm for view rendering
393
            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...
394
                // Trying to find another template path, by default it's an default template path
395
                if (file_exists($templateView)) {
396
                    include($templateView);
397
                } elseif (file_exists($view)) {
398
                    // If another template wasn't found - we will use default template path
399
                    include($view);
400
                } else { // Error no template view was found
401
                    throw(new ViewPathNotFound($view));
402
                }
403 2
                break;
404
405
            // View rendering algorithm from array of view variables
406 2
            case self::RENDER_VARIABLE:
0 ignored issues
show
Deprecated Code introduced by
The constant samson\core\Core::RENDER_VARIABLE has been deprecated with message: View rendering algorithm from array of view variables

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
407
                // Collection of views
408
                $views = &$GLOBALS['__compressor_files'];
409 2
                // Trying to find another template path, by default it's an default template path
410 2
                if (isset($views[$templateView])) {
411 2
                    eval(' ?>' . $views[$templateView] . '<?php ');
412
                } elseif (isset($views[$view])) {
413
                    // If another template wasn't found - we will use default template path
414 2
                    eval(' ?>' . $views[$view] . '<?php ');
415
                } else { // Error no template view was found
416
                    throw(new ViewPathNotFound($view));
417
                }
418
                break;
419
        }
420
421
        // Получим данные из буффера вывода
422
        $html = ob_get_contents();
423
424
        // Очистим буффер
425
        ob_end_clean();
426
427
        // Fire core render event
428
        Event::fire('core.render', array(&$html, &$data, &$this->active));
429
430
        ////elapsed('End rendering '.$__view);
431
        return $html;
432
    }
433
434
    //[PHPCOMPRESSOR(remove,start)]
435
436
    /**
437
     * Load system from composer.json
438
     * @param string $dependencyFilePath Path to dependencies file
439
     * @return $this Chaining
440
     */
441
    public function composer($dependencyFilePath = null)
442
    {
443
        $composerModules = array();
444
445
        Event::fire(
446
            'core.composer.create',
447
            array(
448
                &$composerModules,
449
                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...
450
                array(
451
                    'vendorsList' => array('samsonphp/', 'samsonos/', 'samsoncms/', 'samsonjavascript/'),
452
                    'ignoreKey' => 'samson_module_ignore',
453
                    'includeKey' => 'samson_module_include'
454
                )
455
            )
456
        );
457
458
        $modulesToLoad = [];
459
460
        // Iterate requirements
461
        foreach ($composerModules as $requirement => $parameters) {
462
            $moduleName = $this->load(__SAMSON_CWD__ . __SAMSON_VENDOR_PATH . $requirement,
463
            array_merge(
464
                is_array($parameters) ? $parameters : array($parameters),
465
                array('module_id' => $requirement)
466
            ));
467
468
            $modulesToLoad[$moduleName] = $parameters;
469
        }
470
471
        $localModulesPath = '../src';
472
        ResourceMap::get('cache');
473
        // TODO: Nested modules relation
474
        for ($i = 0; $i < 2; $i++) {
475
            $resourceMap = ResourceMap::get($localModulesPath);
476
477
            foreach ($resourceMap->modules as $moduleFile) {
478
                $modulePath = str_replace(realpath($localModulesPath), '', $moduleFile[1]);
479
                $modulePath = explode('/', $modulePath);
480
                $modulePath = $localModulesPath . '/' . $modulePath[1];
481
                $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 461. 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...
482
                $modulesToLoad[$moduleName] = $parameters;
483
            }
484
        }
485
486
        //$this->active = new VirtualModule($this->system_path, $this->map, $this, 'local');
487
488
        // Create local module and set it as active
489
        $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...
490
491
        // TODO: This should be changed to one single logic
492
        // Require all local module model files
493
        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...
494
            // TODO: Why have to require once?
495
            require_once($model);
496
        }
497
498
        // Create all local modules
499
        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...
500
            // Require class into PHP
501
            require($controller);
502
503
            //new VirtualModule($this->system_path, $this->map, $this, basename($controller, '.php'));
504
505
            $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...
506
        }
507
508
        $metadata = new ClassMetadata();
509
        $metadata->className = get_class($this);
510
        $metadata->name = 'core';
511
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
512
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
513
        $metadata->methodsMetadata['__construct']->dependencies['map'] = ResourceMap::class;
514
515
        $this->metadataCollection[$metadata->name] = $metadata;
516
517
        $metadata = new ClassMetadata();
518
        $metadata->className = ResourceMap::class;
519
        $metadata->name = 'resource_map';
520
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
521
522
        $this->metadataCollection[$metadata->name] = $metadata;
523
524
        // Load annotations
525
        $classes = [];
526
        foreach ($this->metadataCollection as $alias => $metadata) {
527
            $classes[$alias] = $metadata->className;
528
        }
529
530
        // Load annotation and parse classes
531
        new Injectable();
532
        new InjectArgument(['var' => 'type']);
533
534
        $reader = new AnnotationReader();
535
        $resolver = new AnnotationResolver(
536
            new AnnotationClassResolver($reader),
537
            new AnnotationPropertyResolver($reader),
538
            new AnnotationMethodResolver($reader)
539
        );
540
        $annotationCollector = new AnnotationMetadataCollector($resolver);
541
        $this->metadataCollection = $annotationCollector->collect($classes, $this->metadataCollection);
542
543
        // Gather all interface implementations
544
        $implementsByAlias = [];
545
        foreach (get_declared_interfaces() as $interface) {
546
            foreach (get_declared_classes() as $class) {
547
                if (in_array($interface, class_implements($class), true)) {
548
                    // TODO: We should already have metadata by classname collection
549
                    foreach ($this->metadataCollection as $alias => $metadata) {
550
                        if ($metadata->className === $class) {
551
                            $implementsByAlias['\\'.$interface][] = $alias;
552
                            break;
553
                        }
554
                    }
555
                }
556
            }
557
        }
558 7
559
        /**
560
         * TODO: now we need to implement not forcing to load fixed dependencies into modules
561 7
         * to give ability to change constructors and inject old variable into properties
562
         * and them after refactoring remove them. With this we can only specify needed dependencies
563
         * in new modules, and still have old ones working.
564 7
         */
565
566
        foreach ($this->metadataCollection as $alias => $metadata) {
567 7
            foreach ($metadata->propertiesMetadata as $property => $propertyMetadata) {
568
                if (array_key_exists($propertyMetadata->dependency, $implementsByAlias)) {
569
                    $propertyMetadata->dependency = $implementsByAlias[$propertyMetadata->dependency][0];
570 7
                }
571
            }
572
            foreach ($metadata->methodsMetadata as $method => $methodMetadata) {
573 7
                foreach ($methodMetadata->dependencies as $argument => $dependency) {
574
                    if (array_key_exists($dependency, $implementsByAlias)) {
575
                        $methodMetadata->dependencies[$argument] = $implementsByAlias[$dependency][0];
576 7
                    }
577 7
                }
578
            }
579
        }
580
581
        $xmlPath = getcwd().'/cache/config_';
582
        foreach ($this->metadataCollection as $alias => $classMetadata) {
583
            $this->buildXMLConfig($classMetadata, $xmlPath, $alias);
584
        }
585
586
587
588
        // Load container class
589
        $containerPath = $this->path().'www/cache/Container.php';
590
        file_put_contents($containerPath, $this->builder->build($this->metadataCollection));
591
        require_once($containerPath);
592
593
        // Inject current core into container
594
        $this->container = new \Container();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Container() of type object<Container> is incompatible with the declared type object<samsonframework\di\ContainerInterface> of property $container.

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

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

Loading history...
595
        $containerReflection = new \ReflectionClass(get_class($this->container));
596
        $serviceProperty = $containerReflection->getProperty(Builder::DI_FUNCTION_SERVICES);
597
        $serviceProperty->setAccessible(true);
598
        $containerServices = $serviceProperty->getValue($this->container);
599
        $containerServices['core'] = $this;
600
        $serviceProperty->setValue($this->container, $containerServices);
601
        $serviceProperty->setAccessible(false);
602
603
        foreach ($modulesToLoad as $identifier => $parameters) {
604
            $instance = $this->container->get($identifier);
605
606
            // Set composer parameters
607
            $instance->composerParameters = $parameters;
608
609
            // TODO: Change event signature to single approach
610
            // Fire core module load event
611
            Event::fire('core.module_loaded', [$identifier, &$instance]);
612
613
            // Signal core module configure event
614
            Event::signal('core.module.configure', [&$instance, $identifier]);
615
616
            if ($instance instanceof PreparableInterface) {
617
                // Call module preparation handler
618
                if (!$instance->prepare()) {
619
                    //throw new \Exception($identifier.' - Module preparation stage failed');
620
                }
621
            }
622
623
            // Try to set module parent module
624
            $instance->parent = $this->getClassParentModule(get_parent_class($instance));
625
        }
626
627
        $this->active = $this->container->getLocal();
628
629
        return $this;
630
    }
631
632
    public function buildXMLConfig(ClassMetadata $classMetadata, string $path, string $alias = null)
633
    {
634
        $dom = new \DOMDocument("1.0", "utf-8");
635
        $dom->preserveWhiteSpace = false;
636
        $dom->formatOutput = true;
637
        $root = $dom->createElement("dependencies");
638
        $dom->appendChild($root);
639
640
        // Build alias from class name if missing
641
        $alias = $alias ?? strtolower(str_replace('\\', '_', $classMetadata->className));
642
643
        $classData = $dom->createElement('instance');
644
        $classData->setAttribute('service', $alias);
645
        $classData->setAttribute('class', $classMetadata->className);
646
647
        foreach ($classMetadata->scopes as $scope) {
648
            $classData->setAttribute('scope', $scope);
649
        }
650
651
        $methodsData = $dom->createElement('methods');
652
        foreach ($classMetadata->methodsMetadata as $method => $methodMetadata) {
653
            if (count($methodMetadata->dependencies)) {
654
                $methodData = $dom->createElement($method);
655
                $argumentsData = $dom->createElement('arguments');
656
                foreach ($methodMetadata->dependencies as $argument => $dependency) {
657
                    $argumentData = $dom->createElement($argument);
658
                    if (array_key_exists($dependency, $this->metadataCollection)) {
659
                        $argumentData->setAttribute('service', $dependency);
660
                    } elseif (class_exists($dependency)) {
661
                        $argumentData->setAttribute('class', $dependency);
662
                    } else {
663
                        $argumentData->setAttribute('value', $dependency);
664
                    }
665
                    $argumentsData->appendChild($argumentData);
666
                }
667
                $methodData->appendChild($argumentsData);
668
                $methodsData->appendChild($methodData);
669
            }
670
        }
671
        $classData->appendChild($methodsData);
672
673
        $propertiesData = $dom->createElement('properties');
674
        foreach ($classMetadata->propertiesMetadata as $property => $propertyMetadata) {
675
            if ($propertyMetadata->dependency !== null && $propertyMetadata->dependency !== '') {
676
                $propertyData = $dom->createElement($property);
677
678
                if (array_key_exists($propertyMetadata->dependency, $this->metadataCollection)) {
679
                    $propertyData->setAttribute('service', $propertyMetadata->dependency);
680
                } elseif (class_exists($propertyMetadata->dependency)) {
681
                    $propertyData->setAttribute('class', $propertyMetadata->dependency);
682
                } else {
683
                    $propertyData->setAttribute('value', $propertyMetadata->dependency);
684
                }
685
686
                $propertiesData->appendChild($propertyData);
687
            }
688
        }
689
690
        $classData->appendChild($propertiesData);
691
        $root->appendChild($classData);
692
        $dom->save($path.$alias.'.xml');
693
    }
694
695
    /**
696
     * Find parent module by OOP class inheritance.
697
     *
698
     * @param string $className Class name for searching parent modules
699
     * @param array  $ignoredClasses Collection of ignored classes
700
     *
701
     * @return null|mixed Parent service instance if present
702
     */
703
    protected function getClassParentModule(
704
        $className,
705
        array $ignoredClasses = [ExternalModule::class, CompressableExternalModule::class, Service::class, CompressableService::class]
706
    ) {
707
        // Skip ignored class names
708
        if (!in_array($className, $ignoredClasses, true)) {
709
            // Iterate loaded services
710
            foreach ($this->getContainer()->getServices('module') as $service) {
711
                if (get_class($service) === $className) {
712
                    return $service;
713
                }
714
            }
715
        }
716
717
        return null;
718
    }
719
720
    /**
721
     * Load module from path to core.
722
     *
723
     * @param string $path       Path for module loading
724
     * @param array  $parameters Collection of loading parameters
725
     *
726
     * @return string module name
727
     * @throws \samsonphp\core\exception\CannotLoadModule
728
     */
729
    public function load($path, $parameters = array())
730
    {
731
        $name = '';
732
        // Check path
733
        if (file_exists($path)) {
734
            /** @var ResourceMap $resourceMap Gather all resources from path */
735
            $resourceMap = ResourceMap::get($path);
736
            if (isset($resourceMap->module[0])) {
737
738
                /** @var string $controllerPath Path to module controller file */
739
                $controllerPath = $resourceMap->module[1];
740
741
                /** @var string $moduleClass Name of module controller class to load */
742
                $moduleClass = $resourceMap->module[0];
743
744
                // Require module controller class into PHP
745
                if (file_exists($controllerPath)) {
746
                    require_once($controllerPath);
747
                }
748
749
                // TODO: this should be done via composer autoload file field
750
                // Iterate all function-style controllers and require them
751
                foreach ($resourceMap->controllers as $controller) {
752
                    require_once($controller);
753
                }
754
755
                $reflection = new \ReflectionClass($moduleClass);
756
                $name = $reflection->getDefaultProperties();
757
                $name = $this->createMetadata($moduleClass, $name['id'] ?? $moduleClass, $path);
758
759
                /*$this->initModule(
760
                    new $moduleClass($path, $resourceMap, $this),
761
                    $parameters
762
                );*/
763
            } elseif (is_array($parameters) && isset($parameters['samsonphp_package_compressable']) && ($parameters['samsonphp_package_compressable'] == 1)) {
764
                $name = $this->createMetadata(VirtualModule::class, $parameters['module_id'], $path);
765
766
                /*$this->initModule(
767
                    new VirtualModule($path, $resourceMap, $this, str_replace('/', '', $parameters['module_id'])),
768
                    $parameters
769
                );*/
770
            }
771
//            elseif (count($resourceMap->classes)) {
772
//                /** Update for future version: Search classes that implement LoadableInterface */
773
//                foreach ($resourceMap->classes as $classPath => $class) {
774
//                    // This class implements LoadableInterface LoadableInterface::class
775
//                    if (in_array('\samsonframework\core\LoadableInterface', $resourceMap->classData[$classPath]['implements'])) {
776
//
777
//                        $name =  str_replace('/', '', $parameters['module_id']);
778
//
779
//                        $this->createMetadata(VirtualModule::class, str_replace('/', '', $parameters['module_id']), $path);
780
//
781
//                        /*$this->initModule(
782
//                            new VirtualModule(
783
//                                $path,
784
//                                $resourceMap,
785
//                                $this,
786
//                                str_replace('/', '', $resourceMap->classData[$classPath]['className'])
787
//                            ),
788
//                            $parameters
789
//                        );*/
790
//                    }
791
//                }
792
//            }
793
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
794
        } else {
795
            throw new CannotLoadModule($path);
796
        }
797
798
        return $name;
799
    }
800
    //[PHPCOMPRESSOR(remove,end)]
801
802
    /** Магический метод для десериализации объекта */
803
    public function __wakeup()
804
    {
805
        $this->active = &$this->module_stack['local'];
806
    }
807
808
    /** Магический метод для сериализации объекта */
809
    public function __sleep()
810
    {
811
        return array('module_stack', 'render_mode');
812
    }
813
814
    protected function createMetadata($class, $name, $path, $scope = 'module')
815
    {
816
        $metadata = new ClassMetadata();
817
        $class = ltrim($class, '\\');
818
        $name = strtolower(ltrim($name, '\\'));
819
        $metadata->className = $class;
820
        $metadata->name = str_replace(['\\', '/'], '_', $name ?? $class);
821
        $metadata->scopes[] = Builder::SCOPE_SERVICES;
822
        $metadata->scopes[] = $scope;
823
        $metadata->propertiesMetadata['system'] = new PropertyMetadata($metadata);
824
        $metadata->propertiesMetadata['system']->dependency = 'core';
825
        $metadata->propertiesMetadata['system']->isPublic = false;
826
        $metadata->propertiesMetadata['resourceMap'] = new PropertyMetadata($metadata);
827
        $metadata->propertiesMetadata['resourceMap']->dependency = 'resource_map';
828
        $metadata->propertiesMetadata['resourceMap']->isPublic = false;
829
830
        // TODO: Now we need to remove and change constructors
831
        $metadata->methodsMetadata['__construct'] = new MethodMetadata($metadata);
832
        $metadata->methodsMetadata['__construct']->dependencies['path'] = $path;
833
        $metadata->methodsMetadata['__construct']->dependencies['resources'] = 'resource_map';
834
        $metadata->methodsMetadata['__construct']->dependencies['system'] = 'core';
835
836
        $this->metadataCollection[$metadata->name] = $metadata;
837
838
        return $metadata->name;
839
    }
840
}
841