Completed
Push — master ( 0c7cb2...3cbd5e )
by Vitaly
02:17
created

Router::init()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 13
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
namespace samsonphp\resource;
3
4
use samson\core\ExternalModule;
5
use samson\core\File;
6
use samson\core\iModule;
7
use samsonphp\event\Event;
8
9
/**
10
 * Класс для определения, построения и поиска путей к ресурсам
11
 * системы. Класс предназначен для формирования УНИКАЛЬНЫХ URL
12
 * описывающих путь к ресурсу веб-приложения/модуля независимо
13
 * от его расположения на HDD.
14
 *
15
 * Создавая возможность один рас описать путь вида:
16
 *    ИМЯ_РЕСУРСА - ИМЯ_ВЕБПРИЛОЖЕНИЯ - ИМЯ_МОДУЛЯ
17
 *
18
 * И больше не задумываться об реальном(физическом) местоположении
19
 * ресурса
20
 *
21
 * @package SamsonPHP
22
 * @author Vitaly Iegorov <[email protected]>
23
 * @author Nikita Kotenko <[email protected]>
24
 * @version 1.0
25
 */
26
class Router extends ExternalModule
27
{
28
    /** Event showing that new gather resource file was created */
29
    const EVENT_CREATED = 'resource.created';
30
31
    /** Event showing that new gather resource file was created */
32
    const EVENT_START_GENERATE_RESOURCES = 'resource.start.generate.resources';
33
34
    /** Identifier */
35
    protected $id = 'resource';
36
37
    /** @var string Marker for inserting generated javascript link */
38
    public $javascriptMarker = '</body>';
39
40
    /** Cached resources path collection */
41
    public $cached = array();
42
43
    /** Collection of updated cached resources for notification of changes */
44
    public $updated = array();
45
46
    /** Pointer to processing module */
47
    private $c_module;
48
49
    /** @var string Current processed resource */
50
    private $cResource;
51
52
    /**
53
     * Parse URL to get module name and relative path to resource
54
     *
55
     * @param string $url String for parsing
56
     *
57
     * @return array Array [0] => module name, [1]=>relative_path
58
     */
59
    public static function parseURL($url, & $module = null, & $path = null)
60
    {
61
        // If we have URL to resource router
62
        if (preg_match('/resourcer\/(?<module>.+)\?p=(?<path>.+)/ui', $url, $matches)) {
63
            $module = $matches['module'];
64
            $path = $matches['path'];
65
66
            return true;
67
        } else {
68
            return false;
69
        }
70
    }
71
72
    /** @see ModuleConnector::init() */
73
    public function init(array $params = array())
74
    {
75
        parent::init($params);
76
77
        $moduleList = $this->system->module_stack;
78
79
        Event::fire(self::EVENT_START_GENERATE_RESOURCES, array(&$moduleList));
80
81
        $this->generateResources($moduleList);
82
83
        // Subscribe to core rendered event
84
        $this->system->subscribe('core.rendered', array($this, 'renderer'));
85
    }
86
87
    public function generateResources($moduleList, $templatePath = 'default')
88
    {
89
        // Cache main web resources
90
        foreach (array(array('js'), array('css', 'less'), array('coffee')) as $rts) {
91
            // Get first resource type as extension
92
            $rt = $rts[0];
93
94
            $hash_name = '';
95
96
            // Iterate gathered namespaces for their resources
97
            /** @var Module $module */
98
            foreach ($moduleList as $id => $module) {
99
                // If necessary resources has been collected
100
                foreach ($rts as $_rt) {
101
                    if (isset($module->resourceMap->$_rt)) {
102
                        foreach ($module->resourceMap->$_rt as $resource) {
103
                            // Created string with last resource modification time
104
                            $hash_name .= filemtime($resource);
105
                        }
106
                    }
107
                }
108
            }
109
110
            // Get hash that's describes resource status
111
            $hash_name = md5($hash_name) . '.' . $rt;
112
113
            $file = $hash_name;
114
115
            $dir = str_replace(array('/', '.'), '_', $templatePath);
116
117
            // If cached file does not exists
118
            if ($this->cache_refresh($file, true, $dir)) {
0 ignored issues
show
Unused Code introduced by
The call to Router::cache_refresh() has too many arguments starting with $dir.

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...
119
                // Read content of resource files
120
                $content = '';
121
                foreach ($moduleList as $id => $module) {
122
                    $this->c_module = $module;
123
                    // If this ns has resources of specified type
124
                    foreach ($rts as $_rt) {
125
                        if (isset($module->resourceMap->$_rt)) {
126
                            //TODO: If you will remove & from iterator - system will fail at last element
127
                            foreach ($module->resourceMap->$_rt as $resource) {
128
                                // Store current processing resource
129
                                $this->cResource = $resource;
130
                                // Read resource file
131
                                $c = file_get_contents($resource);
132
                                // Rewrite url in css
133
                                if ($rt == 'css') {
134
                                    $c = preg_replace_callback('/url\s*\(\s*(\'|\")?([^\)\s\'\"]+)(\'|\")?\s*\)/i',
135
                                        array($this, 'src_replace_callback'), $c);
136
                                }
137
                                // Gather processed resource text together
138
                                $content .= "\n\r" . $c;
139
                            }
140
                        }
141
                    }
142
                }
143
144
                // Fire event that new resource has been generated
145
                Event::fire(self::EVENT_CREATED, array($rt, &$content, &$file, &$this));
146
147
                // Fix updated resource file with new path to it
148
                $this->updated[$rt] = $file;
149
150
                // Запишем содержание нового "собранного" ресурса
151
                file_put_contents($file, $content);
152
            }
153
154
            // Save path to resource cache
155
            $this->cached[$rt][$templatePath] = __SAMSON_CACHE_PATH . $this->id . '/' . $dir . '/' . $hash_name;
156
        }
157
    }
158
159
    /**
160
     * Core render handler for including CSS and JS resources to html
161
     *
162
     * @param sting $view View content
163
     * @param array $data View data
164
     *
165
     * @return string Processed view content
166
     */
167
    public function renderer(&$view, $data = array(), iModule $m = null)
168
    {
169
        $tempateId = isset($this->cached['css'][$this->system->template()]) ? $this->system->template() : 'default';
170
171
        // Define resource urls
172
        $css = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['css'][$tempateId]);
173
        $js = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['js'][$tempateId]);
