|
1
|
|
|
<?php |
|
2
|
|
|
namespace samsonphp\compressor; |
|
3
|
|
|
|
|
4
|
|
|
use samson\core\Core; |
|
|
|
|
|
|
5
|
|
|
use samson\core\iModule; |
|
6
|
|
|
use samsonframework\localfilemanager\LocalFileManager; |
|
7
|
|
|
use samsonframework\resource\ResourceMap; |
|
8
|
|
|
use samsonos\compressor\Module; |
|
9
|
|
|
use samsonphp\compressor\resource\JavaScript; |
|
10
|
|
|
use samsonphp\event\Event; |
|
11
|
|
|
use samsonphp\resource\Router; |
|
12
|
|
|
|
|
13
|
|
|
/** |
|
14
|
|
|
* Module for automatic code optimization|compression |
|
15
|
|
|
* |
|
16
|
|
|
* @package samsonos\compressor |
|
17
|
|
|
* @author Vitaly Iegorov <[email protected]> |
|
18
|
|
|
*/ |
|
19
|
|
|
class Compressor |
|
20
|
|
|
{ |
|
21
|
|
|
/** Identifier of global namespace */ |
|
22
|
|
|
const NS_GLOBAL = ''; |
|
23
|
|
|
|
|
24
|
|
|
const E_CREATE_MODULE_LIST = 'compressor.create.module.list'; |
|
25
|
|
|
|
|
26
|
|
|
const E_CREATE_RESOURCE_LIST = 'compressor.create.resource.list'; |
|
27
|
|
|
|
|
28
|
|
|
const E_RESOURCE_COMPRESS = 'compressor.resource.compress'; |
|
29
|
|
|
|
|
30
|
|
|
/** Array key for storing last generated data */ |
|
31
|
|
|
const VIEWS = 'views'; |
|
32
|
|
|
|
|
33
|
|
|
/** Output path for compressed web application */ |
|
34
|
|
|
public $output = 'out/'; |
|
35
|
|
|
|
|
36
|
|
|
/** Collection of requires to insert in compressed file */ |
|
37
|
|
|
public $require = array(); |
|
38
|
|
|
|
|
39
|
|
|
/** Ignored resource extensions */ |
|
40
|
|
|
public $ignored_extensions = array('php', 'js', 'css', 'md', 'map', 'dbs', 'vphp', 'less', 'gz', 'lock', 'json', 'sql', 'xml', 'yml'); |
|
41
|
|
|
|
|
42
|
|
|
/** Ignored resource files */ |
|
43
|
|
|
public $ignored_resources = array('.project', '.buildpath', '.gitignore', '.travis.yml', 'phpunit.xml', 'thumbs.db', 'Thumbs.db'); |
|
44
|
|
|
|
|
45
|
|
|
/** @var array Collection of folders to be ignored by compressor */ |
|
46
|
|
|
public $ignoredFolders = array('vendor', 'var'); |
|
47
|
|
|
|
|
48
|
|
|
/** @var array Collection of file paths to be ignored by compressor */ |
|
49
|
|
|
public $ignoredFiles = array(); |
|
50
|
|
|
|
|
51
|
|
|
/** Папка где размещается исходное веб-приложение */ |
|
52
|
|
|
public $input = __SAMSON_CWD__; |
|
53
|
|
|
|
|
54
|
|
|
/** View rendering mode */ |
|
55
|
|
|
protected $view_mode = Core::RENDER_VARIABLE; |
|
|
|
|
|
|
56
|
|
|
|
|
57
|
|
|
/** Указатель на текущий сворачиваемый модуль */ |
|
58
|
|
|
protected $current; |
|
59
|
|
|
|
|
60
|
|
|
/** Коллекция уже обработанных файлов */ |
|
61
|
|
|
protected $files = array(); |
|
62
|
|
|
|
|
63
|
|
|
/** Collection for storing all php code by namespace */ |
|
64
|
|
|
private $php = array(self::NS_GLOBAL => array()); |
|
65
|
|
|
|
|
66
|
|
|
/** @var string Web-application environment identifier */ |
|
67
|
|
|
protected $environment = 'prod'; |
|
68
|
|
|
|
|
69
|
|
|
/** @var bool Debug flag */ |
|
70
|
|
|
protected $debug = false; |
|
71
|
|
|
|
|
72
|
|
|
/** @var string Supported php version */ |
|
73
|
|
|
protected $phpVersion = PHP_VERSION; |
|
74
|
|
|
|
|
75
|
|
|
protected $resourceManager; |
|
76
|
|
|
|
|
77
|
|
|
protected $classConst = array(); |
|
78
|
|
|
|
|
79
|
|
|
|
|
80
|
|
|
protected $resourceUrlsList = []; |
|
81
|
|
|
|
|
82
|
|
|
/** @var FileManagerInterface File system manager */ |
|
83
|
|
|
protected $fileManager; |
|
84
|
|
|
|
|
85
|
|
|
/** |
|
86
|
|
|
* Compress web-application |
|
87
|
|
|
* @param string $output Path for creating compressed version |
|
88
|
|
|
* @param boolean $debug Disable errors output |
|
89
|
|
|
* @param string $environment Configuration environment |
|
90
|
|
|
* @param string $phpVersion PHP version to support |
|
91
|
|
|
* @param array $configuration Configuration |
|
92
|
|
|
*/ |
|
93
|
|
|
public function __construct($output = 'out/', $debug = false, $environment = 'prod', $phpVersion = PHP_VERSION, $configuration = array()) |
|
94
|
|
|
{ |
|
95
|
|
|
$this->resourceManager = new resource\Generic($this); |
|
96
|
|
|
|
|
97
|
|
|
$this->fileManager = new LocalFileManager(); |
|
|
|
|
|
|
98
|
|
|
|
|
99
|
|
|
$this->output = $output; |
|
100
|
|
|
$this->debug = $debug; |
|
101
|
|
|
$this->environment = $environment; |
|
102
|
|
|
$this->phpVersion = $phpVersion; |
|
103
|
|
|
foreach ($configuration as $key => $value) { |
|
104
|
|
|
// If object has configured property defined |
|
105
|
|
|
if (property_exists($this, $key)) { |
|
106
|
|
|
// Set object variable value |
|
107
|
|
|
$this->$key = $value; |
|
108
|
|
|
} |
|
109
|
|
|
} |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
|
|
/** |
|
113
|
|
|
* Свернуть файл представления |
|
114
|
|
|
* |
|
115
|
|
|
* @param string $view_file Полный путь к файлу представления |
|
116
|
|
|
* @param iModule $module Указатель на модуль которому принадлежит это представление |
|
117
|
|
|
* |
|
118
|
|
|
* @return bool |
|
119
|
|
|
*/ |
|
120
|
|
|
public function compress_view($view_file, iModule & $module) |
|
|
|
|
|
|
121
|
|
|
{ |
|
122
|
|
|
// Build relative path to module view |
|
123
|
|
|
$rel_path = ($module->id() == 'local' ? '' : $module->id() . '/') . str_replace($module->path(), '', $view_file); |
|
124
|
|
|
|
|
125
|
|
|
$this->log(' -- Preparing view[##] relative path [##]', $view_file, $rel_path); |
|
126
|
|
|
|
|
127
|
|
|
// Прочитаем файл представления |
|
128
|
|
|
$view_html = file_get_contents($view_file); |
|
129
|
|
|
|
|
130
|
|
|
if (!isset($view_file{0})) return e('View: ##(##) is empty', E_SAMSON_SNAPSHOT_ERROR, array($view_file, $rel_path)); |
|
|
|
|
|
|
131
|
|
|
|
|
132
|
|
|
// TODO: should be done via events in resourcer module |
|
133
|
|
|
// Найдем обращения к роутеру ресурсов |
|
134
|
|
|
$view_html = preg_replace_callback( |
|
135
|
|
|
'/(<\?php)*\s*src\s*\(\s*(\'|\")?(?<path>[^\'\"\?\;\)]+)(\'|\")?(\s*,\s*(\'|\")(?<module>[^\'\"\)]+)(\'|\"))?\s*\)\;?(\s*\?>)?/uis', |
|
136
|
|
|
array($this, 'src_replace_callback'), |
|
137
|
|
|
$view_html |
|
138
|
|
|
); |
|
139
|
|
|
|
|
140
|
|
|
// Replace old inline php tags |
|
141
|
|
|
$view_html = str_ireplace('<? ', '<?php ', $view_html); |
|
142
|
|
|
|
|
143
|
|
|
// Сожмем HTML |
|
144
|
|
|
$view_html = Minify_HTML::minify($view_html); |
|
145
|
|
|
|
|
146
|
|
|
// Fire event to render view correctly |
|
147
|
|
|
Event::fire('core.render', array(&$view_html, array(), &$module)); |
|
148
|
|
|
|
|
149
|
|
|
// Template re-rendering |
|
150
|
|
|
// TODO: We must split regular view and template file to handle differently, for now nothing will change but in future.... |
|
151
|
|
|
|
|
152
|
|
|
$template = !isset($this->resourceUrlsList[$view_file])?Router::I_MAIN_PROJECT_TEMPLATE:$view_file; |
|
153
|
|
|
|
|
154
|
|
|
Event::fire('core.rendered', array(&$view_html, $this->resourceUrlsList[$template])); |
|
155
|
|
|
|
|
156
|
|
|
$view_php = "<<<'EOT'" . "\n" . $view_html . "\n" . "EOT;"; |
|
157
|
|
|
|
|
158
|
|
|
// Add view code to final global namespace |
|
159
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . '$GLOBALS["__compressor_files"]["' . $rel_path . '"] = ' . $view_php; |
|
160
|
|
|
} |
|
161
|
|
|
|
|
162
|
|
|
/** |
|
163
|
|
|
* Свернуть модуль |
|
164
|
|
|
* |
|
165
|
|
|
* @param iModule $module Указатель на модуль для сворачивания |
|
166
|
|
|
* @param ResourceMap $data |
|
167
|
|
|
*/ |
|
168
|
|
|
public function compress_module(iModule &$module, ResourceMap &$data) |
|
|
|
|
|
|
169
|
|
|
{ |
|
170
|
|
|
// Идентификатор модуля |
|
171
|
|
|
$id = $module->id(); |
|
172
|
|
|
|
|
173
|
|
|
// Сохраним указатель на текущий модуль |
|
174
|
|
|
$this->current = &$module; |
|
175
|
|
|
|
|
176
|
|
|
// Build output module path |
|
177
|
|
|
$module_output_path = $id == 'local' ? '' : basename($module->path()) . '/'; |
|
178
|
|
|
|
|
179
|
|
|
// Build resource source path |
|
180
|
|
|
$module_path = $id == 'local' ? $module->path() . __SAMSON_PUBLIC_PATH : $module->path(); |
|
181
|
|
|
|
|
182
|
|
|
$this->log(' - Compressing module[##] from [##]', $id, $module_path); |
|
183
|
|
|
|
|
184
|
|
|
// Call special method enabling module personal resource pre-management on compressing |
|
185
|
|
|
if ($module->beforeCompress($this, $this->php) !== false) { |
|
186
|
|
|
// Copy all module resources |
|
187
|
|
|
$this->copy_path_resources($data->resources, $module_path, $module_output_path); |
|
188
|
|
|
|
|
189
|
|
|
// Internal collection of module php code, not views |
|
190
|
|
|
$module_php = array(); |
|
191
|
|
|
|
|
192
|
|
|
foreach ($data->classes as $key => $php) { |
|
193
|
|
|
$this->compress_php($key, $module, $module_php); |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
// Iterate module plain php code |
|
197
|
|
|
foreach ($data->php as $php) { |
|
198
|
|
|
$this->compress_php($php, $module, $module_php); |
|
199
|
|
|
} |
|
200
|
|
|
|
|
201
|
|
|
foreach ($data->globals as $php) { |
|
202
|
|
|
$this->compress_php($php, $module, $module_php); |
|
203
|
|
|
} |
|
204
|
|
|
|
|
205
|
|
|
// Iterate module controllers php code |
|
206
|
|
|
foreach ($data->controllers as $php) { |
|
207
|
|
|
$this->compress_php($php, $module, $module_php); |
|
208
|
|
|
} |
|
209
|
|
|
// Iterate module controllers php code |
|
210
|
|
|
foreach ($data->modules as $php) { |
|
211
|
|
|
$this->compress_php($php[1], $module, $module_php); |
|
212
|
|
|
} |
|
213
|
|
|
// Iterate module model php code |
|
214
|
|
|
foreach ($data->models as $php) { |
|
215
|
|
|
$this->compress_php($php, $module, $module_php); |
|
216
|
|
|
} |
|
217
|
|
|
// Iterate module views |
|
218
|
|
|
foreach ($data->views as $php) { |
|
219
|
|
|
$this->compress_view($php, $module); |
|
220
|
|
|
} |
|
221
|
|
|
} |
|
222
|
|
|
|
|
223
|
|
|
// Call special method enabling module personal resource post-management on compressing |
|
224
|
|
|
$module->afterCompress($this, $module_php); |
|
|
|
|
|
|
225
|
|
|
|
|
226
|
|
|
// Gather all code in to global code collection with namespaces |
|
227
|
|
|
$this->code_array_combine($module_php, $this->php); |
|
228
|
|
|
|
|
229
|
|
|
// Change module path |
|
230
|
|
|
$module->path($id . '/'); |
|
231
|
|
|
} |
|
232
|
|
|
|
|
233
|
|
|
public function rewriteResourceRouter(array $matches) |
|
234
|
|
|
{ |
|
235
|
|
|
//trace($matches,1); |
|
236
|
|
|
//die; |
|
237
|
|
|
return '"' . $this->current->id() . '/' . $matches['path'] . '";'; |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
/** |
|
241
|
|
|
* Обработчик замены роутера ресурсов |
|
242
|
|
|
* @param array $matches Найденые совпадения по шаблону |
|
243
|
|
|
* @return string Обработанный вариант пути к ресурсу |
|
244
|
|
|
*/ |
|
245
|
|
|
public function src_replace_callback($matches) |
|
|
|
|
|
|
246
|
|
|
{ |
|
247
|
|
|
// Получим относительный путь к ресурсу |
|
248
|
|
|
$path = trim($matches['path']); |
|
249
|
|
|
|
|
250
|
|
|
// Путь к модуля после сжимания |
|
251
|
|
|
$module_path = (isset($matches['module']) && strlen($matches['module']) > 0) ? $matches['module'] . '/' : $this->current->id() . '/'; |
|
252
|
|
|
|
|
253
|
|
|
// Если передана переменная мы не можем гарантировать её значение |
|
254
|
|
|
if (strpos($path, '$') !== false) $path = '<?php echo \'' . $module_path . '\'.' . $path . '; ?>'; |
|
|
|
|
|
|
255
|
|
|
// Просто строка |
|
256
|
|
|
else $path = $module_path . $path; |
|
|
|
|
|
|
257
|
|
|
|
|
258
|
|
|
return $path; |
|
259
|
|
|
//e('Файл представления ## - Обращение к роутеру ресурсов через переменную ##', E_SAMSON_SNAPSHOT_ERROR, array($view_path, $path)); |
|
260
|
|
|
} |
|
261
|
|
|
|
|
262
|
|
|
/** Generic log function for further modification */ |
|
263
|
|
|
public function log($message) |
|
264
|
|
|
{ |
|
265
|
|
|
// Get passed vars |
|
266
|
|
|
$vars = func_get_args(); |
|
267
|
|
|
// Remove first message var |
|
268
|
|
|
array_shift($vars); |
|
269
|
|
|
|
|
270
|
|
|
// Render debug message |
|
271
|
|
|
return trace(debug_parse_markers($message, $vars)); |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
/** |
|
275
|
|
|
* Compress web-application |
|
276
|
|
|
* @param boolean $debug Disable errors output |
|
277
|
|
|
* @param string $php_version PHP version to support |
|
278
|
|
|
*/ |
|
279
|
|
|
public function compress($debug = false, $environment = 'prod', $php_version = PHP_VERSION) |
|
280
|
|
|
{ |
|
281
|
|
|
// Set compressed project environment |
|
282
|
|
|
$this->environment = $environment; |
|
283
|
|
|
|
|
284
|
|
|
elapsed('Started web-application compression[' . $this->environment . ']'); |
|
285
|
|
|
|
|
286
|
|
|
s()->async(true); |
|
|
|
|
|
|
287
|
|
|
ini_set('memory_limit', '256M'); |
|
288
|
|
|
|
|
289
|
|
|
// Check output path |
|
290
|
|
|
if (!isset($this->output{0})) { |
|
291
|
|
|
return $this->log('Cannot compress web-application from [##] - No output path is specified', $this->input); |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
// Define rendering model depending on PHP version |
|
295
|
|
|
$php_version = isset($php_version{0}) ? $php_version : PHP_VERSION; |
|
296
|
|
|
if (version_compare($php_version, '5.3.0', '<')) { |
|
297
|
|
|
$this->view_mode = Core::RENDER_ARRAY; |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
// Add url base to path |
|
301
|
|
|
$this->output .= url()->base(); |
|
302
|
|
|
|
|
303
|
|
|
// Creating output project folder |
|
304
|
|
|
$result = \samson\core\File::mkdir($this->output); |
|
305
|
|
|
if ($result) { |
|
306
|
|
|
$this->log('Created output project folder [##]', $this->output); |
|
307
|
|
|
} else if ($result == -1) { |
|
308
|
|
|
return $this->log('Compression failed! Cannot create output project folder [##]', $this->output); |
|
309
|
|
|
} |
|
310
|
|
|
|
|
311
|
|
|
// Remove all trailing slashes |
|
312
|
|
|
$this->output = realpath($this->output) . '/'; |
|
313
|
|
|
|
|
314
|
|
|
$this->log('[##]## Compressing web-application[##] from [##] to [##]', |
|
315
|
|
|
$environment, |
|
316
|
|
|
$debug ? '[DEBUG]' : '', |
|
317
|
|
|
$php_version, |
|
318
|
|
|
$this->input, |
|
319
|
|
|
$this->output |
|
320
|
|
|
); |
|
321
|
|
|
|
|
322
|
|
|
// Add generic composer auto loader require |
|
323
|
|
|
$this->php['__before_all']['composer'] = "\n" . 'if(file_exists("vendor/autoload.php")) require "vendor/autoload.php";'; |
|
324
|
|
|
|
|
325
|
|
|
// Define global views collection |
|
326
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] = "\n" . '$GLOBALS["__compressor_files"] = array();'; |
|
327
|
|
|
|
|
328
|
|
|
// If resourcer is loaded - copy css and js |
|
329
|
|
|
// Link |
|
330
|
|
|
$rr = &s()->module_stack['resourcer']; |
|
|
|
|
|
|
331
|
|
|
|
|
332
|
|
|
// Iterate all css and js resources |
|
333
|
|
|
$ignoreFolders = array(); |
|
334
|
|
|
foreach ($this->ignoredFolders as $folder) { |
|
335
|
|
|
$ignoreFolders[] = $this->output . $folder; |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
// Remove all old javascript and css |
|
339
|
|
|
\samson\core\File::clear($this->output, array('js', 'css'), $ignoreFolders); |
|
340
|
|
|
|
|
341
|
|
|
$moduleListArray = []; |
|
342
|
|
|
|
|
343
|
|
|
//$moduleListArray[Router::I_MAIN_PROJECT_TEMPLATE] = $this->system->module_stack; |
|
344
|
|
|
|
|
345
|
|
|
Event::fire(self::E_CREATE_MODULE_LIST, array(& $moduleListArray)); |
|
346
|
|
|
|
|
347
|
|
|
$resource = new Resource($this->fileManager); |
|
348
|
|
|
|
|
349
|
|
|
foreach ($moduleListArray as $template => $moduleList) |
|
350
|
|
|
{ |
|
351
|
|
|
$resourceUrls = []; |
|
352
|
|
|
|
|
353
|
|
|
Event::fire(self::E_CREATE_RESOURCE_LIST, array(& $resourceUrls, $moduleList)); |
|
354
|
|
|
|
|
355
|
|
|
foreach ($resourceUrls as $type => $urls) { |
|
356
|
|
|
$file = $resource->compress($urls, $type, $this->output); |
|
357
|
|
|
$this->resourceUrlsList[$template][$type] = [DIRECTORY_SEPARATOR.$file]; |
|
358
|
|
|
} |
|
359
|
|
|
} |
|
360
|
|
|
|
|
361
|
|
|
// Iterate core ns resources collection |
|
362
|
|
|
foreach (s()->module_stack as $id => &$module) { |
|
|
|
|
|
|
363
|
|
|
// Work only with compressable modules |
|
364
|
|
|
if (is_a($module, ns_classname('CompressInterface', 'samsonframework\core')) || |
|
|
|
|
|
|
365
|
|
|
(isset($this->composerParameters['samsonphp_package_compressable']) && |
|
|
|
|
|
|
366
|
|
|
($this->composerParameters['samsonphp_package_compressable'] = 1)) |
|
367
|
|
|
) { |
|
368
|
|
|
$this->compress_module($module, $module->resourceMap); |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
// Change path to local modules |
|
372
|
|
|
if (is_a($module, '\samson\core\VirtualModule')) { |
|
373
|
|
|
$module->path(''); |
|
374
|
|
|
} |
|
375
|
|
|
} |
|
376
|
|
|
|
|
377
|
|
|
|
|
378
|
|
|
|
|
379
|
|
|
|
|
380
|
|
|
|
|
381
|
|
|
|
|
382
|
|
|
|
|
383
|
|
|
|
|
384
|
|
|
/*foreach ($rr->cached['js'] as $jsCachedFile) { |
|
385
|
|
|
// Manage javascript resource |
|
386
|
|
|
$javascriptManager = new resource\JavaScript($this); |
|
387
|
|
|
$javascriptManager->compress(__SAMSON_CWD__ . $jsCachedFile, $this->output . basename($jsCachedFile)); |
|
388
|
|
|
} |
|
389
|
|
|
|
|
390
|
|
|
foreach ($rr->cached['css'] as $cssCachedFile) { |
|
391
|
|
|
// Manage CSS resource |
|
392
|
|
|
$cssManager = new resource\CSS($this, $rr); |
|
393
|
|
|
$cssManager->compress(__SAMSON_CWD__ . $cssCachedFile, $this->output . basename($cssCachedFile)); |
|
394
|
|
|
}*/ |
|
395
|
|
|
//} |
|
396
|
|
|
|
|
397
|
|
|
// Copy main project composer.json |
|
398
|
|
|
$composerPath = __SAMSON_CWD__ . 'composer.json'; |
|
399
|
|
|
if (file_exists($composerPath)) { |
|
400
|
|
|
// Read json file |
|
401
|
|
|
$composerJSON = (array)json_decode(file_get_contents($composerPath)); |
|
402
|
|
|
// Remove development dependencies |
|
403
|
|
|
unset($composerJSON['require-dev']); |
|
404
|
|
|
// Remove autoload section |
|
405
|
|
|
unset($composerJSON['autoload']); |
|
406
|
|
|
// Remove install/update scripts |
|
407
|
|
|
unset($composerJSON['scripts']); |
|
408
|
|
|
|
|
409
|
|
|
// Write modified composer.json |
|
410
|
|
|
file_put_contents($this->output . 'composer.json', json_encode($composerJSON)); |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
// Set errors output |
|
414
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . '\samson\core\Error::$OUTPUT = ' . (!$debug ? 'false' : 'true') . ';'; |
|
415
|
|
|
|
|
416
|
|
|
// Create SamsonPHP core compressor |
|
417
|
|
|
$core = new \samsonphp\compressor\Core(s(), $environment, $this); |
|
|
|
|
|
|
418
|
|
|
|
|
419
|
|
|
// Add global base64 serialized core string |
|
420
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . '$GLOBALS["__CORE_SNAPSHOT"] = \'' . $core->compress() . '\';'; |
|
421
|
|
|
|
|
422
|
|
|
// Add all specified requires |
|
423
|
|
|
foreach ($this->require as $require) $this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'require("' . $require . '");'; |
|
|
|
|
|
|
424
|
|
|
|
|
425
|
|
|
// Add localization data |
|
426
|
|
|
$locale_str = array(); |
|
427
|
|
|
foreach (\samson\core\SamsonLocale::$locales as $locale) { |
|
428
|
|
|
if ($locale != '') { |
|
429
|
|
|
$locale_str[] = '\'' . $locale . '\''; |
|
430
|
|
|
} |
|
431
|
|
|
} |
|
432
|
|
|
// Add [setlocales] code |
|
433
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'setlocales( ' . implode(',', $locale_str) . ');'; |
|
434
|
|
|
|
|
435
|
|
|
// TODO: add generic handlers to modules to provide compressing logic for each module |
|
436
|
|
|
// TODO: add generic constants namespace to put all constants definition there - and put only defined constrat and redeclare them |
|
437
|
|
|
|
|
438
|
|
|
// TODO: WTF???? Thi must be local module logic |
|
439
|
|
|
// If this is remote web-app - collect local resources |
|
440
|
|
|
if (__SAMSON_REMOTE_APP) { |
|
|
|
|
|
|
441
|
|
|
// Gather all resources |
|
442
|
|
|
$path = __SAMSON_CWD__; |
|
443
|
|
|
$ls = array(); |
|
444
|
|
|
s()->resources($path, $ls); |
|
|
|
|
|
|
445
|
|
|
|
|
446
|
|
|
// If we have any resources |
|
447
|
|
|
if (isset($ls['resources'])) { |
|
448
|
|
|
$this->copy_path_resources($ls['resources'], __SAMSON_CWD__, ''); |
|
449
|
|
|
} |
|
450
|
|
|
} |
|
451
|
|
|
|
|
452
|
|
|
// If default locale is defined |
|
453
|
|
|
if (!defined('DEFAULT_LOCALE')) { |
|
454
|
|
|
define('DEFAULT_LOCALE', 'ru'); |
|
455
|
|
|
} |
|
456
|
|
|
|
|
457
|
|
|
// Add default system locale to them end of core definition |
|
458
|
|
|
$this->php['samson\core'][self::VIEWS] = "\n" . 'define("DEFAULT_LOCALE", "' . DEFAULT_LOCALE . '");'; |
|
459
|
|
|
|
|
460
|
|
|
// Pointer to entry script code |
|
461
|
|
|
$entryScriptPath = __SAMSON_CWD__ . __SAMSON_PUBLIC_PATH . 'index.php'; |
|
462
|
|
|
|
|
463
|
|
|
$entryScript = &$this->php[self::NS_GLOBAL][$entryScriptPath]; |
|
464
|
|
|
|
|
465
|
|
|
// Collect all event system data |
|
466
|
|
|
$eventCompressor = new EventCompressor(); |
|
467
|
|
|
$eventCompressor->collect($entryScript); |
|
468
|
|
|
|
|
469
|
|
|
// Remove standard framework entry point from index.php - just preserve default controller |
|
470
|
|
|
if (preg_match('/start\(\s*(\'|\")(?<default>[^\'\"]+)/i', $entryScript, $matches)) { |
|
471
|
|
|
/* |
|
472
|
|
|
* Temporary solution to support compressed version, because other way localization does not work, |
|
473
|
|
|
* as chain is broken, first time URL object is created and URL is parsed only after start, so |
|
474
|
|
|
* CMS::afterCompress does not knows what is current locale and does not inject it to all material |
|
475
|
|
|
* queries. |
|
476
|
|
|
*/ |
|
477
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 'url();'; |
|
478
|
|
|
|
|
479
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] .= "\n" . 's()->start(\'' . $matches['default'] . '\');'; |
|
480
|
|
|
} else e('Default module definition not found - possible errors at compressed version'); |
|
|
|
|
|
|
481
|
|
|
|
|
482
|
|
|
// Clear default entry point |
|
483
|
|
|
unset($this->php[self::NS_GLOBAL][$entryScriptPath]); |
|
484
|
|
|
|
|
485
|
|
|
// Set global namespace as last |
|
486
|
|
|
$global_ns = $this->php[self::NS_GLOBAL]; |
|
487
|
|
|
unset($this->php[self::NS_GLOBAL]); |
|
488
|
|
|
$this->php[self::NS_GLOBAL] = $global_ns; |
|
489
|
|
|
|
|
490
|
|
|
// Set view data to the end of global namespace |
|
491
|
|
|
$s = $this->php[self::NS_GLOBAL][self::VIEWS]; |
|
492
|
|
|
unset($this->php[self::NS_GLOBAL][self::VIEWS]); |
|
493
|
|
|
$this->php[self::NS_GLOBAL][self::VIEWS] = $s; |
|
494
|
|
|
|
|
495
|
|
|
// Load all OOP entities |
|
496
|
|
|
$classes = array(); |
|
497
|
|
|
// Соберем коллекцию загруженных интерфейсов их файлов по пространствам имен |
|
498
|
|
|
$this->classes_to_ns_files(get_declared_interfaces(), $classes); |
|
499
|
|
|
|
|
500
|
|
|
// Соберем коллекцию загруженных классов их файлов по пространствам имен |
|
501
|
|
|
$this->classes_to_ns_files(get_declared_classes(), $classes); |
|
502
|
|
|
|
|
503
|
|
|
// Fix OOP entities |
|
504
|
|
|
foreach ($this->php as $ns => & $files) { |
|
505
|
|
|
// If this namespace has been loaded |
|
506
|
|
|
if (isset($classes[$ns])) { |
|
507
|
|
|
// Fill namespace entities, make OOP entities correct order |
|
508
|
|
|
$files = array_merge($classes[$ns], $files); |
|
509
|
|
|
} |
|
510
|
|
|
} |
|
511
|
|
|
|
|
512
|
|
|
// Соберем весь PHP код в один файл |
|
513
|
|
|
$index_php = $this->code_array_to_str($this->php, ($this->view_mode == 2)); |
|
514
|
|
|
|
|
515
|
|
|
// Collect all event system data |
|
516
|
|
|
$eventCompressor->collect($index_php); |
|
517
|
|
|
|
|
518
|
|
|
// Transform event system in all project code |
|
519
|
|
|
if ($eventCompressor->transform($index_php, $index_php)) { |
|
520
|
|
|
//trace($eventCompressor->subscriptions, true); |
|
521
|
|
|
} |
|
522
|
|
|
|
|
523
|
|
|
// Remove url_base parsing and put current url base |
|
524
|
|
View Code Duplication |
if (preg_match('/define\(\'__SAMSON_BASE__\',\s*([^;]+)/i', $index_php, $matches)) { |
|
|
|
|
|
|
525
|
|
|
$index_php = str_replace($matches[0], 'define(\'__SAMSON_BASE__\',\'' . __SAMSON_BASE__ . '\');', $index_php); |
|
526
|
|
|
} |
|
527
|
|
|
|
|
528
|
|
|
// Set global constant to specify supported PHP version |
|
529
|
|
View Code Duplication |
if (preg_match('/define\s*\(\'__SAMSON_PHP_OLD[^;]+/', $index_php, $matches)) { |
|
|
|
|
|
|
530
|
|
|
$index_php = str_replace($matches[0], 'define(\'__SAMSON_PHP_OLD\',\'' . ($this->view_mode == 2) . '\');', $index_php); |
|
531
|
|
|
} |
|
532
|
|
|
|
|
533
|
|
|
$index_php = $this->removeBlankLines($index_php); |
|
534
|
|
|
$index_php = preg_replace('/(declare *\( *strict_types *= *1 *\) *;)/i', ' ', $index_php); |
|
535
|
|
|
|
|
536
|
|
|
// Запишем пусковой файл |
|
537
|
|
|
file_put_contents($this->output . 'index.php', '<?php ' . $index_php . "\n" . '?>'); |
|
538
|
|
|
|
|
539
|
|
|
// Minify PHP code if no debug is needed |
|
540
|
|
|
if (!$debug) { |
|
541
|
|
|
file_put_contents($this->output . 'index.php', php_strip_whitespace($this->output . 'index.php')); |
|
542
|
|
|
} |
|
543
|
|
|
|
|
544
|
|
|
elapsed('Site has been successfully compressed to ' . $this->output); |
|
545
|
|
|
} |
|
546
|
|
|
|
|
547
|
|
|
/** |
|
548
|
|
|
* Преобразовать коллекцию полученного кода в виде NS/Files в строку |
|
549
|
|
|
* с правильными NS |
|
550
|
|
|
* |
|
551
|
|
|
* @param array $code Коллекция кода полученная функцией @see compress_php() |
|
552
|
|
|
* @param boolean $no_ns Флаг убирания NS из кода |
|
553
|
|
|
* @return string Правильно собранный код в виде строки |
|
554
|
|
|
*/ |
|
555
|
|
|
public function code_array_to_str(array $code, $no_ns = false) |
|
|
|
|
|
|
556
|
|
|
{ |
|
557
|
|
|
// Соберем весь PHP код модуля |
|
558
|
|
|
$php_code = ''; |
|
559
|
|
|
foreach ($code as $ns => $files) { |
|
560
|
|
|
// If we support namespaces |
|
561
|
|
|
if (!$no_ns) $php_code .= "\n" . 'namespace ' . $ns . '{'; |
|
|
|
|
|
|
562
|
|
|
|
|
563
|
|
|
// Insert files code |
|
564
|
|
|
foreach ($files as $file => $php) { |
|
565
|
|
|
// Ignore uses array |
|
566
|
|
|
if ($file == 'uses') continue; |
|
|
|
|
|
|
567
|
|
|
// TODO: Add uses support class name changing |
|
568
|
|
|
|
|
569
|
|
|
// If we does not support namespaces |
|
570
|
|
|
if ($no_ns) { |
|
571
|
|
|
// Find all static class usage |
|
572
|
|
|
if (preg_match_all('/[\!\.\,\(\s\n\=\:]+\s*(?:self|parent|static|(?<classname>[\\\a-z_0-9]+))::/i', $php, $matches)) { |
|
573
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
574
|
|
|
} |
|
575
|
|
|
|
|
576
|
|
|
// Find all class definition |
|
577
|
|
|
if (preg_match_all('/(\n|\s)\s*class\s+(?<classname>[^\s]+)/i', $php, $matches)) { |
|
578
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
579
|
|
|
} |
|
580
|
|
|
|
|
581
|
|
|
// Find all instanceof definition |
|
582
|
|
|
if (preg_match_all('/\s+instanceof\s+(?<classname>[\\\a-z_0-9]+)/i', $php, $matches)) { |
|
583
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
584
|
|
|
} |
|
585
|
|
|
|
|
586
|
|
|
// Find all interface definition |
|
587
|
|
|
if (preg_match_all('/(\n|\s)\s*interface\s+(?<classname>[^\s]+)/i', $php, $matches)) { |
|
588
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
589
|
|
|
} |
|
590
|
|
|
|
|
591
|
|
|
// Find all class implements, class can implement many interfaces |
|
592
|
|
|
if (preg_match_all('/\s+implements\s+(?<classes>.*)/i', $php, $matches)) { |
|
593
|
|
|
$replace = $matches[0][0]; |
|
594
|
|
|
foreach (explode(',', $matches['classes'][0]) as $classname) { |
|
595
|
|
|
$replace = $this->transformClassName($classname, $classname, $replace, $ns); |
|
596
|
|
|
} |
|
597
|
|
|
|
|
598
|
|
|
$php = str_replace($matches[0][0], $replace, $php); |
|
599
|
|
|
} |
|
600
|
|
|
|
|
601
|
|
|
// Find all class extends |
|
602
|
|
|
if (preg_match_all('/\s+extends\s+(?<classname>[^\s]+)/i', $php, $matches)) { |
|
603
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
604
|
|
|
} |
|
605
|
|
|
|
|
606
|
|
|
// Find all class creation |
|
607
|
|
|
if (preg_match_all('/[\.\,\(\s\n=:]+\s*new\s+(?<classname>[^\(]+)\s*\(/i', $php, $matches)) { |
|
608
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
609
|
|
|
} |
|
610
|
|
|
|
|
611
|
|
|
// Find all class hints |
|
612
|
|
|
if (preg_match_all('/(\(|\,)\s*(?:array|(?<classname>[\\\a-z_0-9]+))\s*(\&|\$)/i', $php, $matches)) { |
|
613
|
|
|
$php = $this->changeClassName($matches, $php, $ns); |
|
614
|
|
|
} |
|
615
|
|
|
|
|
616
|
|
|
// Replace special word with its value |
|
617
|
|
|
$php = str_replace('__NAMESPACE__', '\'' . $ns . '\'', $php); |
|
618
|
|
|
} |
|
619
|
|
|
|
|
620
|
|
|
// Just concatenate file code |
|
621
|
|
|
$php_code .= $php; |
|
622
|
|
|
} |
|
623
|
|
|
|
|
624
|
|
|
// Close namespace if we support |
|
625
|
|
|
if (!$no_ns) $php_code .= "\n" . '}'; |
|
|
|
|
|
|
626
|
|
|
} |
|
627
|
|
|
|
|
628
|
|
|
return $php_code; |
|
629
|
|
|
} |
|
630
|
|
|
|
|
631
|
|
|
public function code_array_combine(array & $source, array & $target) |
|
|
|
|
|
|
632
|
|
|
{ |
|
633
|
|
|
foreach ($source as $ns => $files) { |
|
634
|
|
|
// Если в целевом массиве нет нужного NS - создадим |
|
635
|
|
|
if (!isset($target[$ns])) $target[$ns] = array(); |
|
|
|
|
|
|
636
|
|
|
|
|
637
|
|
|
// Запишем содержание NS/Files |
|
638
|
|
|
foreach ($files as $file => $php) { |
|
639
|
|
|
if (isset($target[$ns][$file]) && is_string($php)) $target[$ns][$file] .= $php; |
|
|
|
|
|
|
640
|
|
|
else if (isset($target[$ns][$file]) && is_array($php)) { |
|
641
|
|
|
$target[$ns][$file] = array_unique(array_merge($target[$ns][$file], $php)); |
|
642
|
|
|
} else $target[$ns][$file] = $php; |
|
|
|
|
|
|
643
|
|
|
} |
|
644
|
|
|
} |
|
645
|
|
|
} |
|
646
|
|
|
|
|
647
|
|
|
/** |
|
648
|
|
|
* Выполнить рекурсивное "собирание" файла |
|
649
|
|
|
* |
|
650
|
|
|
* @param string $path Абсолютный путь к файлу сайта |
|
651
|
|
|
* |
|
652
|
|
|
* @param null $module |
|
653
|
|
|
* @param array $code |
|
654
|
|
|
* @param string $namespace |
|
655
|
|
|
* |
|
656
|
|
|
* @return string |
|
657
|
|
|
*/ |
|
658
|
|
|
public function compress_php($path, $module = NULL, & $code = array(), $namespace = self::NS_GLOBAL) |
|
|
|
|
|
|
659
|
|
|
{ |
|
660
|
|
|
// TODO: Довести до ума разпознование require - убрать точку с зяпятоц которая остается |
|
661
|
|
|
// TODO: Убрать пустые линии |
|
662
|
|
|
// TODO: Анализатор использования функция и переменных?? |
|
663
|
|
|
|
|
664
|
|
|
//trace(' + Вошли в функцию:'.$path.'('.$namespace.')'); |
|
665
|
|
|
$_path = $path; |
|
666
|
|
|
$path = normalizepath(realpath($path)); |
|
667
|
|
|
|
|
668
|
|
|
// Если мы уже подключили данный файл или он не существует |
|
669
|
|
View Code Duplication |
if (isset($this->files[$path])) return $this->log(' ! Файл: [##], already compressed', $path); |
|
|
|
|
|
|
670
|
|
|
else if (!is_file($path)) return $this->log(' ! Файл: [##], не существует', $_path); |
|
|
|
|
|
|
671
|
|
|
else if (strpos($path, 'vendor/autoload.php') !== false) return $this->log(' Ignoring composer autoloader [##]', $path); |
|
|
|
|
|
|
672
|
|
|
else if (in_array(basename($path), $this->ignoredFiles)) { |
|
673
|
|
|
return $this->log(' Ignoring file[##] by configuration', $path); |
|
674
|
|
|
} |
|
675
|
|
|
|
|
676
|
|
|
|
|
677
|
|
|
$this->log(' -- Compressing file [##]', $path); |
|
678
|
|
|
|
|
679
|
|
|
// Load file |
|
680
|
|
|
require_once($path); |
|
681
|
|
|
|
|
682
|
|
|
//trace('Чтение файла: '.$path ); |
|
683
|
|
|
|
|
684
|
|
|
// Сохраним файл |
|
685
|
|
|
$this->files[$path] = $path; |
|
686
|
|
|
|
|
687
|
|
|
// Относительный путь к файлу |
|
688
|
|
|
if (isset($rel_path)) $this->files[$rel_path] = $path; |
|
|
|
|
|
|
689
|
|
|
|
|
690
|
|
|
// Прочитаем php файл |
|
691
|
|
|
$fileStr = file_get_contents($path); |
|
692
|
|
|
|
|
693
|
|
|
// Если в файле нет namespace - считаем его глобальным |
|
694
|
|
|
if (strpos($fileStr, 'namespace') === false) |
|
|
|
|
|
|
695
|
|
|
|
|
696
|
|
|
//$file_dir = ''; |
|
697
|
|
|
// Вырежим путь к файлу |
|
698
|
|
|
//$file_dir = (pathinfo( $path, PATHINFO_DIRNAME ) == '.' ? '' : pathinfo( $path, PATHINFO_DIRNAME ).'/'); |
|
699
|
|
|
|
|
700
|
|
|
// Сюда соберем код программы |
|
701
|
|
|
$main_code = ''; |
|
|
|
|
|
|
702
|
|
|
$main_code = "\n" . '// Модуль: ' . m($module)->id() . ', файл: ' . $path . "\n"; |
|
|
|
|
|
|
703
|
|
|
|
|
704
|
|
|
// Создадим уникальную коллекцию алиасов для NS |
|
705
|
|
|
if (!isset($code[$namespace]['uses'])) $code[$namespace]['uses'] = array(); |
|
|
|
|
|
|
706
|
|
|
|
|
707
|
|
|
// Установим ссылку на коллекцию алиасов |
|
708
|
|
|
$uses = &$code[$namespace]['uses']; |
|
709
|
|
|
|
|
710
|
|
|
// Local file uses collection |
|
711
|
|
|
$file_uses = array(); |
|
712
|
|
|
|
|
713
|
|
|
// Получим константы документа |
|
714
|
|
|
$consts = get_defined_constants(); |
|
715
|
|
|
|
|
716
|
|
|
// Маркеры для отрезания специальных блоков которые не нужны в PRODUCTION |
|
717
|
|
|
$rmarker_st = '\/\/\[PHPCOMPRESSOR\(remove\,start\)\]'; |
|
718
|
|
|
$rmarker_en = '\/\/\[PHPCOMPRESSOR\(remove\,end\)\]'; |
|
719
|
|
|
|
|
720
|
|
|
// Найдем все "ненужные" блоки кода и уберем их |
|
721
|
|
|
$fileStr = preg_replace('/' . $rmarker_st . '.*?' . $rmarker_en . '/uis', '', $fileStr); |
|
722
|
|
|
|
|
723
|
|
|
$className = ''; |
|
724
|
|
|
$classConstList = array(); |
|
725
|
|
|
|
|
726
|
|
|
//TODO: Fix to normal external dependency with ResourceRouter |
|
727
|
|
|
$fileStr = preg_replace_callback('/(\\\\samson\\\\resourcer\\\\)?ResourceRouter::url\((\'|\")(?<path>[^,)]+)(\'|\")(,(?<module>[^)]+))?\);/i', array($this, 'rewriteResourceRouter'), $fileStr); |
|
728
|
|
|
|
|
729
|
|
|
/** @var bool $classStared Flag for matching trait uses */ |
|
730
|
|
|
$classStared = false; |
|
731
|
|
|
|
|
732
|
|
|
// Разберем код программы |
|
733
|
|
|
$tokens = token_get_all($fileStr); |
|
734
|
|
|
for ($i = 0; $i < sizeof($tokens); $i++) { |
|
|
|
|
|
|
735
|
|
|
// Получим следующий жетон из кода программы |
|
736
|
|
|
$token = $tokens[$i]; |
|
737
|
|
|
|
|
738
|
|
|
// Если просто строка |
|
739
|
|
|
if (is_string($token)) $main_code .= $token; |
|
|
|
|
|
|
740
|
|
|
// Если это специальный жетон |
|
741
|
|
|
else { |
|
742
|
|
|
// token array |
|
743
|
|
|
list($id, $text) = $token; |
|
744
|
|
|
|
|
745
|
|
|
// Перебирем тип комманды |
|
746
|
|
|
switch ($id) { |
|
747
|
|
|
case T_COMMENT: // Пропускаем все комментарии |
|
748
|
|
|
case T_DOC_COMMENT: |
|
749
|
|
|
case T_CLOSE_TAG: // Начало,конец файла |
|
750
|
|
|
case T_OPEN_TAG: |
|
751
|
|
|
break; |
|
752
|
|
|
|
|
753
|
|
|
case T_WHITESPACE: |
|
754
|
|
|
$main_code .= $text; /*$main_code .= ' ';*/ |
|
755
|
|
|
break; |
|
756
|
|
|
|
|
757
|
|
|
// Обработаем алиасы |
|
758
|
|
|
case T_USE: |
|
759
|
|
|
$_use = ''; |
|
760
|
|
|
|
|
761
|
|
|
// Переберем все что иде после комманды алиаса |
|
762
|
|
|
for ($j = $i + 1; $j < sizeof($tokens); $j++) { |
|
|
|
|
|
|
763
|
|
|
// Получим идентификатор метки и текстовое представление |
|
764
|
|
|
$id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; |
|
765
|
|
|
$text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; |
|
766
|
|
|
|
|
767
|
|
|
//trace('"'.$id.'" - "'.$text.'"'); |
|
768
|
|
|
|
|
769
|
|
|
// Если use используется в функции |
|
770
|
|
|
if ($id == '(') { |
|
771
|
|
|
$j--; |
|
772
|
|
|
break; |
|
773
|
|
|
} |
|
774
|
|
|
|
|
775
|
|
|
// Если это закрывающая скобка - прекратим собирание пути к файлу |
|
776
|
|
|
if ($id == ';') break; |
|
|
|
|
|
|
777
|
|
|
|
|
778
|
|
|
// Все пробелы игнорирую |
|
779
|
|
|
if ($id == T_WHITESPACE) continue; |
|
|
|
|
|
|
780
|
|
|
|
|
781
|
|
|
// Если у метки есть текстовое представление |
|
782
|
|
|
if (isset($text)) { |
|
783
|
|
|
// Если єто константа |
|
784
|
|
|
if (isset($consts[$text])) $_use .= $consts[$text]; |
|
|
|
|
|
|
785
|
|
|
// Если это путь |
|
786
|
|
|
else $_use .= $text; |
|
|
|
|
|
|
787
|
|
|
} |
|
788
|
|
|
} |
|
789
|
|
|
|
|
790
|
|
|
// Если это не use в inline функции - добавим алиас в коллекцию |
|
791
|
|
|
// для данного ns с проверкой на уникальность |
|
792
|
|
|
if ($id !== '(') { |
|
793
|
|
|
// If this tait use |
|
794
|
|
|
if ($classStared) { |
|
795
|
|
|
// Consider rewriting trait usage fully qualified name |
|
796
|
|
|
//TODO: Not fully qualified trait name adds slash before |
|
797
|
|
|
$_use = strpos($_use, '\\') === false |
|
798
|
|
|
? '\\' . $namespace . '\\' . $_use |
|
799
|
|
|
: $_use; |
|
800
|
|
|
|
|
801
|
|
|
// TODO: Import trait code |
|
802
|
|
|
if (!trait_exists($_use)) { |
|
803
|
|
|
throw new \Exception('Trait "' . $_use . '" does not exists in "' . $path . '"'); |
|
804
|
|
|
} else { |
|
805
|
|
|
$main_code .= ' use ' . $_use . ';'; |
|
806
|
|
|
} |
|
807
|
|
|
|
|
|
|
|
|
|
808
|
|
|
} else { |
|
809
|
|
|
// Преведем все use к одному виду |
|
810
|
|
|
if ($_use{0} !== '\\') { |
|
811
|
|
|
$_use = '\\' . $_use; |
|
812
|
|
|
} |
|
813
|
|
|
|
|
814
|
|
|
// Add local file uses |
|
815
|
|
|
$file_uses[] = $_use; |
|
816
|
|
|
|
|
817
|
|
|
// TODO: Вывести замечание что бы код везде был одинаковый |
|
818
|
|
|
if (!in_array($_use, $uses)) { |
|
819
|
|
|
$uses[] = $_use; |
|
820
|
|
|
} |
|
821
|
|
|
} |
|
822
|
|
|
} else { |
|
823
|
|
|
$main_code .= ' use '; |
|
824
|
|
|
} |
|
825
|
|
|
|
|
826
|
|
|
// Сместим указатель чтения файла |
|
827
|
|
|
$i = $j; |
|
828
|
|
|
|
|
829
|
|
|
break; |
|
830
|
|
|
|
|
831
|
|
View Code Duplication |
case T_NAMESPACE: |
|
|
|
|
|
|
832
|
|
|
|
|
833
|
|
|
// Определим временное пространство имен |
|
834
|
|
|
$_namespace = ''; |
|
835
|
|
|
|
|
836
|
|
|
// Переберем все что иде после комманды подключения файла |
|
837
|
|
|
for ($j = $i + 1; $j < sizeof($tokens); $j++) { |
|
|
|
|
|
|
838
|
|
|
// Получим идентификатор метки и текстовое представление |
|
839
|
|
|
$id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; |
|
840
|
|
|
$text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; |
|
841
|
|
|
|
|
842
|
|
|
//trace('"'.$id.'" - "'.$text.'"'); |
|
843
|
|
|
|
|
844
|
|
|
// Если это закрывающая скобка - прекратим собирание пути к файлу |
|
845
|
|
|
if ($id == ')' || $id == ';' || $id == '{') break; |
|
|
|
|
|
|
846
|
|
|
|
|
847
|
|
|
// Все пробелы игнорирую |
|
848
|
|
|
if ($id == T_WHITESPACE) continue; |
|
|
|
|
|
|
849
|
|
|
|
|
850
|
|
|
// Если у метки есть текстовое представление |
|
851
|
|
|
if (isset($text)) { |
|
852
|
|
|
// Если єто константа |
|
853
|
|
|
if (isset($consts[$text])) $_namespace .= $consts[$text]; |
|
|
|
|
|
|
854
|
|
|
// Если это путь |
|
855
|
|
|
else $_namespace .= $text; |
|
|
|
|
|
|
856
|
|
|
} |
|
857
|
|
|
} |
|
858
|
|
|
|
|
859
|
|
|
// Если найденный NS отличается от текущего - установим переход к новому NS |
|
860
|
|
|
if ($namespace !== $_namespace) { |
|
861
|
|
|
// Сохраним новый как текущий |
|
862
|
|
|
$namespace = strtolower($_namespace); |
|
863
|
|
|
|
|
864
|
|
|
//trace(' #'.$i.' -> Изменили NS с '.$namespace.' на '.$_namespace); |
|
865
|
|
|
|
|
866
|
|
|
// Если мы еще не создали данный NS |
|
867
|
|
|
if (!isset($code[$namespace])) $code[$namespace] = array(); |
|
|
|
|
|
|
868
|
|
|
// Создадим уникальную коллекцию алиасов для NS |
|
869
|
|
|
if (!isset($code[$namespace]['uses'])) $code[$namespace]['uses'] = array(); |
|
|
|
|
|
|
870
|
|
|
// Установим ссылку на коллекцию алиасов |
|
871
|
|
|
$uses = &$code[$namespace]['uses']; |
|
872
|
|
|
} |
|
873
|
|
|
|
|
874
|
|
|
// Сместим указатель чтения файла |
|
875
|
|
|
$i = $j; |
|
876
|
|
|
|
|
877
|
|
|
break; |
|
878
|
|
|
|
|
879
|
|
|
// Выделяем код подключаемых файлов |
|
880
|
|
|
case T_REQUIRE : |
|
|
|
|
|
|
881
|
|
|
case T_REQUIRE_ONCE : |
|
|
|
|
|
|
882
|
|
|
//case T_INCLUDE : |
|
883
|
|
View Code Duplication |
case T_INCLUDE_ONCE: { |
|
|
|
|
|
|
884
|
|
|
// Получим путь к подключаемому файлу |
|
885
|
|
|
$file_path = ''; |
|
886
|
|
|
|
|
887
|
|
|
// Переберем все что иде после комманды подключения файла |
|
888
|
|
|
for ($j = $i + 1; $j < sizeof($tokens); $j++) { |
|
|
|
|
|
|
889
|
|
|
// Получим идентификатор метки и текстовое представление |
|
890
|
|
|
$id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; |
|
891
|
|
|
$text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; |
|
892
|
|
|
|
|
893
|
|
|
//trace('"'.$id.'" - "'.$text.'"'); |
|
894
|
|
|
|
|
895
|
|
|
// Если это закрывающая скобка - прекратим собирание пути к файлу |
|
896
|
|
|
if ($id == ';') break; |
|
|
|
|
|
|
897
|
|
|
|
|
898
|
|
|
// Все пробелы игнорирую |
|
899
|
|
|
if ($id == T_WHITESPACE) continue; |
|
|
|
|
|
|
900
|
|
|
|
|
901
|
|
|
// Если у метки есть текстовое представление |
|
902
|
|
|
if (isset($text)) { |
|
903
|
|
|
// Если єто константа |
|
904
|
|
|
if (isset($consts[$text])) $file_path .= $consts[$text]; |
|
|
|
|
|
|
905
|
|
|
// Если это путь |
|
906
|
|
|
else $file_path .= $text; |
|
|
|
|
|
|
907
|
|
|
} |
|
908
|
|
|
} |
|
909
|
|
|
|
|
910
|
|
|
// Если указан путь к файлу |
|
911
|
|
|
if (isset($file_path{1})) { |
|
912
|
|
|
// Уберем ковычки |
|
913
|
|
|
$file_path = str_replace(array("'", '"'), array('', ''), $file_path); |
|
914
|
|
|
|
|
915
|
|
|
// Если это не абсолютный путь - попробуем относительный |
|
916
|
|
|
if (!file_exists($file_path)) $file_path = pathname($path) . $file_path; |
|
|
|
|
|
|
917
|
|
|
|
|
918
|
|
|
// Если файл найден - получим его содержимое |
|
919
|
|
|
if (file_exists($file_path)) { |
|
920
|
|
|
//trace('Углубляемся в файл:'.$file_path.'('.$namespace.')'); |
|
921
|
|
|
|
|
922
|
|
|
// Углубимся в рекурсию |
|
923
|
|
|
$this->compress_php($file_path, $module, $code, $namespace); |
|
924
|
|
|
|
|
925
|
|
|
// Измением позицию маркера чтения файла |
|
926
|
|
|
$i = $j + 1; |
|
927
|
|
|
} |
|
928
|
|
|
} else { |
|
929
|
|
|
$main_code .= $text; |
|
930
|
|
|
} |
|
931
|
|
|
|
|
932
|
|
|
} |
|
933
|
|
|
break; |
|
934
|
|
|
|
|
935
|
|
|
case T_INTERFACE: |
|
936
|
|
|
case T_CLASS: |
|
937
|
|
|
$classStared = true; |
|
938
|
|
|
$main_code .= $text; |
|
939
|
|
|
for ($j = $i + 1; $j < sizeof($tokens); $j++) { |
|
|
|
|
|
|
940
|
|
|
// Get id and text of token |
|
941
|
|
|
$id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; |
|
942
|
|
|
$text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; |
|
943
|
|
|
|
|
944
|
|
|
// Ignore all whitespace |
|
945
|
|
|
if ($id == T_WHITESPACE) continue; |
|
|
|
|
|
|
946
|
|
|
|
|
947
|
|
|
if (isset($text)) { |
|
948
|
|
|
$className = $text; |
|
949
|
|
|
break; |
|
950
|
|
|
} |
|
951
|
|
|
} |
|
952
|
|
|
|
|
953
|
|
|
break; |
|
954
|
|
|
|
|
955
|
|
|
case T_CONST: |
|
956
|
|
|
$main_code .= $text; |
|
957
|
|
|
$classConst = array(); |
|
958
|
|
|
$nameFlag = 'name'; |
|
959
|
|
|
for ($j = $i + 1; $j < sizeof($tokens); $j++) { |
|
|
|
|
|
|
960
|
|
|
// Get id and text of token |
|
961
|
|
|
$id = isset($tokens[$j][0]) ? $tokens[$j][0] : ''; |
|
962
|
|
|
$text = isset($tokens[$j][1]) ? $tokens[$j][1] : ''; |
|
963
|
|
|
if ($id == ';') break; |
|
|
|
|
|
|
964
|
|
|
|
|
965
|
|
|
// Ignore all whitespace |
|
966
|
|
|
if ($id == T_WHITESPACE) continue; |
|
|
|
|
|
|
967
|
|
|
|
|
968
|
|
|
if ($id == '=') { |
|
969
|
|
|
$nameFlag = 'value'; |
|
970
|
|
|
continue; |
|
971
|
|
|
} |
|
972
|
|
|
|
|
973
|
|
|
if (isset($text)) { |
|
974
|
|
|
// Is it defined constant |
|
975
|
|
|
if (isset($consts[$text])) $classConst[$nameFlag] = $consts[$text]; |
|
|
|
|
|
|
976
|
|
|
else $classConst[$nameFlag] = $text; |
|
|
|
|
|
|
977
|
|
|
} |
|
978
|
|
|
} |
|
979
|
|
|
$classConstList[$classConst['name']] = $classConst['value']; |
|
980
|
|
|
|
|
981
|
|
|
break; |
|
982
|
|
|
|
|
983
|
|
|
// Собираем основной код программы |
|
984
|
|
|
default: |
|
985
|
|
|
$main_code .= $text; |
|
986
|
|
|
break; |
|
987
|
|
|
} |
|
988
|
|
|
} |
|
989
|
|
|
} |
|
990
|
|
|
|
|
991
|
|
|
|
|
992
|
|
|
// Replace all class shortcut usage with full name |
|
993
|
|
|
if (count($file_uses)) { |
|
994
|
|
|
$main_code = $this->removeUSEStatement($main_code, $file_uses); |
|
995
|
|
|
} |
|
996
|
|
|
|
|
997
|
|
|
$matches = array(); |
|
998
|
|
|
if ($className == 'Module') { |
|
999
|
|
|
$temp = ''; |
|
|
|
|
|
|
1000
|
|
|
} |
|
1001
|
|
|
|
|
1002
|
|
|
if (preg_match_all('/(?<start>[(=+-\/*%., \n\t])(?<class>[\\\\a-zA-Z_]+)::(?<name>[a-zA-Z_]+)(?<end>[):;=+-\/*%., \n\t])/i', $main_code, $matches)) { |
|
1003
|
|
|
for ($i = 0; $i < sizeof($matches['name']); $i++) { |
|
|
|
|
|
|
1004
|
|
|
$matchClass = $matches['class'][$i]; |
|
1005
|
|
|
// If this is self - use current file class |
|
1006
|
|
|
if ($matches['class'][$i] === 'self') { |
|
1007
|
|
|
$constantName = $namespace . '\\' . $className; |
|
1008
|
|
|
} elseif ($matches['class'][$i] == $className) { |
|
1009
|
|
|
// If this is current class add namespace |
|
1010
|
|
|
$constantName = $namespace . '\\' . $className; |
|
1011
|
|
|
} elseif ($matches['class'][$i] === 'parent') { |
|
1012
|
|
|
continue; |
|
1013
|
|
|
} elseif ($matches['class'][$i] === 'static') { |
|
1014
|
|
|
continue; |
|
1015
|
|
|
} else { |
|
1016
|
|
|
$constantName = $matches['class'][$i]; |
|
1017
|
|
|
} |
|
1018
|
|
|
|
|
1019
|
|
|
// If constant has no namespace - use current |
|
1020
|
|
|
if (strpos($constantName, '\\') === false) { |
|
1021
|
|
|
$constantName = $namespace . '\\' . $constantName; |
|
1022
|
|
|
} |
|
1023
|
|
|
|
|
1024
|
|
|
// Add constant name |
|
1025
|
|
|
$constantName .= '::' . $matches['name'][$i]; |
|
1026
|
|
|
|
|
1027
|
|
|
$replaceName = $matches['start'][$i] . $matchClass . '::' . $matches['name'][$i] . $matches['end'][$i]; |
|
1028
|
|
|
|
|
1029
|
|
|
// Check if we have this constant defined |
|
1030
|
|
|
if (defined($constantName)) { |
|
1031
|
|
|
// Get constant value |
|
1032
|
|
|
$value = constant($constantName); |
|
1033
|
|
|
// Fix slashes, add quotes for string |
|
1034
|
|
|
$value = is_string($value) ? str_replace('\\', '\\\\\\\\', "'" . $value . "'") : $value; |
|
1035
|
|
|
$replacer = str_replace('\\', '\\\\', $replaceName); |
|
1036
|
|
|
$replacer = str_replace(array(')','('), array('\)', '\('), $replacer); |
|
1037
|
|
|
// Replace constant call in the code |
|
1038
|
|
|
$main_code = preg_replace( |
|
1039
|
|
|
'/' . $replacer . '/i', //([;=+-\/*%., ]) |
|
1040
|
|
|
$matches['start'][$i] . $value . $matches['end'][$i], |
|
1041
|
|
|
$main_code |
|
1042
|
|
|
); |
|
1043
|
|
|
} |
|
1044
|
|
|
} |
|
1045
|
|
|
} |
|
1046
|
|
|
// Запишем в коллекцию кода полученный код |
|
1047
|
|
|
$code[$namespace][$path] = $main_code; |
|
1048
|
|
|
|
|
1049
|
|
|
return $main_code; |
|
1050
|
|
|
} |
|
1051
|
|
|
|
|
1052
|
|
|
/** |
|
1053
|
|
|
* Transform class name with namespace to PHP 5.2 format |
|
1054
|
|
|
* @param $source |
|
1055
|
|
|
* @param $className |
|
1056
|
|
|
* @param $php |
|
1057
|
|
|
* @param $ns |
|
1058
|
|
|
* |
|
1059
|
|
|
* @return mixed |
|
1060
|
|
|
*/ |
|
1061
|
|
|
private function transformClassName($source, $className, $php, $ns) |
|
1062
|
|
|
{ |
|
1063
|
|
|
// Create copy |
|
1064
|
|
|
$nClassName = trim($className); |
|
1065
|
|
|
|
|
1066
|
|
|
// If this class uses other namespace or in global namespace |
|
1067
|
|
|
if (strrpos($nClassName, '\\') > 0) { |
|
1068
|
|
|
// If this is full class name |
|
1069
|
|
|
if ($nClassName{0} == '\\') { |
|
1070
|
|
|
// Remove global name space character from beginning |
|
1071
|
|
|
$nClassName = substr($nClassName, 1); |
|
1072
|
|
|
} |
|
1073
|
|
|
|
|
1074
|
|
|
// Transform namespace |
|
1075
|
|
|
$nClassName = str_replace('\\', '_', $nClassName); |
|
1076
|
|
|
|
|
|
|
|
|
|
1077
|
|
|
} else if ($nClassName{0} == '\\') { // This is global namespace class |
|
1078
|
|
|
// Remove first character "\" |
|
1079
|
|
|
$nClassName = substr($nClassName, 1); |
|
1080
|
|
|
|
|
|
|
|
|
|
1081
|
|
|
} else { // No name space in class name |
|
1082
|
|
|
// Create old-styled namespace format |
|
1083
|
|
|
$nClassName = str_replace('\\', '_', $ns) . '_' . $nClassName; |
|
1084
|
|
|
} |
|
1085
|
|
|
|
|
1086
|
|
|
// Replace class name in source |
|
1087
|
|
|
$replace = str_replace($className, $nClassName, $source); |
|
1088
|
|
|
|
|
1089
|
|
|
if (strpos($source, 'm(')) { |
|
1090
|
|
|
//trace($source, true); |
|
1091
|
|
|
} |
|
1092
|
|
|
|
|
1093
|
|
|
// Replace code |
|
1094
|
|
|
$php = str_ireplace($source, $replace, $php); |
|
1095
|
|
|
|
|
1096
|
|
|
//trace('Changing class name('.$ns.')"'.htmlentities(trim($className)).'" with "'.htmlentities(trim($nClassName)).'"'); |
|
1097
|
|
|
//trace('Replacing "'.htmlentities(trim($source)).'" with "'.htmlentities(trim($replace)).'"'); |
|
1098
|
|
|
|
|
1099
|
|
|
return $php; |
|
1100
|
|
|
} |
|
1101
|
|
|
|
|
1102
|
|
|
/** |
|
1103
|
|
|
* Remove blank lines from code |
|
1104
|
|
|
* http://stackoverflow.com/questions/709669/how-do-i-remove-blank-lines-from-text-in-php |
|
1105
|
|
|
* @param string $code Code for removing blank lines |
|
1106
|
|
|
* @return string Modified code |
|
1107
|
|
|
*/ |
|
1108
|
|
|
protected function removeBlankLines($code) |
|
1109
|
|
|
{ |
|
1110
|
|
|
// New line is required to split non-blank lines |
|
1111
|
|
|
return preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $code); |
|
1112
|
|
|
} |
|
1113
|
|
|
|
|
1114
|
|
|
/** Change class name to old format without namespace */ |
|
1115
|
|
|
private function changeClassName($matches, $php, $ns, $uses = array()) |
|
1116
|
|
|
{ |
|
1117
|
|
|
// Iterate all class name usage matches |
|
1118
|
|
|
for ($i = 0; $i < sizeof($matches[0]); $i++) { |
|
|
|
|
|
|
1119
|
|
|
// Get source matching string |
|
1120
|
|
|
$source = $matches[0][$i]; |
|
1121
|
|
|
|
|
1122
|
|
|
// Get found classname |
|
1123
|
|
|
$className = &$matches['classname'][$i]; |
|
1124
|
|
|
|
|
1125
|
|
|
// If class name found or this is variable |
|
1126
|
|
|
if (!isset($className) || !isset($className{0}) || strpos($className, '$') !== false) { |
|
1127
|
|
|
continue; |
|
1128
|
|
|
} |
|
1129
|
|
|
|
|
1130
|
|
|
// Transform class name |
|
1131
|
|
|
$php = $this->transformClassName($source, $className, $php, $ns, $uses); |
|
|
|
|
|
|
1132
|
|
|
} |
|
1133
|
|
|
|
|
1134
|
|
|
return $php; |
|
1135
|
|
|
} |
|
1136
|
|
|
|
|
1137
|
|
|
/** |
|
1138
|
|
|
* Copy resources |
|
1139
|
|
|
*/ |
|
1140
|
|
|
private function copy_path_resources($path_resources, $module_path, $module_output_path) |
|
|
|
|
|
|
1141
|
|
|
{ |
|
1142
|
|
|
$this->log(' -> Copying resources from [##] to [##]', $module_path, $module_output_path); |
|
1143
|
|
|
|
|
1144
|
|
|
// Iterate module resources |
|
1145
|
|
|
foreach ($path_resources as $extension => $resources) { |
|
1146
|
|
|
foreach ($resources as $resource) { |
|
1147
|
|
|
// Build relative module resource path |
|
1148
|
|
|
$relative_path = str_replace($module_path, '', $resource); |
|
1149
|
|
|
|
|
1150
|
|
|
$this->resourceManager->compress($resource, $this->output . $module_output_path . $relative_path); |
|
1151
|
|
|
} |
|
1152
|
|
|
} |
|
1153
|
|
|
} |
|
1154
|
|
|
|
|
1155
|
|
|
/** |
|
1156
|
|
|
* Remove all USE statements and replace class shortcuts to full class names |
|
1157
|
|
|
* |
|
1158
|
|
|
* @param string $code Code to work with |
|
1159
|
|
|
* @param array $classes Array of class names to replace |
|
1160
|
|
|
* |
|
1161
|
|
|
* @return bool|mixed|string |
|
1162
|
|
|
*/ |
|
1163
|
|
|
private function removeUSEStatement($code, array $classes) |
|
1164
|
|
|
{ |
|
1165
|
|
|
// Iterate found use statements |
|
1166
|
|
|
foreach (array_unique($classes) as $full_class) { |
|
1167
|
|
|
// Ignore trait uses |
|
1168
|
|
|
if (trait_exists($full_class)) { |
|
1169
|
|
|
continue; |
|
1170
|
|
|
} |
|
1171
|
|
|
|
|
1172
|
|
|
// Get class shortcut |
|
1173
|
|
|
$class_name = \samson\core\AutoLoader::getOnlyClass($full_class); |
|
1174
|
|
|
|
|
1175
|
|
|
// Check class existance |
|
1176
|
|
|
if (!class_exists($full_class) && !interface_exists($full_class)) { |
|
1177
|
|
|
//return e('Found USE statement for undeclared class ##', E_SAMSON_FATAL_ERROR, $full_class); |
|
1178
|
|
|
continue; |
|
1179
|
|
|
} |
|
1180
|
|
|
|
|
1181
|
|
|
// Replace class static call |
|
1182
|
|
|
$code = preg_replace('/([^\\\a-z])' . $class_name . '::/i', '$1' . $full_class . '::', $code); |
|
1183
|
|
|
|
|
1184
|
|
|
// Replace class implements calls |
|
1185
|
|
|
$code = preg_replace('/\s+implements(.*\W)' . $class_name . '([^\\\])/i', ' implements $1' . $full_class . '$2 ', $code); |
|
1186
|
|
|
|
|
1187
|
|
|
// Handle instanceof operator |
|
1188
|
|
|
$code = preg_replace('/instanceof\s+' . $class_name . '/i', 'instanceof ' . $full_class . '', $code); |
|
1189
|
|
|
|
|
1190
|
|
|
// Replace class extends calls |
|
1191
|
|
|
$code = preg_replace('/extends\s+' . $class_name . '/i', 'extends ' . $full_class . '', $code); |
|
1192
|
|
|
|
|
1193
|
|
|
// Replace multiple class extends calls |
|
1194
|
|
|
$code = preg_replace('/\s+extends(.*\W),?\s' . $class_name . '([^\\\])/i', ' extends $1' . $full_class . '$2 ', $code); |
|
1195
|
|
|
|
|
1196
|
|
|
// Replace class hint calls |
|
1197
|
|
|
$code = preg_replace('/(\(|\s|\,)\s*' . $class_name . '\s*(&|$)/i', '$1' . $full_class . ' $2', $code); |
|
1198
|
|
|
|
|
1199
|
|
|
// Replace class creation call |
|
1200
|
|
|
$code = preg_replace('/new\s+' . $class_name . '\s*\(/i', 'new ' . $full_class . '(', $code); |
|
1201
|
|
|
|
|
1202
|
|
|
// Replace annotations |
|
1203
|
|
|
$code = preg_replace('/([, (])' . $class_name . '\s\$/i', '$1 $2' . $full_class . ' $', $code); |
|
1204
|
|
|
} |
|
1205
|
|
|
|
|
1206
|
|
|
return $code; |
|
1207
|
|
|
} |
|
1208
|
|
|
|
|
1209
|
|
|
/** |
|
1210
|
|
|
* Преобразовать коллекцию имен классов в коллекцию |
|
1211
|
|
|
* [Namespace][ ClassFileName ] |
|
1212
|
|
|
* |
|
1213
|
|
|
* @param array $collection Коллекция имен классов |
|
1214
|
|
|
* @param array $classes Коллекция для возврата результатов |
|
1215
|
|
|
*/ |
|
1216
|
|
|
private function classes_to_ns_files($collection, & $classes = array()) |
|
|
|
|
|
|
1217
|
|
|
{ |
|
1218
|
|
|
// Соберем коллекцию загруженных интерфейсов их файлов по пространствам имен |
|
1219
|
|
|
foreach ($collection as $class) { |
|
1220
|
|
|
$ac = new \ReflectionClass($class); |
|
1221
|
|
|
|
|
1222
|
|
|
$ns = $ac->getNamespaceName(); |
|
1223
|
|
|
|
|
1224
|
|
|
if ($ns != '') { |
|
1225
|
|
|
$ns = strtolower($ns); |
|
1226
|
|
|
|
|
1227
|
|
|
if (!isset($classes[$ns])) { |
|
1228
|
|
|
$classes[$ns] = array(); |
|
1229
|
|
|
} |
|
1230
|
|
|
|
|
1231
|
|
|
$classes[$ns][normalizepath($ac->getFileName())] = ''; |
|
1232
|
|
|
} |
|
1233
|
|
|
} |
|
1234
|
|
|
} |
|
1235
|
|
|
} |
|
1236
|
|
|
|
Let’s assume that you have a directory layout like this:
. |-- OtherDir | |-- Bar.php | `-- Foo.php `-- SomeDir `-- Foo.phpand let’s assume the following content of
Bar.php:If both files
OtherDir/Foo.phpandSomeDir/Foo.phpare 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.phpHowever, as
OtherDir/Foo.phpdoes not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/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: