1
|
|
|
<?php |
2
|
|
|
namespace samsonphp\resource; |
3
|
|
|
|
4
|
|
|
use samson\core\ExternalModule; |
5
|
|
|
use samson\core\iModule; |
6
|
|
|
use samsonphp\event\Event; |
7
|
|
|
use samsonphp\resource\exception\ResourceNotFound; |
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 = STATIC_RESOURCE_HANDLER; |
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 $currentModule; |
48
|
|
|
|
49
|
|
|
/** @var string Current processed resource */ |
50
|
|
|
private $currentResource; |
51
|
|
|
|
52
|
|
|
|
53
|
|
|
/** @see ModuleConnector::init() */ |
54
|
|
|
public function init(array $params = array()) |
55
|
|
|
{ |
56
|
|
|
parent::init($params); |
57
|
|
|
|
58
|
|
|
$moduleList = $this->system->module_stack; |
59
|
|
|
|
60
|
|
|
Event::fire(self::EVENT_START_GENERATE_RESOURCES, array(&$moduleList)); |
61
|
|
|
|
62
|
|
|
$this->generateResources($moduleList); |
63
|
|
|
|
64
|
|
|
// Subscribe to core rendered event |
65
|
|
|
$this->system->subscribe('core.rendered', array($this, 'renderer')); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
public function generateResources($moduleList, $templatePath = 'default') |
69
|
|
|
{ |
70
|
|
|
// Cache main web resources |
71
|
|
|
foreach (array(array('js'), array('css', 'less'), array('coffee')) as $rts) { |
72
|
|
|
// Get first resource type as extension |
73
|
|
|
$rt = $rts[0]; |
74
|
|
|
|
75
|
|
|
$hash_name = ''; |
76
|
|
|
|
77
|
|
|
// Iterate gathered namespaces for their resources |
78
|
|
|
/** @var Module $module */ |
79
|
|
|
foreach ($moduleList as $id => $module) { |
80
|
|
|
// If necessary resources has been collected |
81
|
|
|
foreach ($rts as $_rt) { |
82
|
|
|
if (isset($module->resourceMap->$_rt)) { |
83
|
|
|
foreach ($module->resourceMap->$_rt as $resource) { |
84
|
|
|
// Created string with last resource modification time |
85
|
|
|
$hash_name .= filemtime($resource); |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
// Get hash that's describes resource status |
92
|
|
|
$hash_name = md5($hash_name) . '.' . $rt; |
93
|
|
|
|
94
|
|
|
$file = $hash_name; |
95
|
|
|
|
96
|
|
|
$dir = str_replace(array('/', '.'), '_', $templatePath); |
97
|
|
|
|
98
|
|
|
// If cached file does not exists |
99
|
|
|
if ($this->cache_refresh($file, true, $dir)) { |
|
|
|
|
100
|
|
|
// Read content of resource files |
101
|
|
|
$content = ''; |
102
|
|
|
foreach ($moduleList as $id => $module) { |
103
|
|
|
$this->currentModule = $module; |
104
|
|
|
// If this ns has resources of specified type |
105
|
|
|
foreach ($rts as $_rt) { |
106
|
|
|
if (isset($module->resourceMap->$_rt)) { |
107
|
|
|
//TODO: If you will remove & from iterator - system will fail at last element |
108
|
|
|
foreach ($module->resourceMap->$_rt as $resource) { |
109
|
|
|
// Store current processing resource |
110
|
|
|
$this->currentResource = $resource; |
111
|
|
|
// Read resource file |
112
|
|
|
$c = file_get_contents($resource); |
113
|
|
|
// Rewrite url in css |
114
|
|
|
if ($rt == 'css') { |
115
|
|
|
$c = preg_replace_callback('/url\s*\(\s*(\'|\")?([^\)\s\'\"]+)(\'|\")?\s*\)/i', |
116
|
|
|
array($this, 'src_replace_callback'), $c); |
117
|
|
|
} |
118
|
|
|
// Gather processed resource text together |
119
|
|
|
$content .= "\n\r" . $c; |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
// Fire event that new resource has been generated |
126
|
|
|
Event::fire(self::EVENT_CREATED, array($rt, &$content, &$file, &$this)); |
127
|
|
|
|
128
|
|
|
// Fix updated resource file with new path to it |
129
|
|
|
$this->updated[$rt] = $file; |
130
|
|
|
|
131
|
|
|
// Запишем содержание нового "собранного" ресурса |
132
|
|
|
file_put_contents($file, $content); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
// Save path to resource cache |
136
|
|
|
$this->cached[$rt][$templatePath] = __SAMSON_CACHE_PATH . $this->id . '/' . $dir . '/' . $hash_name; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Core render handler for including CSS and JS resources to html |
142
|
|
|
* |
143
|
|
|
* @param sting $view View content |
144
|
|
|
* @param array $data View data |
145
|
|
|
* |
146
|
|
|
* @return string Processed view content |
147
|
|
|
*/ |
148
|
|
|
public function renderer(&$view, $data = array(), iModule $m = null) |
149
|
|
|
{ |
150
|
|
|
$tempateId = isset($this->cached['css'][$this->system->template()]) ? $this->system->template() : 'default'; |
151
|
|
|
|
152
|
|
|
// Define resource urls |
153
|
|
|
$css = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['css'][$tempateId]); |
154
|
|
|
$js = url()->base() . str_replace(__SAMSON_PUBLIC_PATH, '', $this->cached['js'][$tempateId]); |
155
|
|
|
|
156
|
|
|
// TODO: Прорисовка зависит от текущего модуля, сделать єто через параметр прорисовщика |
157
|
|
|
// If called from compressor |
158
|
|
|
if ($m->id() == 'compressor') { |
|
|
|
|
159
|
|
|
$tempateId = isset($this->cached['css'][$data['file']]) ? $data['file'] : 'default'; |
160
|
|
|
$css = url()->base() . basename($this->cached['css'][$tempateId]); |
161
|
|
|
$js = url()->base() . basename($this->cached['js'][$tempateId]); |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
// Put css link at the end of <head> page block |
165
|
|
|
$view = str_ireplace('</head>', |
166
|
|
|
"\n" . '<link type="text/css" rel="stylesheet" href="' . $css . '">' . "\n" . '</head>', $view); |
167
|
|
|
|
168
|
|
|
// Put javascript link in the end of the document |
169
|
|
|
$view = str_ireplace($this->javascriptMarker, |
170
|
|
|
"\n" . '<script type="text/javascript" src="' . $js . '"></script>' . "\n" . $this->javascriptMarker, |
171
|
|
|
$view); |
172
|
|
|
|
173
|
|
|
//elapsed('Rendering view =)'); |
174
|
|
|
|
175
|
|
|
return $view; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
/** |
179
|
|
|
* Callback for CSS url(...) rewriting. |
180
|
|
|
* |
181
|
|
|
* @param array $matches Regular expression matches collection |
182
|
|
|
* |
183
|
|
|
* @return string Rewritten url(..) with static resource handler url |
184
|
|
|
* @throws ResourceNotFound |
185
|
|
|
*/ |
186
|
|
|
public function src_replace_callback($matches) |
|
|
|
|
187
|
|
|
{ |
188
|
|
|
// If we have found static resource path definition and its not inline |
189
|
|
|
if (array_key_exists(2, $matches) && strpos($matches[2], 'data:') === false) { |
190
|
|
|
// Store static resource path |
191
|
|
|
$url = $matches[2]; |
192
|
|
|
|
193
|
|
|
// Ignore preprocessor vars |
194
|
|
|
// TODO: This is totally wrong need to come up with decision |
195
|
|
|
if (strpos($url, '@') !== false) { |
196
|
|
|
return $matches[0]; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
// Remove possible GET parameters from resource path |
200
|
|
View Code Duplication |
if (($getStart = stripos($url, '?')) !== false) { |
|
|
|
|
201
|
|
|
$url = substr($url, 0, $getStart); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
// Remove possible HASH parameters from resource path |
205
|
|
View Code Duplication |
if (($getStart = stripos($url, '#')) !== false) { |
|
|
|
|
206
|
|
|
$url = substr($url, 0, $getStart); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
// Build path to static resource handler |
210
|
|
|
return 'url("/' . $this->id . '/?p=' |
211
|
|
|
.Resource::getRelativePath($url, dirname($this->currentResource)) |
212
|
|
|
. '")'; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
return $matches[0]; |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
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.