Author: spadgos
(2009/05/22 07:01) Over 2 years ago
Updated to the latest version of the JSPacker.
1
<?php
2
/* 7 December 2006. version 1.0
/* 9 April 2008. version 1.1
3
*
4
* This is the php version of the Dean Edwards JavaScript 's Packer,
* This is the php version of the Dean Edwards JavaScript's Packer,
5
* Based on :
6
7
* ParseMaster, version 1.0.2 (2005-08-19) Copyright 2005, Dean Edwards
8
* a multi-pattern parser.
9
* KNOWN BUG: erroneous behavior when using escapeChar with a replacement
10
* value that is a function
11
12
* packer, version 2.0.2 (2005-08-19) Copyright 2004-2005, Dean Edwards
13
14
* License: http://creativecommons.org/licenses/LGPL/2.1/
15
16
* Ported to PHP by Nicolas Martin.
17
18
* ----------------------------------------------------------------------
19
* changelog:
20
* examples of usage :
* 1.1 : correct a bug, '\0' packed then unpacked becomes '\'.
21
* $myPacker = new JavaScriptPacker($script, 62, true, false);
22
* $packed = $myPacker->pack();
23
24
* or
25
26
* $myPacker = new JavaScriptPacker($script, 'Normal', true, false);
27
28
29
* or (default values)
30
31
* $myPacker = new JavaScriptPacker($script);
32
33
34
35
* params of the constructor :
36
* $script: the JavaScript to pack, string.
37
* $encoding: level of encoding, int or string :
38
* 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.
39
* default: 62.
40
* $fastDecode: include the fast decoder in the packed result, boolean.
41
* default : true.
42
* $specialChars: if you are flagged your private and local variables
43
* in the script, boolean.
44
* default: false.
45
46
* The pack() method return the compressed JavasScript, as a string.
47
48
* see http://dean.edwards.name/packer/usage/ for more information.
49
50
* Notes :
51
* # need PHP 5 . Tested with PHP 5.1.2
52
53
* # The packed result may be different than with the Dean Edwards
54
* version, but with the same length. The reason is that the PHP
* # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3
55
* function usort to sort array don't necessarily preserve the
56
* original order of two equal member. The Javascript sort function
57
* in fact preserve this order (but that's not require by the
58
* ECMAScript standard). So the encoded keywords order can be
59
* different in the two results.
60
61
* # Be careful with the 'High ASCII' Level encoding if you use
62
* UTF-8 in your files...
63
*/
64
65
66
class JavaScriptPacker {
67
// constants
68
const IGNORE = '$1';
69
70
// validate parameters
71
private $_script = '';
72
private $_encoding = 62;
73
private $_fastDecode = true;
74
private $_specialChars = false;
75
76
private $LITERAL_ENCODING = array(
77
'None' => 0,
78
'Numeric' => 10,
79
'Normal' => 62,
80
'High ASCII' => 95
81
);
82
83
public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)
84
{
85
$this->_script = $_script . "\n";
86
if (array_key_exists($_encoding, $this->LITERAL_ENCODING))
87
$_encoding = $this->LITERAL_ENCODING[$_encoding];
88
$this->_encoding = min((int)$_encoding, 95);
89
$this->_fastDecode = $_fastDecode;
90
$this->_specialChars = $_specialChars;
91
}
92
93
public function pack() {
94
$this->_addParser('_basicCompression');
95
if ($this->_specialChars)
96
$this->_addParser('_encodeSpecialChars');
97
if ($this->_encoding)
98
$this->_addParser('_encodeKeywords');
99
100
// go!
101
return $this->_pack($this->_script);
102
103
104
// apply all parsing routines
105
private function _pack($script) {
106
for ($i = 0; isset($this->_parsers[$i]); $i++) {
107
$script = call_user_func(array(&$this,$this->_parsers[$i]), $script);
108
109
return $script;
110
111
112
// keep a list of parsing functions, they'll be executed all at once
113
private $_parsers = array();
114
private function _addParser($parser) {
115
$this->_parsers[] = $parser;
116
117
118
// zero encoding - just removal of white space and comments
119
private function _basicCompression($script) {
120
$parser = new ParseMaster();
121
// make safe
122
$parser->escapeChar = '\\';
123
// protect strings
124
$parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);
125
$parser->add('/"[^"\\n\\r]*"/', self::IGNORE);
126
// remove comments
127
$parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
128
$parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
129
// protect regular expressions
130
$parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
131
$parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);
132
// remove: ;;; doSomething();
133
if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
134
// remove redundant semi-colons
135
$parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops
136
$parser->add('/;+\\s*([};])/', '$2');
137
// apply the above
138
$script = $parser->exec($script);
139
140
// remove white-space
141
$parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
142
$parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
143
$parser->add('/\\s+/', '');
144
// done
145
return $parser->exec($script);
146
147
148
private function _encodeSpecialChars($script) {
149
150
// replace: $name -> n, $$name -> na
151
$parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
152
array('fn' => '_replace_name')
153
154
// replace: _name -> _0, double-underscore (__name) is ignored
155
$regexp = '/\\b_[A-Za-z\\d]\\w*/';
156
// build the word list
157
$keywords = $this->_analyze($script, $regexp, '_encodePrivate');
158
// quick ref
159
$encoded = $keywords['encoded'];
160
161
$parser->add($regexp,
162
array(
163
'fn' => '_replace_encoded',
164
'data' => $encoded
165
)
166
167
168
169
170
private function _encodeKeywords($script) {
171
// escape high-ascii values already in the script (i.e. in strings)
172
if ($this->_encoding > 62)
173
$script = $this->_escape95($script);
174
// create the parser
175
176
$encode = $this->_getEncoder($this->_encoding);
177
// for high-ascii, don't encode single character low-ascii
178
$regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';
179
180
$keywords = $this->_analyze($script, $regexp, $encode);
181
182
183
// encode
184
185
186
187
188
189
190
if (empty($script)) return $script;
191
else {
192
//$res = $parser->exec($script);
193
//$res = $this->_bootStrap($res, $keywords);
194
//return $res;
195
return $this->_bootStrap($parser->exec($script), $keywords);
196
197
198
199
private function _analyze($script, $regexp, $encode) {
200
// analyse
201
// retreive all words in the script
202
$all = array();
203
preg_match_all($regexp, $script, $all);
204
$_sorted = array(); // list of words sorted by frequency
205
$_encoded = array(); // dictionary of word->encoding
206
$_protected = array(); // instances of "protected" words
207
$all = $all[0]; // simulate the javascript comportement of global match
208
if (!empty($all)) {
209
$unsorted = array(); // same list, not sorted
210
$protected = array(); // "protected" words (dictionary of word->"word")
211
$value = array(); // dictionary of charCode->encoding (eg. 256->ff)
212
$this->_count = array(); // word->count
213
$i = count($all); $j = 0; //$word = null;
214
// count the occurrences - used for sorting later
215
do {
216
--$i;
217
$word = '$' . $all[$i];
218
if (!isset($this->_count[$word])) {
219
$this->_count[$word] = 0;
220
$unsorted[$j] = $word;
221
// make a dictionary of all of the protected words in this script
222
// these are words that might be mistaken for encoding
223
//if (is_string($encode) && method_exists($this, $encode))
224
$values[$j] = call_user_func(array(&$this, $encode), $j);
225
$protected['$' . $values[$j]] = $j++;
226
227
// increment the word counter
228
$this->_count[$word]++;
229
} while ($i > 0);
230
// prepare to sort the word list, first we must protect
231
// words that are also used as codes. we assign them a code
232
// equivalent to the word itself.
233
// e.g. if "do" falls within our encoding range
234
// then we store keywords["do"] = "do";
235
// this avoids problems when decoding
236
$i = count($unsorted);
237
238
$word = $unsorted[--$i];
239
if (isset($protected[$word]) /*!= null*/) {
240
$_sorted[$protected[$word]] = substr($word, 1);
241
$_protected[$protected[$word]] = true;
242
243
244
} while ($i);
245
246
// sort the words by frequency
247
// Note: the javascript and php version of sort can be different :
248
// in php manual, usort :
249
// " If two members compare as equal,
250
// their order in the sorted array is undefined."
251
// so the final packed script is different of the Dean's javascript version
252
// but equivalent.
253
// the ECMAscript standard does not guarantee this behaviour,
254
// and thus not all browsers (e.g. Mozilla versions dating back to at
255
// least 2003) respect this.
256
usort($unsorted, array(&$this, '_sortWords'));
257
$j = 0;
258
// because there are "protected" words in the list
259
// we must add the sorted words around them
260
261
if (!isset($_sorted[$i]))
262
$_sorted[$i] = substr($unsorted[$j++], 1);
263
$_encoded[$_sorted[$i]] = $values[$i];
264
} while (++$i < count($unsorted));
265
266
return array(
267
'sorted' => $_sorted,
268
'encoded' => $_encoded,
269
'protected' => $_protected);
270
271
272
private $_count = array();
273
private function _sortWords($match1, $match2) {
274
return $this->_count[$match2] - $this->_count[$match1];
275
276
277
// build the boot function used for loading and decoding
278
private function _bootStrap($packed, $keywords) {
279
$ENCODE = $this->_safeRegExp('$encode\\($count\\)');
280
281
// $packed: the packed script
282
$packed = "'" . $this->_escape($packed) . "'";
283
284
// $ascii: base for encoding
285
$ascii = min(count($keywords['sorted']), $this->_encoding);
286
if ($ascii == 0) $ascii = 1;
287
288
// $count: number of words contained in the script
289
$count = count($keywords['sorted']);
290
291
// $keywords: list of words contained in the script
292
foreach ($keywords['protected'] as $i=>$value) {
293
$keywords['sorted'][$i] = '';
294
295
// convert from a string to an array
296
ksort($keywords['sorted']);
297
$keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";
298
299
$encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);
300
$encode = $this->_getJSFunction($encode);
301
$encode = preg_replace('/_encoding/','$ascii', $encode);
302
$encode = preg_replace('/arguments\\.callee/','$encode', $encode);
303
$inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');
304
305
// $decode: code snippet to speed up decoding
306
if ($this->_fastDecode) {
307
// create the decoder
308
$decode = $this->_getJSFunction('_decodeBody');
309
310
$decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
311
// perform the encoding inline for lower ascii values
312
elseif ($ascii < 36)
313
$decode = preg_replace($ENCODE, $inline, $decode);
314
// special case: when $count==0 there are no keywords. I want to keep
315
// the basic shape of the unpacking funcion so i'll frig the code...
316
if ($count == 0)
317
$decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
318
319
320
// boot function
321
$unpack = $this->_getJSFunction('_unpack');
322
323
// insert the decoder
324
$this->buffer = $decode;
325
$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
326
327
$unpack = preg_replace('/"/', "'", $unpack);
328
if ($this->_encoding > 62) { // high-ascii
329
// get rid of the word-boundaries for regexp matches
330
$unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
331
332
if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {
333
// insert the encode function
334
$this->buffer = $encode;
335
$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
336
} else {
337
// perform the encoding inline
338
$unpack = preg_replace($ENCODE, $inline, $unpack);
339
340
// pack the boot function too
341
$unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
342
$unpack = $unpackPacker->pack();
343
344
// arguments
345
$params = array($packed, $ascii, $count, $keywords);
346
347
$params[] = 0;
348
$params[] = '{}';
349
350
$params = implode(',', $params);
351
352
// the whole thing
353
return 'eval(' . $unpack . '(' . $params . "))\n";
354
355
356
private $buffer;
357
private function _insertFastDecode($match) {
358
return '{' . $this->buffer . ';';
359
360
private function _insertFastEncode($match) {
361
return '{$encode=' . $this->buffer . ';';
362
363
364
// mmm.. ..which one do i need ??
365
private function _getEncoder($ascii) {
366
return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?
367
'_encode95' : '_encode62' : '_encode36' : '_encode10';
368
369
370
// zero encoding
371
// characters: 0123456789
372
private function _encode10($charCode) {
373
return $charCode;
374
375
376
// inherent base36 support
377
// characters: 0123456789abcdefghijklmnopqrstuvwxyz
378
private function _encode36($charCode) {
379
return base_convert($charCode, 10, 36);
380
381
382
// hitch a ride on base36 and add the upper case alpha characters
383
// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
384
private function _encode62($charCode) {
385
$res = '';
386
if ($charCode >= $this->_encoding) {
387
$res = $this->_encode62((int)($charCode / $this->_encoding));
388
389
$charCode = $charCode % $this->_encoding;
390
391
if ($charCode > 35)
392
return $res . chr($charCode + 29);
393
else
394
return $res . base_convert($charCode, 10, 36);
395
396
397
// use high-ascii values
398
// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
399
private function _encode95($charCode) {
400
401
if ($charCode >= $this->_encoding)
402
$res = $this->_encode95($charCode / $this->_encoding);
403
404
return $res . chr(($charCode % $this->_encoding) + 161);
405
406
407
private function _safeRegExp($string) {
408
return '/'.preg_replace('/\$/', '\\\$', $string).'/';
409
410
411
private function _encodePrivate($charCode) {
412
return "_" . $charCode;
413
414
415
// protect characters used by the parser
416
private function _escape($script) {
417
return preg_replace('/([\\\\\'])/', '\\\$1', $script);
418
419
420
// protect high-ascii characters already in the script
421
private function _escape95($script) {
422
return preg_replace_callback(
423
'/[\\xa1-\\xff]/',
424
array(&$this, '_escape95Bis'),
425
$script
426
427
428
private function _escape95Bis($match) {
429
return '\x'.((string)dechex(ord($match)));
430
431
432
433
private function _getJSFunction($aName) {
434
if (defined('self::JSFUNCTION'.$aName))
435
return constant('self::JSFUNCTION'.$aName);
436
437
return '';
438
439
440
// JavaScript Functions used.
441
// Note : In Dean's version, these functions are converted
442
// with 'String(aFunctionName);'.
443
// This internal conversion complete the original code, ex :
444
// 'while (aBool) anAction();' is converted to
445
// 'while (aBool) { anAction(); }'.
446
// The JavaScript functions below are corrected.
447
448
// unpacking function - this is the boot strap function
449
// data extracted from this packing routine is passed to
450
// this function when decoded in the target
451
// NOTE ! : without the ';' final.
452
const JSFUNCTION_unpack =
453
454
'function($packed, $ascii, $count, $keywords, $encode, $decode) {
455
while ($count--) {
456
if ($keywords[$count]) {
457
$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
458
459
460
return $packed;
461
}';
462
/*
463
464
while ($count--)
465
if ($keywords[$count])
466
467
468
469
470
471
// code-snippet inserted into the unpacker to speed up decoding
472
const JSFUNCTION_decodeBody =
473
//_decode = function() {
474
// does the browser support String.replace where the
475
// replacement value is a function?
476
477
' if (!\'\'.replace(/^/, String)) {
478
// decode all the values we need
479
480
$decode[$encode($count)] = $keywords[$count] || $encode($count);
481
482
// global replacement function
483
$keywords = [function ($encoded) {return $decode[$encoded]}];
484
// generic match
485
$encode = function () {return \'\\\\w+\'};
486
// reset the loop counter - we are now doing a global replace
487
$count = 1;
488
489
';
490
//};
491
492
493
494
while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
495
496
497
498
$encode = function () {return\'\\\\w+\'};
499
500
501
502
503
504
505
506
const JSFUNCTION_encode10 =
507
'function($charCode) {
508
509
}';//;';
510
511
512
513
const JSFUNCTION_encode36 =
514
515
return $charCode.toString(36);
516
517
518
519
520
const JSFUNCTION_encode62 =
521
522
return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
523
(($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
524
525
526
527
528
const JSFUNCTION_encode95 =
529
530
return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
531
String.fromCharCode($charCode % _encoding + 161);
532
533
534
535
536
537
class ParseMaster {
538
public $ignoreCase = false;
539
public $escapeChar = '';
540
541
542
const EXPRESSION = 0;
543
const REPLACEMENT = 1;
544
const LENGTH = 2;
545
546
// used to determine nesting levels
547
private $GROUPS = '/\\(/';//g
548
private $SUB_REPLACE = '/\\$\\d/';
549
private $INDEXED = '/^\\$\\d+$/';
550
private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
551
private $ESCAPE = '/\\\./';//g
552
private $QUOTE = '/\'/';
553
private $DELETED = '/\\x01[^\\x01]*\\x01/';//g
554
555
public function add($expression, $replacement = '') {
556
// count the number of sub-expressions
557
// - add one because each pattern is itself a sub-expression
558
$length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out);
559
560
// treat only strings $replacement
561
if (is_string($replacement)) {
562
// does the pattern deal with sub-expressions?
563
if (preg_match($this->SUB_REPLACE, $replacement)) {
564
// a simple lookup? (e.g. "$2")
565
if (preg_match($this->INDEXED, $replacement)) {
566
// store the index (used for fast retrieval of matched strings)
567
$replacement = (int)(substr($replacement, 1)) - 1;
568
} else { // a complicated lookup (e.g. "Hello $2 $1")
569
// build a function to do the lookup
570
$quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))
571
? '"' : "'";
572
$replacement = array(
573
'fn' => '_backReferences',
574
'data' => array(
575
'replacement' => $replacement,
576
'length' => $length,
577
'quote' => $quote
578
579
580
581
582
583
// pass the modified arguments
584
if (!empty($expression)) $this->_add($expression, $replacement, $length);
585
else $this->_add('/^$/', $replacement, $length);
586
587
588
public function exec($string) {
589
// execute the global replacement
590
$this->_escaped = array();
591
592
// simulate the _patterns.toSTring of Dean
593
$regexp = '/';
594
foreach ($this->_patterns as $reg) {
595
$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
596
597
$regexp = substr($regexp, 0, -1) . '/';
598
$regexp .= ($this->ignoreCase) ? 'i' : '';
599
600
$string = $this->_escape($string, $this->escapeChar);
601
$string = preg_replace_callback(
602
$regexp,
603
604
&$this,
605
'_replacement'
606
),
607
$string
608
609
$string = $this->_unescape($string, $this->escapeChar);
610
611
return preg_replace($this->DELETED, '', $string);
612
613
614
public function reset() {
615
// clear the patterns collection so that this object may be re-used
616
$this->_patterns = array();
617
618
619
// private
620
private $_escaped = array(); // escaped characters
621
private $_patterns = array(); // patterns stored by index
622
623
// create and add a new pattern to the patterns collection
624
private function _add() {
625
$arguments = func_get_args();
626
$this->_patterns[] = $arguments;
627
628
629
// this is the global replace function (it's quite complicated)
630
private function _replacement($arguments) {
631
if (empty($arguments)) return '';
632
633
$i = 1; $j = 0;
634
// loop through the patterns
635
while (isset($this->_patterns[$j])) {
636
$pattern = $this->_patterns[$j++];
637
// do we have a result?
638
if (isset($arguments[$i]) && ($arguments[$i] != '')) {
639
$replacement = $pattern[self::REPLACEMENT];
640
641
if (is_array($replacement) && isset($replacement['fn'])) {
642
643
if (isset($replacement['data'])) $this->buffer = $replacement['data'];
644
return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
645
646
} elseif (is_int($replacement)) {
647
return $arguments[$replacement + $i];
648
649
650
$delete = ($this->escapeChar == '' ||
651
strpos($arguments[$i], $this->escapeChar) === false)
652
? '' : "\x01" . $arguments[$i] . "\x01";
653
return $delete . $replacement;
654
655
// skip over references to sub-expressions
656
657
$i += $pattern[self::LENGTH];
658
659
660
661
662
private function _backReferences($match, $offset) {
663
$replacement = $this->buffer['replacement'];
664
$quote = $this->buffer['quote'];
665
$i = $this->buffer['length'];
666
while ($i) {
667
$replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);
668
669
return $replacement;
670
671
672
private function _replace_name($match, $offset){
673
$length = strlen($match[$offset + 2]);
674
$start = $length - max($length - strlen($match[$offset + 3]), 0);
675
return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];
676
677
678
private function _replace_encoded($match, $offset) {
679
return $this->buffer[$match[$offset]];
680
681
682
683
// php : we cannot pass additional data to preg_replace_callback,
684
// and we cannot use &$this in create_function, so let's go to lower level
685
686
687
// encode escaped characters
688
private function _escape($string, $escapeChar) {
689
if ($escapeChar) {
690
$this->buffer = $escapeChar;
691
692
'/\\' . $escapeChar . '(.)' .'/',
693
array(&$this, '_escapeBis'),
694
695
696
697
698
return $string;
699
700
701
private function _escapeBis($match) {
702
$this->_escaped[] = $match[1];
703
return $this->buffer;
704
705
706
// decode escaped characters
707
private function _unescape($string, $escapeChar) {
708
709
$regexp = '/'.'\\'.$escapeChar.'/';
710
$this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);
711
return preg_replace_callback
712
(
713
714
array(&$this, '_unescapeBis'),
715
716
717
718
719
720
721
722
private function _unescapeBis() {
723
if (!empty($this->_escaped[$this->buffer['i']])) {
724
$temp = $this->_escaped[$this->buffer['i']];
725
726
$temp = '';
if (isset($this->_escaped[$this->buffer['i']])
727
&& $this->_escaped[$this->buffer['i']] != '')
728
$this->buffer['i']++;
729
return $this->buffer['escapeChar'] . $temp;
730
731
732
private function _internalEscape($string) {
733
return preg_replace($this->ESCAPE, '', $string);
734
735
736
?>
737
738
739
740
741
/* 7 December 2006. version 1.0/* 9 April 2008. version 1.1** This is the php version of the Dean Edwards JavaScript 's Packer,****** changelog:* examples of usage :* 1.1 : correct a bug, '\0' packed then unpacked becomes '\'.* $myPacker = new JavaScriptPacker($script, 62, true, false);* ----------------------------------------------------------------------* $packed = $myPacker->pack();* examples of usage :* or* $myPacker = new JavaScriptPacker($script, 62, true, false);* $packed = $myPacker->pack();* $myPacker = new JavaScriptPacker($script, 'Normal', true, false);* $packed = $myPacker->pack();* or** or (default values)* $myPacker = new JavaScriptPacker($script, 'Normal', true, false);* $packed = $myPacker->pack();* $myPacker = new JavaScriptPacker($script);* $packed = $myPacker->pack();* or (default values)** $myPacker = new JavaScriptPacker($script);* params of the constructor :* $packed = $myPacker->pack();* $script: the JavaScript to pack, string.* $encoding: level of encoding, int or string :* 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.* params of the constructor :* default: 62.* $script: the JavaScript to pack, string.* $fastDecode: include the fast decoder in the packed result, boolean.* $encoding: level of encoding, int or string :* default : true.* 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.* $specialChars: if you are flagged your private and local variables* default: 62.* in the script, boolean.* $fastDecode: include the fast decoder in the packed result, boolean.* default: false.* default : true.* $specialChars: if you are flagged your private and local variables* The pack() method return the compressed JavasScript, as a string.* in the script, boolean.* default: false.* see http://dean.edwards.name/packer/usage/ for more information.* The pack() method return the compressed JavasScript, as a string.* Notes :* # need PHP 5 . Tested with PHP 5.1.2* see http://dean.edwards.name/packer/usage/ for more information.** # The packed result may be different than with the Dean Edwards* Notes :* version, but with the same length. The reason is that the PHP* # need PHP 5 . Tested with PHP 5.1.2, 5.1.3, 5.1.4, 5.2.3* function usort to sort array don't necessarily preserve the* original order of two equal member. The Javascript sort function* # The packed result may be different than with the Dean Edwards* in fact preserve this order (but that's not require by the* version, but with the same length. The reason is that the PHP* ECMAScript standard). So the encoded keywords order can be* function usort to sort array don't necessarily preserve the* different in the two results.* original order of two equal member. The Javascript sort function* in fact preserve this order (but that's not require by the* # Be careful with the 'High ASCII' Level encoding if you use* ECMAScript standard). So the encoded keywords order can be* UTF-8 in your files...* different in the two results.*/** # Be careful with the 'High ASCII' Level encoding if you use* UTF-8 in your files...class JavaScriptPacker {*/// constantsconst IGNORE = '$1';class JavaScriptPacker {// validate parameters// constantsprivate $_script = '';const IGNORE = '$1';private $_encoding = 62;private $_fastDecode = true;// validate parametersprivate $_specialChars = false;private $_script = '';private $_encoding = 62;private $LITERAL_ENCODING = array(private $_fastDecode = true;'None' => 0,private $_specialChars = false;'Numeric' => 10,'Normal' => 62,private $LITERAL_ENCODING = array('High ASCII' => 95'None' => 0,);'Numeric' => 10,'Normal' => 62,public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)'High ASCII' => 95{);$this->_script = $_script . "\n";if (array_key_exists($_encoding, $this->LITERAL_ENCODING))public function __construct($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)$_encoding = $this->LITERAL_ENCODING[$_encoding];{$this->_encoding = min((int)$_encoding, 95);$this->_script = $_script . "\n";$this->_fastDecode = $_fastDecode;if (array_key_exists($_encoding, $this->LITERAL_ENCODING))$this->_specialChars = $_specialChars;$_encoding = $this->LITERAL_ENCODING[$_encoding];}$this->_encoding = min((int)$_encoding, 95);$this->_fastDecode = $_fastDecode;public function pack() {$this->_specialChars = $_specialChars;$this->_addParser('_basicCompression');}if ($this->_specialChars)$this->_addParser('_encodeSpecialChars');public function pack() {if ($this->_encoding)$this->_addParser('_basicCompression');$this->_addParser('_encodeKeywords');if ($this->_specialChars)$this->_addParser('_encodeSpecialChars');// go!if ($this->_encoding)return $this->_pack($this->_script);$this->_addParser('_encodeKeywords');}// go!// apply all parsing routinesreturn $this->_pack($this->_script);private function _pack($script) {}for ($i = 0; isset($this->_parsers[$i]); $i++) {$script = call_user_func(array(&$this,$this->_parsers[$i]), $script);// apply all parsing routines}private function _pack($script) {return $script;for ($i = 0; isset($this->_parsers[$i]); $i++) {}$script = call_user_func(array(&$this,$this->_parsers[$i]), $script);}// keep a list of parsing functions, they'll be executed all at oncereturn $script;private $_parsers = array();}private function _addParser($parser) {$this->_parsers[] = $parser;// keep a list of parsing functions, they'll be executed all at once}private $_parsers = array();private function _addParser($parser) {// zero encoding - just removal of white space and comments$this->_parsers[] = $parser;private function _basicCompression($script) {}$parser = new ParseMaster();// make safe// zero encoding - just removal of white space and comments$parser->escapeChar = '\\';private function _basicCompression($script) {// protect strings$parser = new ParseMaster();$parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);// make safe$parser->add('/"[^"\\n\\r]*"/', self::IGNORE);$parser->escapeChar = '\\';// remove comments// protect strings$parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');$parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);$parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');$parser->add('/"[^"\\n\\r]*"/', self::IGNORE);// protect regular expressions// remove comments$parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE$parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');$parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);$parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');// remove: ;;; doSomething();// protect regular expressionsif ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');$parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE// remove redundant semi-colons$parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);$parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops// remove: ;;; doSomething();$parser->add('/;+\\s*([};])/', '$2');if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');// apply the above// remove redundant semi-colons$script = $parser->exec($script);$parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops$parser->add('/;+\\s*([};])/', '$2');// remove white-space// apply the above$parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');$script = $parser->exec($script);$parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');$parser->add('/\\s+/', '');// remove white-space// done$parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');return $parser->exec($script);$parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');}$parser->add('/\\s+/', '');// doneprivate function _encodeSpecialChars($script) {return $parser->exec($script);$parser = new ParseMaster();}// replace: $name -> n, $$name -> na$parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',private function _encodeSpecialChars($script) {array('fn' => '_replace_name')$parser = new ParseMaster(););// replace: $name -> n, $$name -> na// replace: _name -> _0, double-underscore (__name) is ignored$parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',$regexp = '/\\b_[A-Za-z\\d]\\w*/';array('fn' => '_replace_name')// build the word list);$keywords = $this->_analyze($script, $regexp, '_encodePrivate');// replace: _name -> _0, double-underscore (__name) is ignored// quick ref$regexp = '/\\b_[A-Za-z\\d]\\w*/';$encoded = $keywords['encoded'];// build the word list$keywords = $this->_analyze($script, $regexp, '_encodePrivate');$parser->add($regexp,// quick refarray($encoded = $keywords['encoded'];'fn' => '_replace_encoded','data' => $encoded$parser->add($regexp,)array();'fn' => '_replace_encoded',return $parser->exec($script);'data' => $encoded}));private function _encodeKeywords($script) {return $parser->exec($script);// escape high-ascii values already in the script (i.e. in strings)}if ($this->_encoding > 62)$script = $this->_escape95($script);private function _encodeKeywords($script) {// create the parser// escape high-ascii values already in the script (i.e. in strings)$parser = new ParseMaster();if ($this->_encoding > 62)$encode = $this->_getEncoder($this->_encoding);$script = $this->_escape95($script);// for high-ascii, don't encode single character low-ascii// create the parser$regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';$parser = new ParseMaster();// build the word list$encode = $this->_getEncoder($this->_encoding);$keywords = $this->_analyze($script, $regexp, $encode);// for high-ascii, don't encode single character low-ascii$encoded = $keywords['encoded'];$regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';// build the word list// encode$keywords = $this->_analyze($script, $regexp, $encode);$parser->add($regexp,$encoded = $keywords['encoded'];array('fn' => '_replace_encoded',// encode'data' => $encoded$parser->add($regexp,)array();'fn' => '_replace_encoded',if (empty($script)) return $script;'data' => $encodedelse {)//$res = $parser->exec($script);//$res = $this->_bootStrap($res, $keywords);if (empty($script)) return $script;//return $res;else {return $this->_bootStrap($parser->exec($script), $keywords);//$res = $parser->exec($script);}//$res = $this->_bootStrap($res, $keywords);}//return $res;return $this->_bootStrap($parser->exec($script), $keywords);private function _analyze($script, $regexp, $encode) {}// analyse}// retreive all words in the script$all = array();private function _analyze($script, $regexp, $encode) {preg_match_all($regexp, $script, $all);// analyse$_sorted = array(); // list of words sorted by frequency// retreive all words in the script$_encoded = array(); // dictionary of word->encoding$all = array();$_protected = array(); // instances of "protected" wordspreg_match_all($regexp, $script, $all);$all = $all[0]; // simulate the javascript comportement of global match$_sorted = array(); // list of words sorted by frequencyif (!empty($all)) {$_encoded = array(); // dictionary of word->encoding$unsorted = array(); // same list, not sorted$_protected = array(); // instances of "protected" words$protected = array(); // "protected" words (dictionary of word->"word")$all = $all[0]; // simulate the javascript comportement of global match$value = array(); // dictionary of charCode->encoding (eg. 256->ff)if (!empty($all)) {$this->_count = array(); // word->count$unsorted = array(); // same list, not sorted$i = count($all); $j = 0; //$word = null;$protected = array(); // "protected" words (dictionary of word->"word")// count the occurrences - used for sorting later$value = array(); // dictionary of charCode->encoding (eg. 256->ff)do {$this->_count = array(); // word->count--$i;$i = count($all); $j = 0; //$word = null;$word = '$' . $all[$i];// count the occurrences - used for sorting laterif (!isset($this->_count[$word])) {do {$this->_count[$word] = 0;--$i;$unsorted[$j] = $word;$word = '$' . $all[$i];// make a dictionary of all of the protected words in this scriptif (!isset($this->_count[$word])) {// these are words that might be mistaken for encoding$this->_count[$word] = 0;//if (is_string($encode) && method_exists($this, $encode))$unsorted[$j] = $word;$values[$j] = call_user_func(array(&$this, $encode), $j);// make a dictionary of all of the protected words in this script$protected['$' . $values[$j]] = $j++;// these are words that might be mistaken for encoding}//if (is_string($encode) && method_exists($this, $encode))// increment the word counter$values[$j] = call_user_func(array(&$this, $encode), $j);$this->_count[$word]++;$protected['$' . $values[$j]] = $j++;} while ($i > 0);}// prepare to sort the word list, first we must protect// increment the word counter// words that are also used as codes. we assign them a code$this->_count[$word]++;// equivalent to the word itself.} while ($i > 0);// e.g. if "do" falls within our encoding range// prepare to sort the word list, first we must protect// then we store keywords["do"] = "do";// words that are also used as codes. we assign them a code// this avoids problems when decoding// equivalent to the word itself.$i = count($unsorted);// e.g. if "do" falls within our encoding rangedo {// then we store keywords["do"] = "do";$word = $unsorted[--$i];// this avoids problems when decodingif (isset($protected[$word]) /*!= null*/) {$i = count($unsorted);$_sorted[$protected[$word]] = substr($word, 1);do {$_protected[$protected[$word]] = true;$word = $unsorted[--$i];$this->_count[$word] = 0;if (isset($protected[$word]) /*!= null*/) {}$_sorted[$protected[$word]] = substr($word, 1);} while ($i);$_protected[$protected[$word]] = true;$this->_count[$word] = 0;// sort the words by frequency}// Note: the javascript and php version of sort can be different :} while ($i);// in php manual, usort :// " If two members compare as equal,// sort the words by frequency// their order in the sorted array is undefined."// Note: the javascript and php version of sort can be different :// so the final packed script is different of the Dean's javascript version// in php manual, usort :// but equivalent.// " If two members compare as equal,// the ECMAscript standard does not guarantee this behaviour,// their order in the sorted array is undefined."// and thus not all browsers (e.g. Mozilla versions dating back to at// so the final packed script is different of the Dean's javascript version// least 2003) respect this.// but equivalent.usort($unsorted, array(&$this, '_sortWords'));// the ECMAscript standard does not guarantee this behaviour,$j = 0;// and thus not all browsers (e.g. Mozilla versions dating back to at// because there are "protected" words in the list// least 2003) respect this.// we must add the sorted words around themusort($unsorted, array(&$this, '_sortWords'));do {$j = 0;if (!isset($_sorted[$i]))// because there are "protected" words in the list$_sorted[$i] = substr($unsorted[$j++], 1);// we must add the sorted words around them$_encoded[$_sorted[$i]] = $values[$i];do {} while (++$i < count($unsorted));if (!isset($_sorted[$i]))}$_sorted[$i] = substr($unsorted[$j++], 1);return array($_encoded[$_sorted[$i]] = $values[$i];'sorted' => $_sorted,} while (++$i < count($unsorted));'encoded' => $_encoded,}'protected' => $_protected);return array(}'sorted' => $_sorted,'encoded' => $_encoded,private $_count = array();'protected' => $_protected);private function _sortWords($match1, $match2) {}return $this->_count[$match2] - $this->_count[$match1];}private $_count = array();private function _sortWords($match1, $match2) {// build the boot function used for loading and decodingreturn $this->_count[$match2] - $this->_count[$match1];private function _bootStrap($packed, $keywords) {}$ENCODE = $this->_safeRegExp('$encode\\($count\\)');// build the boot function used for loading and decoding// $packed: the packed scriptprivate function _bootStrap($packed, $keywords) {$packed = "'" . $this->_escape($packed) . "'";$ENCODE = $this->_safeRegExp('$encode\\($count\\)');// $ascii: base for encoding// $packed: the packed script$ascii = min(count($keywords['sorted']), $this->_encoding);$packed = "'" . $this->_escape($packed) . "'";if ($ascii == 0) $ascii = 1;// $ascii: base for encoding// $count: number of words contained in the script$ascii = min(count($keywords['sorted']), $this->_encoding);$count = count($keywords['sorted']);if ($ascii == 0) $ascii = 1;// $keywords: list of words contained in the script// $count: number of words contained in the scriptforeach ($keywords['protected'] as $i=>$value) {$count = count($keywords['sorted']);$keywords['sorted'][$i] = '';}// $keywords: list of words contained in the script// convert from a string to an arrayforeach ($keywords['protected'] as $i=>$value) {ksort($keywords['sorted']);$keywords['sorted'][$i] = '';$keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";}// convert from a string to an array$encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);ksort($keywords['sorted']);$encode = $this->_getJSFunction($encode);$keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";$encode = preg_replace('/_encoding/','$ascii', $encode);$encode = preg_replace('/arguments\\.callee/','$encode', $encode);$encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);$inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');$encode = $this->_getJSFunction($encode);$encode = preg_replace('/_encoding/','$ascii', $encode);// $decode: code snippet to speed up decoding$encode = preg_replace('/arguments\\.callee/','$encode', $encode);if ($this->_fastDecode) {$inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');// create the decoder$decode = $this->_getJSFunction('_decodeBody');// $decode: code snippet to speed up decodingif ($this->_encoding > 62)if ($this->_fastDecode) {$decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);// create the decoder// perform the encoding inline for lower ascii values$decode = $this->_getJSFunction('_decodeBody');elseif ($ascii < 36)if ($this->_encoding > 62)$decode = preg_replace($ENCODE, $inline, $decode);$decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);// special case: when $count==0 there are no keywords. I want to keep// perform the encoding inline for lower ascii values// the basic shape of the unpacking funcion so i'll frig the code...elseif ($ascii < 36)if ($count == 0)$decode = preg_replace($ENCODE, $inline, $decode);$decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);// special case: when $count==0 there are no keywords. I want to keep}// the basic shape of the unpacking funcion so i'll frig the code...if ($count == 0)// boot function$decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);$unpack = $this->_getJSFunction('_unpack');}if ($this->_fastDecode) {// insert the decoder// boot function$this->buffer = $decode;$unpack = $this->_getJSFunction('_unpack');$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);if ($this->_fastDecode) {}// insert the decoder$unpack = preg_replace('/"/', "'", $unpack);$this->buffer = $decode;if ($this->_encoding > 62) { // high-ascii$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);// get rid of the word-boundaries for regexp matches}$unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);$unpack = preg_replace('/"/', "'", $unpack);}if ($this->_encoding > 62) { // high-asciiif ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {// get rid of the word-boundaries for regexp matches// insert the encode function$unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);$this->buffer = $encode;}$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {} else {// insert the encode function// perform the encoding inline$this->buffer = $encode;$unpack = preg_replace($ENCODE, $inline, $unpack);$unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);} else {// pack the boot function too// perform the encoding inline$unpackPacker = new JavaScriptPacker($unpack, 0, false, true);$unpack = preg_replace($ENCODE, $inline, $unpack);$unpack = $unpackPacker->pack();}// pack the boot function too// arguments$unpackPacker = new JavaScriptPacker($unpack, 0, false, true);$params = array($packed, $ascii, $count, $keywords);$unpack = $unpackPacker->pack();if ($this->_fastDecode) {$params[] = 0;// arguments$params[] = '{}';$params = array($packed, $ascii, $count, $keywords);}if ($this->_fastDecode) {$params = implode(',', $params);$params[] = 0;$params[] = '{}';// the whole thing}return 'eval(' . $unpack . '(' . $params . "))\n";$params = implode(',', $params);}// the whole thingprivate $buffer;return 'eval(' . $unpack . '(' . $params . "))\n";private function _insertFastDecode($match) {}return '{' . $this->buffer . ';';}private $buffer;private function _insertFastEncode($match) {private function _insertFastDecode($match) {return '{$encode=' . $this->buffer . ';';return '{' . $this->buffer . ';';private function _insertFastEncode($match) {// mmm.. ..which one do i need ??return '{$encode=' . $this->buffer . ';';private function _getEncoder($ascii) {}return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?'_encode95' : '_encode62' : '_encode36' : '_encode10';// mmm.. ..which one do i need ??}private function _getEncoder($ascii) {return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?// zero encoding'_encode95' : '_encode62' : '_encode36' : '_encode10';// characters: 0123456789}private function _encode10($charCode) {return $charCode;// zero encoding}// characters: 0123456789private function _encode10($charCode) {// inherent base36 supportreturn $charCode;// characters: 0123456789abcdefghijklmnopqrstuvwxyz}private function _encode36($charCode) {return base_convert($charCode, 10, 36);// inherent base36 support}// characters: 0123456789abcdefghijklmnopqrstuvwxyzprivate function _encode36($charCode) {// hitch a ride on base36 and add the upper case alpha charactersreturn base_convert($charCode, 10, 36);// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}private function _encode62($charCode) {$res = '';// hitch a ride on base36 and add the upper case alpha charactersif ($charCode >= $this->_encoding) {// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$res = $this->_encode62((int)($charCode / $this->_encoding));private function _encode62($charCode) {}$res = '';$charCode = $charCode % $this->_encoding;if ($charCode >= $this->_encoding) {$res = $this->_encode62((int)($charCode / $this->_encoding));if ($charCode > 35)}return $res . chr($charCode + 29);$charCode = $charCode % $this->_encoding;elsereturn $res . base_convert($charCode, 10, 36);if ($charCode > 35)}return $res . chr($charCode + 29);else// use high-ascii valuesreturn $res . base_convert($charCode, 10, 36);// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ}private function _encode95($charCode) {$res = '';// use high-ascii valuesif ($charCode >= $this->_encoding)// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ$res = $this->_encode95($charCode / $this->_encoding);private function _encode95($charCode) {$res = '';return $res . chr(($charCode % $this->_encoding) + 161);if ($charCode >= $this->_encoding)}$res = $this->_encode95($charCode / $this->_encoding);private function _safeRegExp($string) {return $res . chr(($charCode % $this->_encoding) + 161);return '/'.preg_replace('/\$/', '\\\$', $string).'/';}}private function _safeRegExp($string) {private function _encodePrivate($charCode) {return '/'.preg_replace('/\$/', '\\\$', $string).'/';return "_" . $charCode;}}private function _encodePrivate($charCode) {// protect characters used by the parserreturn "_" . $charCode;private function _escape($script) {}return preg_replace('/([\\\\\'])/', '\\\$1', $script);}// protect characters used by the parserprivate function _escape($script) {// protect high-ascii characters already in the scriptreturn preg_replace('/([\\\\\'])/', '\\\$1', $script);private function _escape95($script) {}return preg_replace_callback('/[\\xa1-\\xff]/',// protect high-ascii characters already in the scriptarray(&$this, '_escape95Bis'),private function _escape95($script) {$scriptreturn preg_replace_callback();'/[\\xa1-\\xff]/',}array(&$this, '_escape95Bis'),private function _escape95Bis($match) {$scriptreturn '\x'.((string)dechex(ord($match)));private function _escape95Bis($match) {return '\x'.((string)dechex(ord($match)));private function _getJSFunction($aName) {}if (defined('self::JSFUNCTION'.$aName))return constant('self::JSFUNCTION'.$aName);elseprivate function _getJSFunction($aName) {return '';if (defined('self::JSFUNCTION'.$aName))}return constant('self::JSFUNCTION'.$aName);else// JavaScript Functions used.return '';// Note : In Dean's version, these functions are converted}// with 'String(aFunctionName);'.// This internal conversion complete the original code, ex :// JavaScript Functions used.// 'while (aBool) anAction();' is converted to// Note : In Dean's version, these functions are converted// 'while (aBool) { anAction(); }'.// with 'String(aFunctionName);'.// The JavaScript functions below are corrected.// This internal conversion complete the original code, ex :// 'while (aBool) anAction();' is converted to// unpacking function - this is the boot strap function// 'while (aBool) { anAction(); }'.// data extracted from this packing routine is passed to// The JavaScript functions below are corrected.// this function when decoded in the target// NOTE ! : without the ';' final.// unpacking function - this is the boot strap functionconst JSFUNCTION_unpack =// data extracted from this packing routine is passed to// this function when decoded in the target'function($packed, $ascii, $count, $keywords, $encode, $decode) {// NOTE ! : without the ';' final.while ($count--) {const JSFUNCTION_unpack =if ($keywords[$count]) {$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);'function($packed, $ascii, $count, $keywords, $encode, $decode) {}while ($count--) {}if ($keywords[$count]) {return $packed;$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);}';}/*}'function($packed, $ascii, $count, $keywords, $encode, $decode) {return $packed;while ($count--)}';if ($keywords[$count])/*$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);'function($packed, $ascii, $count, $keywords, $encode, $decode) {return $packed;while ($count--)}';if ($keywords[$count])*/$packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);return $packed;// code-snippet inserted into the unpacker to speed up decoding}';const JSFUNCTION_decodeBody =*///_decode = function() {// does the browser support String.replace where the// code-snippet inserted into the unpacker to speed up decoding// replacement value is a function?const JSFUNCTION_decodeBody =//_decode = function() {' if (!\'\'.replace(/^/, String)) {// does the browser support String.replace where the// decode all the values we need// replacement value is a function?while ($count--) {$decode[$encode($count)] = $keywords[$count] || $encode($count);' if (!\'\'.replace(/^/, String)) {}// decode all the values we need// global replacement functionwhile ($count--) {$keywords = [function ($encoded) {return $decode[$encoded]}];$decode[$encode($count)] = $keywords[$count] || $encode($count);// generic match}$encode = function () {return \'\\\\w+\'};// global replacement function// reset the loop counter - we are now doing a global replace$keywords = [function ($encoded) {return $decode[$encoded]}];$count = 1;// generic match}$encode = function () {return \'\\\\w+\'};';// reset the loop counter - we are now doing a global replace//};$count = 1;/*}' if (!\'\'.replace(/^/, String)) {';// decode all the values we need//};while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);/*// global replacement function' if (!\'\'.replace(/^/, String)) {$keywords = [function ($encoded) {return $decode[$encoded]}];// decode all the values we need// generic matchwhile ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);$encode = function () {return\'\\\\w+\'};// global replacement function// reset the loop counter - we are now doing a global replace$keywords = [function ($encoded) {return $decode[$encoded]}];$count = 1;// generic match}';$encode = function () {return\'\\\\w+\'};*/// reset the loop counter - we are now doing a global replace$count = 1;// zero encoding}';// characters: 0123456789*/const JSFUNCTION_encode10 ='function($charCode) {// zero encodingreturn $charCode;// characters: 0123456789}';//;';const JSFUNCTION_encode10 ='function($charCode) {// inherent base36 supportreturn $charCode;// characters: 0123456789abcdefghijklmnopqrstuvwxyz}';//;';const JSFUNCTION_encode36 ='function($charCode) {// inherent base36 supportreturn $charCode.toString(36);// characters: 0123456789abcdefghijklmnopqrstuvwxyz}';//;';const JSFUNCTION_encode36 ='function($charCode) {// hitch a ride on base36 and add the upper case alpha charactersreturn $charCode.toString(36);// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}';//;';const JSFUNCTION_encode62 ='function($charCode) {// hitch a ride on base36 and add the upper case alpha charactersreturn ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +// characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));const JSFUNCTION_encode62 =}';'function($charCode) {return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +// use high-ascii values(($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ}';const JSFUNCTION_encode95 ='function($charCode) {// use high-ascii valuesreturn ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +// characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþString.fromCharCode($charCode % _encoding + 161);const JSFUNCTION_encode95 =}';'function($charCode) {return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +}String.fromCharCode($charCode % _encoding + 161);}';class ParseMaster {}public $ignoreCase = false;public $escapeChar = '';class ParseMaster {// constantspublic $ignoreCase = false;const EXPRESSION = 0;public $escapeChar = '';const REPLACEMENT = 1;const LENGTH = 2;// constantsconst EXPRESSION = 0;// used to determine nesting levelsconst REPLACEMENT = 1;private $GROUPS = '/\\(/';//gconst LENGTH = 2;private $SUB_REPLACE = '/\\$\\d/';private $INDEXED = '/^\\$\\d+$/';// used to determine nesting levelsprivate $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';private $GROUPS = '/\\(/';//gprivate $ESCAPE = '/\\\./';//gprivate $SUB_REPLACE = '/\\$\\d/';private $QUOTE = '/\'/';private $INDEXED = '/^\\$\\d+$/';private $DELETED = '/\\x01[^\\x01]*\\x01/';//gprivate $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';private $ESCAPE = '/\\\./';//gpublic function add($expression, $replacement = '') {private $QUOTE = '/\'/';// count the number of sub-expressionsprivate $DELETED = '/\\x01[^\\x01]*\\x01/';//g// - add one because each pattern is itself a sub-expression$length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out);public function add($expression, $replacement = '') {// count the number of sub-expressions// treat only strings $replacement// - add one because each pattern is itself a sub-expressionif (is_string($replacement)) {$length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out);// does the pattern deal with sub-expressions?if (preg_match($this->SUB_REPLACE, $replacement)) {// treat only strings $replacement// a simple lookup? (e.g. "$2")if (is_string($replacement)) {if (preg_match($this->INDEXED, $replacement)) {// does the pattern deal with sub-expressions?// store the index (used for fast retrieval of matched strings)if (preg_match($this->SUB_REPLACE, $replacement)) {$replacement = (int)(substr($replacement, 1)) - 1;// a simple lookup? (e.g. "$2")} else { // a complicated lookup (e.g. "Hello $2 $1")if (preg_match($this->INDEXED, $replacement)) {// build a function to do the lookup// store the index (used for fast retrieval of matched strings)$quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))$replacement = (int)(substr($replacement, 1)) - 1;? '"' : "'";} else { // a complicated lookup (e.g. "Hello $2 $1")$replacement = array(// build a function to do the lookup'fn' => '_backReferences',$quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))'data' => array(? '"' : "'";'replacement' => $replacement,$replacement = array('length' => $length,'fn' => '_backReferences','quote' => $quote'data' => array()'replacement' => $replacement,);'length' => $length,}'quote' => $quote})});// pass the modified arguments}if (!empty($expression)) $this->_add($expression, $replacement, $length);}else $this->_add('/^$/', $replacement, $length);}}// pass the modified argumentsif (!empty($expression)) $this->_add($expression, $replacement, $length);public function exec($string) {else $this->_add('/^$/', $replacement, $length);// execute the global replacement}$this->_escaped = array();public function exec($string) {// simulate the _patterns.toSTring of Dean// execute the global replacement$regexp = '/';$this->_escaped = array();foreach ($this->_patterns as $reg) {$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';// simulate the _patterns.toSTring of Dean}$regexp = '/';$regexp = substr($regexp, 0, -1) . '/';foreach ($this->_patterns as $reg) {$regexp .= ($this->ignoreCase) ? 'i' : '';$regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';}$string = $this->_escape($string, $this->escapeChar);$regexp = substr($regexp, 0, -1) . '/';$string = preg_replace_callback($regexp .= ($this->ignoreCase) ? 'i' : '';$regexp,array($string = $this->_escape($string, $this->escapeChar);&$this,$string = preg_replace_callback('_replacement'$regexp,),array($string&$this,);'_replacement'$string = $this->_unescape($string, $this->escapeChar);),$stringreturn preg_replace($this->DELETED, '', $string);}$string = $this->_unescape($string, $this->escapeChar);public function reset() {return preg_replace($this->DELETED, '', $string);// clear the patterns collection so that this object may be re-used}$this->_patterns = array();}public function reset() {// clear the patterns collection so that this object may be re-used// private$this->_patterns = array();private $_escaped = array(); // escaped characters}private $_patterns = array(); // patterns stored by index// private// create and add a new pattern to the patterns collectionprivate $_escaped = array(); // escaped charactersprivate function _add() {private $_patterns = array(); // patterns stored by index$arguments = func_get_args();$this->_patterns[] = $arguments;// create and add a new pattern to the patterns collection}private function _add() {$arguments = func_get_args();// this is the global replace function (it's quite complicated)$this->_patterns[] = $arguments;private function _replacement($arguments) {}if (empty($arguments)) return '';// this is the global replace function (it's quite complicated)$i = 1; $j = 0;private function _replacement($arguments) {// loop through the patternsif (empty($arguments)) return '';while (isset($this->_patterns[$j])) {$pattern = $this->_patterns[$j++];$i = 1; $j = 0;// do we have a result?// loop through the patternsif (isset($arguments[$i]) && ($arguments[$i] != '')) {while (isset($this->_patterns[$j])) {$replacement = $pattern[self::REPLACEMENT];$pattern = $this->_patterns[$j++];// do we have a result?if (is_array($replacement) && isset($replacement['fn'])) {if (isset($arguments[$i]) && ($arguments[$i] != '')) {$replacement = $pattern[self::REPLACEMENT];if (isset($replacement['data'])) $this->buffer = $replacement['data'];return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);if (is_array($replacement) && isset($replacement['fn'])) {} elseif (is_int($replacement)) {if (isset($replacement['data'])) $this->buffer = $replacement['data'];return $arguments[$replacement + $i];return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);} elseif (is_int($replacement)) {$delete = ($this->escapeChar == '' ||return $arguments[$replacement + $i];strpos($arguments[$i], $this->escapeChar) === false)? '' : "\x01" . $arguments[$i] . "\x01";}return $delete . $replacement;$delete = ($this->escapeChar == '' ||strpos($arguments[$i], $this->escapeChar) === false)// skip over references to sub-expressions? '' : "\x01" . $arguments[$i] . "\x01";} else {return $delete . $replacement;$i += $pattern[self::LENGTH];}// skip over references to sub-expressions}} else {}$i += $pattern[self::LENGTH];}private function _backReferences($match, $offset) {}$replacement = $this->buffer['replacement'];}$quote = $this->buffer['quote'];$i = $this->buffer['length'];private function _backReferences($match, $offset) {while ($i) {$replacement = $this->buffer['replacement'];$replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);$quote = $this->buffer['quote'];}$i = $this->buffer['length'];return $replacement;while ($i) {}$replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);}private function _replace_name($match, $offset){return $replacement;$length = strlen($match[$offset + 2]);}$start = $length - max($length - strlen($match[$offset + 3]), 0);return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];private function _replace_name($match, $offset){}$length = strlen($match[$offset + 2]);$start = $length - max($length - strlen($match[$offset + 3]), 0);private function _replace_encoded($match, $offset) {return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];return $this->buffer[$match[$offset]];}}private function _replace_encoded($match, $offset) {return $this->buffer[$match[$offset]];// php : we cannot pass additional data to preg_replace_callback,}// and we cannot use &$this in create_function, so let's go to lower levelprivate $buffer;// php : we cannot pass additional data to preg_replace_callback,// encode escaped characters// and we cannot use &$this in create_function, so let's go to lower levelprivate function _escape($string, $escapeChar) {private $buffer;if ($escapeChar) {$this->buffer = $escapeChar;// encode escaped charactersreturn preg_replace_callback(private function _escape($string, $escapeChar) {'/\\' . $escapeChar . '(.)' .'/',if ($escapeChar) {array(&$this, '_escapeBis'),$this->buffer = $escapeChar;$stringreturn preg_replace_callback();'/\\' . $escapeChar . '(.)' .'/',array(&$this, '_escapeBis'),} else {$stringreturn $string;);}}} else {private function _escapeBis($match) {return $string;$this->_escaped[] = $match[1];}return $this->buffer;}}private function _escapeBis($match) {$this->_escaped[] = $match[1];// decode escaped charactersreturn $this->buffer;private function _unescape($string, $escapeChar) {}if ($escapeChar) {$regexp = '/'.'\\'.$escapeChar.'/';// decode escaped characters$this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);private function _unescape($string, $escapeChar) {return preg_replace_callbackif ($escapeChar) {($regexp = '/'.'\\'.$escapeChar.'/';$regexp,$this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);array(&$this, '_unescapeBis'),return preg_replace_callback$string();$regexp,array(&$this, '_unescapeBis'),} else {$stringreturn $string;);}}} else {private function _unescapeBis() {return $string;if (!empty($this->_escaped[$this->buffer['i']])) {}$temp = $this->_escaped[$this->buffer['i']];}} else {private function _unescapeBis() {$temp = '';if (isset($this->_escaped[$this->buffer['i']])}&& $this->_escaped[$this->buffer['i']] != '')$this->buffer['i']++;{return $this->buffer['escapeChar'] . $temp;$temp = $this->_escaped[$this->buffer['i']];}} else {$temp = '';private function _internalEscape($string) {}return preg_replace($this->ESCAPE, '', $string);$this->buffer['i']++;}return $this->buffer['escapeChar'] . $temp;}?>private function _internalEscape($string) {return preg_replace($this->ESCAPE, '', $string);}}?>