|
1 <?php |
|
2 |
|
3 /* |
|
4 * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between |
|
5 * Version 1.0 (Banshee) |
|
6 * Copyright (C) 2006-2007 Dan Fuhry |
|
7 * Javascript compression library - used to compact the client-side Javascript code (all 72KB of it!) to save some bandwidth |
|
8 * |
|
9 * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License |
|
10 * as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
|
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
|
14 * |
|
15 * This class was written by Andrea Giammarchi and was downloaded from PHPClasses.org. The information page stated that |
|
16 * this class is licensed under the GNU General Public License, the terms of which can be found by reading the file |
|
17 * "GPL" included with the Enano package. |
|
18 */ |
|
19 |
|
20 /** |
|
21 * JavaScriptCompressor class, |
|
22 * removes comments or pack JavaScript source[s] code. |
|
23 * ______________________________________________________________ |
|
24 * JavaScriptCompressor (just 2 public methods) |
|
25 * | |
|
26 * |________ getClean(jsSource:mixed):string |
|
27 * | returns one or more JavaScript code without comments, |
|
28 * | by default removes some spaces too |
|
29 * | |
|
30 * |________ getPacked(jsSource:mixed):string |
|
31 * returns one or more JavaScript code packed, |
|
32 * using getClean and obfuscating output |
|
33 * -------------------------------------------------------------- |
|
34 * Note about $jsSource input varible: |
|
35 * this var should be a string (i.e. $jsSource = file_get_contents("myFile.js");) |
|
36 * should be an array of strings (i.e. array(file_get_contents("1.js"), file_get_contents("2.js"), ... )) |
|
37 * should be an array with 1 or 2 keys: |
|
38 * (i.e. array('code'=>file_get_contents("mySource.js"))) |
|
39 * (i.e. array('code'=>file_get_contents("mySource.js"), 'name'=>'mySource')) |
|
40 * ... and should be an array of arrays created with theese rules |
|
41 * array( |
|
42 * file_get_contents("secret.js"), |
|
43 * array('code'=>$anotherJS), |
|
44 * array('code'=>$myJSapplication, 'name'=>'JSApplication V 1.0') |
|
45 * ) |
|
46 * |
|
47 * The name used on dedicated key, will be write on parsed source header |
|
48 * -------------------------------------------------------------- |
|
49 * Note about returned strings: |
|
50 * Your browser should wrap very long strings, then don't use |
|
51 * cut and paste from your browser, save output into your database or directly |
|
52 * in a file or print them only inside <script> and </script> tags |
|
53 * -------------------------------------------------------------- |
|
54 * Note about parser performance: |
|
55 * With pure PHP embed code this class should be slow and not really safe |
|
56 * for your server performance then don't parse JavaScript runtime for each |
|
57 * file you need and create some "parsed" caching system |
|
58 * (at least while i've not created a compiled version of theese class functions). |
|
59 * Here there's a caching system example: http://www.phpclasses.org/browse/package/3158.html |
|
60 * -------------------------------------------------------------- |
|
61 * Note about JavaScript packed compatibility: |
|
62 * To be sure about compatibility include before every script JSL Library: |
|
63 * http://www.devpro.it/JSL/ |
|
64 * JSL library add some features for old or buggy browsers, one of |
|
65 * those functions is String.replace with function as second argument, |
|
66 * used by JavaScript generated packed code to rebuild original code. |
|
67 * |
|
68 * Remember that KDE 3.5, Safari and IE5 will not work correctly with packed version |
|
69 * if you'll not include JSL. |
|
70 * -------------------------------------------------------------- |
|
71 * @Compatibility >= PHP 4 |
|
72 * @Author Andrea Giammarchi |
|
73 * @see http://www.devpro.it/ |
|
74 * @since 2006/05/31 |
|
75 * @since 2006/08/01 [requires SourceMap.class.php to parse source faster and better (dojo.js.uncompressed.js file (211Kb) successfull cleaned or packed)] |
|
76 * @version 0.8 |
|
77 * Dependencies: |
|
78 * Server: BaseConvert.class.php |
|
79 * Server: SourceMap.class.php |
|
80 * Client: JSL.js (http://www.devpro.it/JSL/) |
|
81 * Convertion is supported by every browser with JSL Library (FF 1+ Opera 8+ and IE5.5+ are supported without JSL too) |
|
82 * @copyright Dean Edwards for his originally idea [dean.edwards.name] and his JavaScript packer |
|
83 */ |
|
84 class JavaScriptCompressor { |
|
85 |
|
86 /** |
|
87 * public variables |
|
88 * stats:string after every compression has some informations |
|
89 * version:string version of this class |
|
90 */ |
|
91 var $stats = '', |
|
92 $version = '0.8'; |
|
93 |
|
94 /** 'private' variables, any comment sorry */ |
|
95 var $__startTime = 0, |
|
96 $__sourceLength = 0, |
|
97 $__sourceNewLength = 0, |
|
98 $__totalSources = 0, |
|
99 $__sources = array(), |
|
100 $__delimeter = array(), |
|
101 $__cleanFinder = array("/(\n|\r)+/", "/( |\t)+/", "/(\n )|( \n)|( \n )/", "/[[:space:]]+(\)|})/", "/(\(|{)[[:space:]]+/", "/[[:space:]]*(;|,|:|<|>|\&|\||\=|\?|\+|\-|\%)[[:space:]]*/", "/\)[[:space:]]+{/", "/}[[:space:]]+\(/"), |
|
102 $__cleanReplacer = array("\n", " ", "\n", "\\1", "\\1", "\\1", "){", "}("), |
|
103 $__BC = null, |
|
104 $__SourceMap = null; |
|
105 |
|
106 /** |
|
107 * public constructor |
|
108 * creates a new BaseConvert class variable (base 36) |
|
109 */ |
|
110 function JavaScriptCompressor() { |
|
111 $this->__SourceMap = new SourceMap(); |
|
112 $this->__BC = new BaseConvert('0123456789abcdefghijklmnopqrstuvwxyz'); |
|
113 $this->__delimeter = array( |
|
114 array('name'=>'doublequote', 'start'=>'"', 'end'=>'"', 'noslash'=>true), |
|
115 array('name'=>'singlequote', 'start'=>"'", 'end'=>"'", 'noslash'=>true), |
|
116 array('name'=>'singlelinecomment', 'start'=>'//', 'end'=>array("\n", "\r")), |
|
117 array('name'=>'multilinecomment', 'start'=>'/*', 'end'=>'*/'), |
|
118 array('name'=>'regexp', 'start'=>'/', 'end'=>'/', 'match'=>"/^\/[^\n\r]+\/$/", 'noslash'=>true) |
|
119 ); |
|
120 } |
|
121 |
|
122 /** |
|
123 * public method |
|
124 * getClean(mixed [, bool]):string |
|
125 * compress JavaScript removing comments and somespaces (on by default) |
|
126 * @param mixed view example and notes on class comments |
|
127 */ |
|
128 function getClean($jsSource) { |
|
129 return $this->__commonInitMethods($jsSource, false); |
|
130 } |
|
131 |
|
132 /** |
|
133 * public method |
|
134 * getPacked(mixed):string |
|
135 * compress JavaScript replaceing words and removing comments and some spaces |
|
136 * @param mixed view example and notes on class comments |
|
137 */ |
|
138 function getPacked($jsSource) { |
|
139 return $this->__commonInitMethods($jsSource, true); |
|
140 } |
|
141 |
|
142 /** 'private' methods, any comment sorry */ |
|
143 function __addCleanCode($str) { |
|
144 return preg_replace($this->__cleanFinder, $this->__cleanReplacer, trim($str)); |
|
145 } |
|
146 function __addClean(&$arr, &$str, &$start, &$end, $clean) { |
|
147 if($clean) |
|
148 array_push($arr, $this->__addCleanCode(substr($str, $start, $end - $start))); |
|
149 else |
|
150 array_push($arr, substr($str, $start, $end - $start)); |
|
151 } |
|
152 function __clean(&$str) { |
|
153 $len = strlen($str); |
|
154 $type = ''; |
|
155 $clean = array(); |
|
156 $map = $this->__SourceMap->getMap($str, $this->__delimeter); |
|
157 for($a = 0, $b = 0, $c = count($map); $a < $c; $a++) { |
|
158 $type = &$map[$a]['name']; |
|
159 switch($type) { |
|
160 case 'code': |
|
161 case 'regexp': |
|
162 case 'doublequote': |
|
163 case 'singlequote': |
|
164 $this->__addClean($clean, $str, $map[$a]['start'], $map[$a]['end'], ($type === 'code')); |
|
165 if($type !== 'regexp') |
|
166 array_push($clean, "\n"); |
|
167 break; |
|
168 } |
|
169 } |
|
170 return preg_replace("/(\n)+/", "\n", trim(implode('', $clean))); |
|
171 } |
|
172 function __commonInitMethods(&$jsSource, $packed) { |
|
173 $header = ''; |
|
174 $this->__startTime = $this->__getTime(); |
|
175 $this->__sourceLength = 0; |
|
176 $this->__sourceManager($jsSource); |
|
177 for($a = 0, $b = $this->__totalSources; $a < $b; $a++) |
|
178 $this->__sources[$a]['code'] = $this->__clean($this->__sources[$a]['code']); |
|
179 $header = $this->__getHeader(); |
|
180 for($a = 0, $b = $this->__totalSources; $a < $b; $a++) |
|
181 $this->__sources[$a] = &$this->__sources[$a]['code']; |
|
182 $this->__sources = implode(';', $this->__sources); |
|
183 if($packed) |
|
184 $this->__sources = $this->__pack($this->__sources); |
|
185 $this->__sourceNewLength = strlen($this->__sources); |
|
186 $this->__setStats(); |
|
187 return $header.$this->__sources; |
|
188 } |
|
189 function __getHeader() { |
|
190 return implode('', array( |
|
191 '/* ',$this->__getScriptNames(),'JavaScriptCompressor ',$this->version,' [www.devpro.it], ', |
|
192 'thanks to Dean Edwards for idea [dean.edwards.name]', |
|
193 " */\r\n" |
|
194 )); |
|
195 } |
|
196 function __getScriptNames() { |
|
197 $a = 0; |
|
198 $result = array(); |
|
199 for($b = $this->__totalSources; $a < $b; $a++) { |
|
200 if($this->__sources[$a]['name'] !== '') |
|
201 array_push($result, $this->__sources[$a]['name']); |
|
202 } |
|
203 $a = count($result); |
|
204 if($a-- > 0) |
|
205 $result[$a] .= ' with '; |
|
206 return $a < 0 ? '' : implode(', ', $result); |
|
207 } |
|
208 function __getSize($size, $dec = 2) { |
|
209 $toEval = ''; |
|
210 $type = array('bytes', 'Kb', 'Mb', 'Gb'); |
|
211 $nsize = $size; |
|
212 $times = 0; |
|
213 while($nsize > 1024) { |
|
214 $nsize = $nsize / 1024; |
|
215 $toEval .= '/1024'; |
|
216 $times++; |
|
217 } |
|
218 if($times === 0) |
|
219 $fSize = $size.' '.$type[$times]; |
|
220 else { |
|
221 eval('$size=($size'.$toEval.');'); |
|
222 $fSize = number_format($size, $dec, '.', '').' '.$type[$times]; |
|
223 } |
|
224 return $fSize; |
|
225 } |
|
226 function __getTime($startTime = null) { |
|
227 list($usec, $sec) = explode(' ', microtime()); |
|
228 $newtime = (float)$usec + (float)$sec; |
|
229 if($startTime !== null) |
|
230 $newtime = number_format(($newtime - $startTime), 3); |
|
231 return $newtime; |
|
232 } |
|
233 function __pack(&$str) { |
|
234 $container = array(); |
|
235 $str = preg_replace("/(\w+)/e", '$this->__BC->toBase($this->__wordsParser("\\1",$container));', $this->__clean($str)); |
|
236 $str = str_replace("\n", '\n', addslashes($str)); |
|
237 return 'eval(function(A,G){return A.replace(/(\\w+)/g,function(a,b){return G[parseInt(b,36)]})}("'.$str.'","'.implode(',', $container).'".split(",")));'; |
|
238 } |
|
239 function __setStats() { |
|
240 $this->stats = implode(' ', array( |
|
241 $this->__getSize($this->__sourceLength), |
|
242 'to', |
|
243 $this->__getSize($this->__sourceNewLength), |
|
244 'in', |
|
245 $this->__getTime($this->__startTime), |
|
246 'seconds' |
|
247 )); |
|
248 } |
|
249 function __sourceManager(&$jsSource) { |
|
250 $b = count($jsSource); |
|
251 $this->__sources = array(); |
|
252 if(is_string($jsSource)) |
|
253 $this->__sourcePusher($jsSource, ''); |
|
254 elseif(is_array($jsSource) && $b > 0) { |
|
255 if(isset($jsSource['code'])) |
|
256 $this->__sourcePusher($jsSource['code'], (isset($jsSource['name']) ? $jsSource['name'] : '')); |
|
257 else { |
|
258 for($a = 0; $a < $b; $a++) { |
|
259 if(is_array($jsSource[$a]) && isset($jsSource[$a]['code'], $jsSource[$a]['name'])) |
|
260 $this->__sourcePusher($jsSource[$a]['code'], trim($jsSource[$a]['name'])); |
|
261 elseif(is_string($jsSource[$a])) |
|
262 $this->__sourcePusher($jsSource[$a], ''); |
|
263 } |
|
264 } |
|
265 } |
|
266 $this->__totalSources = count($this->__sources); |
|
267 } |
|
268 function __sourcePusher(&$code, $name) { |
|
269 $this->__sourceLength += strlen($code); |
|
270 array_push($this->__sources, array('code'=>$code, 'name'=>$name)); |
|
271 } |
|
272 function __wordsParser($str, &$d) { |
|
273 if(is_null($key = array_shift($key = array_keys($d,$str)))) |
|
274 $key = array_push($d, $str) - 1; |
|
275 return $key; |
|
276 } |
|
277 } |
|
278 |
|
279 /** |
|
280 * BaseConvert class, |
|
281 * converts an unsigned base 10 integer to a different base and vice versa. |
|
282 * ______________________________________________________________ |
|
283 * BaseConvert |
|
284 * | |
|
285 * |________ constructor(newBase:string) |
|
286 * | uses newBase string var for convertion |
|
287 * | [i.e. "0123456789abcdef" for an hex convertion] |
|
288 * | |
|
289 * |________ toBase(unsignedInteger:uint):string |
|
290 * | return base value of input |
|
291 * | |
|
292 * |________ fromBase(baseString:string):uint |
|
293 * return base 10 integer value of base input |
|
294 * -------------------------------------------------------------- |
|
295 * REMEMBER: PHP < 6 doesn't work correctly with integer greater than 2147483647 (2^31 - 1) |
|
296 * -------------------------------------------------------------- |
|
297 * @Compatibility >= PHP 4 |
|
298 * @Author Andrea Giammarchi |
|
299 * @Site http://www.devpro.it/ |
|
300 * @Date 2006/06/05 |
|
301 * @Version 1.0 |
|
302 */ |
|
303 |
|
304 class BaseConvert { |
|
305 |
|
306 var $base, $baseLength; |
|
307 |
|
308 function BaseConvert($base) { |
|
309 $this->base = &$base; |
|
310 $this->baseLength = strlen($base); |
|
311 } |
|
312 |
|
313 function toBase($num) { |
|
314 $module = 0; $result = ''; |
|
315 while($num) { |
|
316 $result = $this->base{($module = $num % $this->baseLength)}.$result; |
|
317 $num = (int)(($num - $module) / $this->baseLength); |
|
318 } |
|
319 return $result !== '' ? $result : $this->base{0}; |
|
320 } |
|
321 |
|
322 function fromBase($str) { |
|
323 $pos = 0; $len = strlen($str) - 1; $result = 0; |
|
324 while($pos < $len) |
|
325 $result += pow($this->baseLength, ($len - $pos)) * strpos($this->base, $str{($pos++)}); |
|
326 return $len >= 0 ? $result + strpos($this->base, $str{($pos)}) : null; |
|
327 } |
|
328 } |
|
329 |
|
330 /** |
|
331 * SourceMap class, |
|
332 * reads a generic language source code and returns its map. |
|
333 * ______________________________________________________________ |
|
334 * The SourceMap goals is to create a map of a generic script/program language. |
|
335 * The getMap method returns an array/list of arrays/dictionary/objects |
|
336 * of source map using delimeters variable to map correctly: |
|
337 * - multi line comments |
|
338 * - single line comments |
|
339 * - double quoted strings |
|
340 * - single quoted strings |
|
341 * - pure code |
|
342 * - everything else (for example regexp [/re/] with javascript), just adding a correct delimeter |
|
343 * -------------------------------------------------------------- |
|
344 * What about the delimeter |
|
345 * It's an array/list of arrays/dictionary/obects with some properties to find what you're looking for. |
|
346 * |
|
347 * parameters are: |
|
348 * - name, the name of the delimeter (i.e. "doublequote") |
|
349 * - start, one or mode chars to find as start delimeter (i.e. " for double quoted string) |
|
350 * - end, one or mode chars to find as end delimeter (i.e. " for double quoted string) [end should be an array/list too] |
|
351 * |
|
352 * optional parameters are: |
|
353 * - noslash, if true find the end of the delimeter only if last char is not slashed (i.e. "string\"test" find " after test) |
|
354 * - match, if choosed language has regexp, verify if string from start to end matches used regexp (i.e. /^\/[^\n\r]+\/$/ for JavaScript regexp) |
|
355 * |
|
356 * If end parameter is an array, match and noslash are not supported (i.e. ["\n", "\r"] for end delimeter of a single line comment) |
|
357 * -------------------------------------------------------------- |
|
358 * What about SourceMap usage |
|
359 * It should be a good solution to create sintax highlighter, parser, |
|
360 * verifier or some other source code parsing procedure |
|
361 * -------------------------------------------------------------- |
|
362 * What about SourceMap performance script/languages |
|
363 * I've created different version of this class to test each script/program language performance too. |
|
364 * Python with or without Psyco is actually the faster parser. |
|
365 * However with this PHP version this class has mapped "dojo.js.uncompressed.js" file (about 211Kb) in less than 0.5 second. |
|
366 * Test has been done with embed class and PHP as module, any accelerator was used for this PHP test. |
|
367 * -------------------------------------------------------------- |
|
368 * @Compatibility >= PHP 4 |
|
369 * @Author Andrea Giammarchi |
|
370 * @Site http://www.devpro.it/ |
|
371 * @Date 2006/08/01 |
|
372 * @LastMOd 2006/08/01 |
|
373 * @Version 0.1 |
|
374 * @Application Last version of JavaScriptCompressor class use this one to map source code. |
|
375 */ |
|
376 class SourceMap { |
|
377 |
|
378 /** |
|
379 * public method |
|
380 * getMap(&$source:string, &$delimeters:array):array |
|
381 * Maps the source code using $delimeters rules and returns map as an array |
|
382 * NOTE: read comments to know more about map and delimeter |
|
383 * |
|
384 * @param string generic source code |
|
385 * @param array array with nested array with code rules |
|
386 */ |
|
387 function getMap(&$source, &$delimeters) { |
|
388 |
|
389 # "unsigned" integer variables |
|
390 $sourcePosition = 0; |
|
391 $delimetersPosition = 0; |
|
392 $findLength = 0; |
|
393 $len = 0; |
|
394 $tempIndex = 0; |
|
395 $sourceLength = strlen($source); |
|
396 $delimetersLength = count($delimeters); |
|
397 |
|
398 # integer variables |
|
399 $tempPosition = -1; |
|
400 $endPosition = -1; |
|
401 |
|
402 # array variables |
|
403 $map = array(); |
|
404 $tempMap = array(); |
|
405 $tempDelimeter = array(); |
|
406 |
|
407 while($sourcePosition < $sourceLength) { |
|
408 $endPosition = -1; |
|
409 for($delimetersPosition = 0; $delimetersPosition < $delimetersLength; $delimetersPosition++) { |
|
410 $tempPosition = strpos($source, $delimeters[$delimetersPosition]['start'], $sourcePosition); |
|
411 if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) { |
|
412 $endPosition = $tempPosition; |
|
413 $tempIndex = $delimetersPosition; |
|
414 } |
|
415 } |
|
416 if($endPosition !== -1) { |
|
417 $sourcePosition = $endPosition; |
|
418 $tempDelimeter = &$delimeters[$tempIndex]; |
|
419 $findLength = strlen($tempDelimeter['start']); |
|
420 if(is_array($tempDelimeter['end'])) { |
|
421 $delimetersPosition = 0; |
|
422 $endPosition = -1; |
|
423 for($len = count($tempDelimeter['end']); $delimetersPosition < $len; $delimetersPosition++) { |
|
424 $tempPosition = strpos($source, $tempDelimeter['end'][$delimetersPosition], $sourcePosition + $findLength); |
|
425 if($tempPosition !== false && ($tempPosition < $endPosition || $endPosition === -1)) { |
|
426 $endPosition = $tempPosition; |
|
427 $tempIndex = $delimetersPosition; |
|
428 } |
|
429 } |
|
430 if($endPosition !== -1) |
|
431 $endPosition = $endPosition + strlen($tempDelimeter['end'][$tempIndex]); |
|
432 else |
|
433 $endPosition = $sourceLength; |
|
434 array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); |
|
435 $sourcePosition = $endPosition - 1; |
|
436 } |
|
437 elseif(isset($tempDelimeter['match'])) { |
|
438 $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength); |
|
439 $len = strlen($tempDelimeter['end']); |
|
440 if($tempPosition !== false && preg_match($tempDelimeter['match'], substr($source, $sourcePosition, $tempPosition - $sourcePosition + $len))) { |
|
441 $endPosition = isset($tempDelimeter['noslash']) ? $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength) : $tempPosition + $len; |
|
442 array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); |
|
443 $sourcePosition = $endPosition - 1; |
|
444 } |
|
445 } |
|
446 else { |
|
447 if(isset($tempDelimeter['noslash'])) |
|
448 $endPosition = $this->__endCharNoSlash($source, $sourcePosition, $tempDelimeter['end'], $sourceLength); |
|
449 else { |
|
450 $tempPosition = strpos($source, $tempDelimeter['end'], $sourcePosition + $findLength); |
|
451 if($tempPosition !== false) |
|
452 $endPosition = $tempPosition + strlen($tempDelimeter['end']); |
|
453 else |
|
454 $endPosition = $sourceLength; |
|
455 } |
|
456 array_push($map, array('name'=>$tempDelimeter['name'], 'start'=>$sourcePosition, 'end'=>$endPosition)); |
|
457 $sourcePosition = $endPosition - 1; |
|
458 } |
|
459 } |
|
460 else |
|
461 $sourcePosition = $sourceLength - 1; |
|
462 ++$sourcePosition; |
|
463 } |
|
464 $len = count($map); |
|
465 if($len === 0) |
|
466 array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$sourceLength)); |
|
467 else { |
|
468 for($tempIndex = 0; $tempIndex < $len; $tempIndex++) { |
|
469 if($tempIndex === 0 && $map[$tempIndex]['start'] > 0) |
|
470 array_push($tempMap, array('name'=>'code', 'start'=>0, 'end'=>$map[$tempIndex]['start'])); |
|
471 elseif($tempIndex > 0 && $map[$tempIndex]['start'] > $map[$tempIndex-1]['end']) |
|
472 array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex-1]['end'], 'end'=>$map[$tempIndex]['start'])); |
|
473 array_push($tempMap, array('name'=>$map[$tempIndex]['name'], 'start'=>$map[$tempIndex]['start'], 'end'=>$map[$tempIndex]['end'])); |
|
474 if($tempIndex + 1 === $len && $map[$tempIndex]['end'] < $sourceLength) |
|
475 array_push($tempMap, array('name'=>'code', 'start'=>$map[$tempIndex]['end'], 'end'=>$sourceLength)); |
|
476 } |
|
477 } |
|
478 return $tempMap; |
|
479 } |
|
480 |
|
481 function __endCharNoSlash(&$source, $position, &$find, &$len) { |
|
482 $temp = strlen($find); |
|
483 do { |
|
484 $position = strpos($source, $find, $position + 1); |
|
485 }while($position !== false && !$this->__charNoSlash($source, $position)); |
|
486 if($position === false) $position = $len - $temp; |
|
487 return $position + $temp; |
|
488 } |
|
489 |
|
490 function __charNoSlash(&$source, &$position) { |
|
491 $next = 1; $len = $position - $next; |
|
492 while($len > 0 && $source{$len} === '\\') $len = $position - (++$next); |
|
493 return (($next - 1) % 2 === 0); |
|
494 } |
|
495 } |
|
496 ?> |