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)) { |
|
|
|
|
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') { |
|
|
|
|
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) |
|
|
|
|
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) { |
|
|
|
|
207
|
|
|
|
208
|
|
|
// Remove possible GET parameters from resource path |
209
|
|
View Code Duplication |
if (($getStart = stripos($url, '?')) !== false) { |
|
|
|
|
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) { |
|
|
|
|
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 Имя модуля которому принадлежит ресурс |
|
|
|
|
261
|
|
|
* @param string $app Имя веб-приложения которому принадлежит ресурс |
|
|
|
|
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
|
|
|
|
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.