174
175
        // TODO: Прорисовка зависит от текущего модуля, сделать єто через параметр прорисовщика
176
        // If called from compressor
177
        if ($m->id() == 'compressor') {
0 ignored issues
show
Bug introduced by
It seems like $m is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
178
            $tempateId = isset($this->cached['css'][$data['file']]) ? $data['file'] : 'default';
179
            $css = url()->base() . basename($this->cached['css'][$tempateId]);
180
            $js = url()->base() . basename($this->cached['js'][$tempateId]);
181
        }
182
183
        // Put css link at the end of <head> page block
184
        $view = str_ireplace('</head>',
185
            "\n" . '<link type="text/css" rel="stylesheet" href="' . $css . '">' . "\n" . '</head>', $view);
186
187
        // Put javascript link in the end of the document
188
        $view = str_ireplace($this->javascriptMarker,
189
            "\n" . '<script type="text/javascript" src="' . $js . '"></script>' . "\n" . $this->javascriptMarker,
190
            $view);
191
192
        //elapsed('Rendering view =)');
193
194
        return $view;
195
    }
196
197
    /** Callback for CSS url rewriting */
198
    public function src_replace_callback($matches)
0 ignored issues
show
Coding Style introduced by
Method name "Router::src_replace_callback" is not in camel caps format
Loading history...
199
    {
200
        // Если мы нашли шаблон - переберем все найденные патерны
201
        if (isset($matches[2]) && strpos($matches[2], 'data:') === false) {
202
            // Remove relative path from resource path
203
            $url = str_replace('../', '/', $matches[2]);
204
205
            // Routes with this module controller do not need changes
206
            if (strpos($url, '/' . $this->id . '/') === false) {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
207
208
                // Remove possible GET parameters from resource path
209 View Code Duplication
                if (($getStart = stripos($url, '?')) !== false) {
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...
210
                    $url = substr($url, 0, $getStart);
211
                }
212
213
                // Remove possible HASH parameters from resource path
214 View Code Duplication
                if (($getStart = stripos($url, '#')) !== false) {
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...
215
                    $url = substr($url, 0, $getStart);
216
                }
217
218
                //trace($this->c_module->id.'-'.get_class($this->c_module).'-'.$url.'-'.is_a( $this->c_module, ns_classname('ExternalModule','samson\core')));;
219
220
                // Always rewrite url's for external modules and for remote web applications
221
                if (is_a($this->c_module,
222
                        \samson\core\AutoLoader::className('ExternalModule', 'samson\core')) || __SAMSON_REMOTE_APP
223
                ) {
224
                    // Build real path to resource
225
                    $realPath = $this->c_module->path() . $url;
226
227
                    // Try to find path in module root folder
228
                    if (!file_exists($realPath)) {
229
                        // Build path to "new" module public folder www
230
                        $realPath = $this->c_module->path() . __SAMSON_PUBLIC_PATH . $url;
231
232
                        // Try to find path in module Public folder
233
                        if (file_exists($realPath)) {
234
                            $url = 'www/' . $url;
235
                        } else { // Signal error
236
                            //e('[##][##] Cannot find CSS resource[##] in path[##]',D_SAMSON_DEBUG, array($this->c_module->id, $realPath, $url, $this->cResource));
237
                        }
238
                    }
239
240
                    // Rewrite URL using router
241
                    $url = self::url($url, $this->c_module);
242
                } else if (is_a($this->c_module, \samson\core\AutoLoader::className('LocalModule', 'samson\core'))) {
243
                    $url = url()->base() . $url;
244
                }
245
246
                return 'url("' . $url . '")';
247
            } else {
248
                return 'url("' . $matches[2] . '")';
249
            }
250
        } else { // Just return original value
251
            return $matches[0];
252
        }
253
    }
254
255
    /**
256
     * Получить уникальный URL однозначно определяющий маршрут к ресурсу
257
     * веб-приложения/модуля
258
     *
259
     * @param string $path Путь к требуемому ресурсу вннутри веб-приложения/модуля
260
     * @param string $module Имя модуля которому принадлежит ресурс
0 ignored issues
show
Documentation introduced by
There is no parameter named $module. Did you maybe mean $_module?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
261
     * @param string $app Имя веб-приложения которому принадлежит ресурс
0 ignored issues
show
Bug introduced by
There is no parameter named $app. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
262
     *
263
     * @return string Унифицированный URL для получения ресурса веб-приложения/модуля
264
     */
265
    public static function url($path, $_module)
266
    {
267
        // TODO: rewrite it
268
        // Безопасно получим переданный модуль
269
        $_module = s()->module($_module);
270
271
        // Сформируем URL-маршрут для доступа к ресурсу
272
        return url()->base() . 'resourcer/' . ($_module->id() != 'resourcer' ? $_module->id() : '') . '/?p=' . $path;
273
    }
274
}
275