2 /* 7 December 2006. version 1.0
4 * This is the php version of the Dean Edwards JavaScript 's Packer,
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
12 * packer, version 2.0.2 (2005-08-19) Copyright 2004-2005, Dean Edwards
14 * License: http://creativecommons.org/licenses/LGPL/2.1/
16 * Ported to PHP by Nicolas Martin.
17 * modified by Mark Fabrizio Jr. to work with php 4
19 * ----------------------------------------------------------------------
22 * $myPacker = new JavaScriptPacker($script, 62, true, false);
23 * $packed = $myPacker->pack();
27 * $myPacker = new JavaScriptPacker($script, 'Normal', true, false);
28 * $packed = $myPacker->pack();
32 * $myPacker = new JavaScriptPacker($script);
33 * $packed = $myPacker->pack();
36 * params of the constructor :
37 * $script: the JavaScript to pack, string.
38 * $encoding: level of encoding, int or string :
39 * 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'.
41 * $fastDecode: include the fast decoder in the packed result, boolean.
43 * $specialChars: if you are flagged your private and local variables
44 * in the script, boolean.
47 * The pack() method return the compressed JavasScript, as a string.
49 * see http://dean.edwards.name/packer/usage/ for more information.
52 * # [del]need PHP 5 . Tested with PHP 5.1.2[/del]
53 * this is a modified version for PHP 4
55 * # The packed result may be different than with the Dean Edwards
56 * version, but with the same length. The reason is that the PHP
57 * function usort to sort array don't necessarily preserve the
58 * original order of two equal member. The Javascript sort function
59 * in fact preserve this order (but that's not require by the
60 * ECMAScript standard). So the encoded keywords order can be
61 * different in the two results.
63 * # Be careful with the 'High ASCII' Level encoding if you use
64 * UTF-8 in your files...
68 * modified by Mark Fabrizio Jr. to work with php 4
72 class JavaScriptPacker
{
75 // validate parameters
78 var $_fastDecode = true;
79 var $_specialChars = false;
81 var $LITERAL_ENCODING = array(
88 // http://doc.spip.org/@JavaScriptPacker
89 function JavaScriptPacker($_script, $_encoding = 62, $_fastDecode = true, $_specialChars = false)
91 $this->_script
= $_script . "\n";
92 if (array_key_exists($_encoding, $this->LITERAL_ENCODING
))
93 $_encoding = $this->LITERAL_ENCODING
[$_encoding];
94 $this->_encoding
= min((int)$_encoding, 95);
95 $this->_fastDecode
= $_fastDecode;
96 $this->_specialChars
= $_specialChars;
99 // http://doc.spip.org/@pack
101 $this->_addParser('_basicCompression');
102 if ($this->_specialChars
)
103 $this->_addParser('_encodeSpecialChars');
104 if ($this->_encoding
)
105 $this->_addParser('_encodeKeywords');
108 return $this->_pack($this->_script
);
111 // apply all parsing routines
112 // http://doc.spip.org/@_pack
113 function _pack($script) {
114 for ($i = 0; isset($this->_parsers
[$i]); $i++
) {
115 $script = call_user_func(array(&$this,$this->_parsers
[$i]), $script);
120 // keep a list of parsing functions, they'll be executed all at once
121 var $_parsers = array();
122 // http://doc.spip.org/@_addParser
123 function _addParser($parser) {
124 $this->_parsers
[] = $parser;
127 // zero encoding - just removal of white space and comments
128 // http://doc.spip.org/@_basicCompression
129 function _basicCompression($script) {
130 $parser = new ParseMaster();
132 $parser->escapeChar
= '\\';
134 $parser->add('/\'[^\'\\n\\r]*\'/',$this->IGNORE
);
135 $parser->add('/"[^"\\n\\r]*"/', $this->IGNORE
);
137 $parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
138 $parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
139 // protect regular expressions
140 $parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
141 $parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', $this->IGNORE
);
142 // remove: ;;; doSomething();
143 if ($this->_specialChars
) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
144 // remove redundant semi-colons
145 $parser->add('/\\(;;\\)/', $this->IGNORE
); // protect for (;;) loops
146 $parser->add('/;+\\s*([};])/', '$2');
148 $script = $parser->exec($script);
150 // remove white-space
151 # $parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
152 # $parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
153 # $parser->add('/\\s+/', '');
154 # Modif fil@rezo.net pour conserver les \n
155 $parser->add('/(\\b|\\x24)[\\t ]+(\\b|\\x24)/', '$2 $3');
156 $parser->add('/([+\\-])[\\t ]+([+\\-])/', '$2 $3');
157 $parser->add('/[\\t ]+/', '');
158 $parser->add('/\\s+/', "\n");
160 return $parser->exec($script);
163 // http://doc.spip.org/@_encodeSpecialChars
164 function _encodeSpecialChars($script) {
165 $parser = new ParseMaster();
166 // replace: $name -> n, $$name -> na
167 $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
168 array('fn' => '_replace_name')
170 // replace: _name -> _0, double-underscore (__name) is ignored
171 $regexp = '/\\b_[A-Za-z\\d]\\w*/';
172 // build the word list
173 $keywords = $this->_analyze($script, $regexp, '_encodePrivate');
175 $encoded = $keywords['encoded'];
177 $parser->add($regexp,
179 'fn' => '_replace_encoded',
183 return $parser->exec($script);
186 // http://doc.spip.org/@_encodeKeywords
187 function _encodeKeywords($script) {
188 // escape high-ascii values already in the script (i.e. in strings)
189 if ($this->_encoding
> 62)
190 $script = $this->_escape95($script);
192 $parser = new ParseMaster();
193 $encode = $this->_getEncoder($this->_encoding
);
194 // for high-ascii, don't encode single character low-ascii
195 $regexp = ($this->_encoding
> 62) ?
'/\\w\\w+/' : '/\\w+/';
196 // build the word list
197 $keywords = $this->_analyze($script, $regexp, $encode);
198 $encoded = $keywords['encoded'];
201 $parser->add($regexp,
203 'fn' => '_replace_encoded',
207 if (empty($script)) return $script;
209 //$res = $parser->exec($script);
210 //$res = $this->_bootStrap($res, $keywords);
212 return $this->_bootStrap($parser->exec($script), $keywords);
216 // http://doc.spip.org/@_analyze
217 function _analyze($script, $regexp, $encode) {
219 // retreive all words in the script
221 preg_match_all($regexp, $script, $all);
222 $_sorted = array(); // list of words sorted by frequency
223 $_encoded = array(); // dictionary of word->encoding
224 $_protected = array(); // instances of "protected" words
225 $all = $all[0]; // simulate the javascript comportement of global match
227 $unsorted = array(); // same list, not sorted
228 $protected = array(); // "protected" words (dictionary of word->"word")
229 $value = array(); // dictionary of charCode->encoding (eg. 256->ff)
230 $this->_count
= array(); // word->count
231 $i = count($all); $j = 0; //$word = null;
232 // count the occurrences - used for sorting later
235 $word = '$' . $all[$i];
236 if (!isset($this->_count
[$word])) {
237 $this->_count
[$word] = 0;
238 $unsorted[$j] = $word;
239 // make a dictionary of all of the protected words in this script
240 // these are words that might be mistaken for encoding
241 //if (is_string($encode) && method_exists($this, $encode))
242 $values[$j] = call_user_func(array(&$this, $encode), $j);
243 $protected['$' . $values[$j]] = $j++
;
245 // increment the word counter
246 $this->_count
[$word]++
;
248 // prepare to sort the word list, first we must protect
249 // words that are also used as codes. we assign them a code
250 // equivalent to the word itself.
251 // e.g. if "do" falls within our encoding range
252 // then we store keywords["do"] = "do";
253 // this avoids problems when decoding
254 $i = count($unsorted);
256 $word = $unsorted[--$i];
257 if (isset($protected[$word]) /*!= null*/) {
258 $_sorted[$protected[$word]] = substr($word, 1);
259 $_protected[$protected[$word]] = true;
260 $this->_count
[$word] = 0;
264 // sort the words by frequency
265 // Note: the javascript and php version of sort can be different :
266 // in php manual, usort :
267 // " If two members compare as equal,
268 // their order in the sorted array is undefined."
269 // so the final packed script is different of the Dean's javascript version
271 // the ECMAscript standard does not guarantee this behaviour,
272 // and thus not all browsers (e.g. Mozilla versions dating back to at
273 // least 2003) respect this.
274 usort($unsorted, array(&$this, '_sortWords'));
276 // because there are "protected" words in the list
277 // we must add the sorted words around them
279 if (!isset($_sorted[$i]))
280 $_sorted[$i] = substr($unsorted[$j++
], 1);
281 $_encoded[$_sorted[$i]] = $values[$i];
282 } while (++
$i < count($unsorted));
285 'sorted' => $_sorted,
286 'encoded' => $_encoded,
287 'protected' => $_protected);
290 var $_count = array();
291 // http://doc.spip.org/@_sortWords
292 function _sortWords($match1, $match2) {
293 return $this->_count
[$match2] - $this->_count
[$match1];
296 // build the boot function used for loading and decoding
297 // http://doc.spip.org/@_bootStrap
298 function _bootStrap($packed, $keywords) {
299 $ENCODE = $this->_safeRegExp('$encode\\($count\\)');
301 // $packed: the packed script
302 $packed = "'" . $this->_escape($packed) . "'";
304 // $ascii: base for encoding
305 $ascii = min(count($keywords['sorted']), $this->_encoding
);
306 if ($ascii == 0) $ascii = 1;
308 // $count: number of words contained in the script
309 $count = count($keywords['sorted']);
311 // $keywords: list of words contained in the script
312 foreach ($keywords['protected'] as $i=>$value) {
313 $keywords['sorted'][$i] = '';
315 // convert from a string to an array
316 ksort($keywords['sorted']);
317 $keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";
319 $encode = ($this->_encoding
> 62) ?
'_encode95' : $this->_getEncoder($ascii);
320 $encode = $this->_getJSFunction($encode);
321 $encode = preg_replace('/_encoding/','$ascii', $encode);
322 $encode = preg_replace('/arguments\\.callee/','$encode', $encode);
323 $inline = '\\$count' . ($ascii > 10 ?
'.toString(\\$ascii)' : '');
325 // $decode: code snippet to speed up decoding
326 if ($this->_fastDecode
) {
327 // create the decoder
328 $decode = $this->_getJSFunction('_decodeBody');
329 if ($this->_encoding
> 62)
330 $decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
331 // perform the encoding inline for lower ascii values
333 $decode = preg_replace($ENCODE, $inline, $decode);
334 // special case: when $count==0 there are no keywords. I want to keep
335 // the basic shape of the unpacking funcion so i'll frig the code...
337 $decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
341 $unpack = $this->_getJSFunction('_unpack');
342 if ($this->_fastDecode
) {
343 // insert the decoder
344 $this->buffer
= $decode;
345 $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
347 $unpack = preg_replace('/"/', "'", $unpack);
348 if ($this->_encoding
> 62) { // high-ascii
349 // get rid of the word-boundaries for regexp matches
350 $unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
352 if ($ascii > 36 ||
$this->_encoding
> 62 ||
$this->_fastDecode
) {
353 // insert the encode function
354 $this->buffer
= $encode;
355 $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
357 // perform the encoding inline
358 $unpack = preg_replace($ENCODE, $inline, $unpack);
360 // pack the boot function too
361 $unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
362 $unpack = $unpackPacker->pack();
365 $params = array($packed, $ascii, $count, $keywords);
366 if ($this->_fastDecode
) {
370 $params = implode(',', $params);
373 return 'eval(' . $unpack . '(' . $params . "))\n";
377 // http://doc.spip.org/@_insertFastDecode
378 function _insertFastDecode($match) {
379 return '{' . $this->buffer
. ';';
381 // http://doc.spip.org/@_insertFastEncode
382 function _insertFastEncode($match) {
383 return '{$encode=' . $this->buffer
. ';';
386 // mmm.. ..which one do i need ??
387 // http://doc.spip.org/@_getEncoder
388 function _getEncoder($ascii) {
389 return $ascii > 10 ?
$ascii > 36 ?
$ascii > 62 ?
390 '_encode95' : '_encode62' : '_encode36' : '_encode10';
394 // characters: 0123456789
395 // http://doc.spip.org/@_encode10
396 function _encode10($charCode) {
400 // inherent base36 support
401 // characters: 0123456789abcdefghijklmnopqrstuvwxyz
402 // http://doc.spip.org/@_encode36
403 function _encode36($charCode) {
404 return base_convert($charCode, 10, 36);
407 // hitch a ride on base36 and add the upper case alpha characters
408 // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
409 // http://doc.spip.org/@_encode62
410 function _encode62($charCode) {
412 if ($charCode >= $this->_encoding
) {
413 $res = $this->_encode62((int)($charCode / $this->_encoding
));
415 $charCode = $charCode %
$this->_encoding
;
418 return $res . chr($charCode +
29);
420 return $res . base_convert($charCode, 10, 36);
423 // use high-ascii values
424 // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
425 // http://doc.spip.org/@_encode95
426 function _encode95($charCode) {
428 if ($charCode >= $this->_encoding
)
429 $res = $this->_encode95($charCode / $this->_encoding
);
431 return $res . chr(($charCode %
$this->_encoding
) +
161);
434 // http://doc.spip.org/@_safeRegExp
435 function _safeRegExp($string) {
436 return '/'.preg_replace('/\$/', '\\\$', $string).'/';
439 // http://doc.spip.org/@_encodePrivate
440 function _encodePrivate($charCode) {
441 return "_" . $charCode;
444 // protect characters used by the parser
445 // http://doc.spip.org/@_escape
446 function _escape($script) {
447 return preg_replace('/([\\\\\'])/', '\\\$1', $script);
450 // protect high-ascii characters already in the script
451 // http://doc.spip.org/@_escape95
452 function _escape95($script) {
453 return preg_replace_callback(
455 array(&$this, '_escape95Bis'),
459 // http://doc.spip.org/@_escape95Bis
460 function _escape95Bis($match) {
461 return '\x'.((string)dechex(ord($match)));
465 // http://doc.spip.org/@_getJSFunction
466 function _getJSFunction($aName) {
467 $func = 'JSFUNCTION'.$aName;
468 if (isset($this->$func)){
475 // JavaScript Functions used.
476 // Note : In Dean's version, these functions are converted
477 // with 'String(aFunctionName);'.
478 // This internal conversion complete the original code, ex :
479 // 'while (aBool) anAction();' is converted to
480 // 'while (aBool) { anAction(); }'.
481 // The JavaScript functions below are corrected.
483 // unpacking function - this is the boot strap function
484 // data extracted from this packing routine is passed to
485 // this function when decoded in the target
486 // NOTE ! : without the ';' final.
487 var $JSFUNCTION_unpack = 'function($packed, $ascii, $count, $keywords, $encode, $decode) {
489 if ($keywords[$count]) {
490 $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
496 'function($packed, $ascii, $count, $keywords, $encode, $decode) {
498 if ($keywords[$count])
499 $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
504 // code-snippet inserted into the unpacker to speed up decoding
505 var $JSFUNCTION_decodeBody = ' if (!\'\'.replace(/^/, String)) {
506 // decode all the values we need
508 $decode[$encode($count)] = $keywords[$count] || $encode($count);
510 // global replacement function
511 $keywords = [function ($encoded) {return $decode[$encoded]}];
513 $encode = function () {return \'\\\\w+\'};
514 // reset the loop counter - we are now doing a global replace
520 ' if (!\'\'.replace(/^/, String)) {
521 // decode all the values we need
522 while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
523 // global replacement function
524 $keywords = [function ($encoded) {return $decode[$encoded]}];
526 $encode = function () {return\'\\\\w+\'};
527 // reset the loop counter - we are now doing a global replace
533 // characters: 0123456789
534 var $JSFUNCTION_encode10 = 'function($charCode) {
538 // inherent base36 support
539 // characters: 0123456789abcdefghijklmnopqrstuvwxyz
540 var $JSFUNCTION_encode36 = 'function($charCode) {
541 return $charCode.toString(36);
544 // hitch a ride on base36 and add the upper case alpha characters
545 // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
546 var $JSFUNCTION_encode62 = 'function($charCode) {
547 return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
548 (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
551 // use high-ascii values
552 // characters: ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿À�?ÂÃÄÅÆÇÈÉÊËÌ�?Î�?�?ÑÒÓÔÕÖ×ØÙÚÛÜ�?Þßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ
553 var $JSFUNCTION_encode95 = 'function($charCode) {
554 return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
555 String.fromCharCode($charCode % _encoding + 161);
562 var $ignoreCase = false;
563 var $escapeChar = '';
567 var $REPLACEMENT = 1;
570 // used to determine nesting levels
571 var $GROUPS = '/\\(/';//g
572 var $SUB_REPLACE = '/\\$\\d/';
573 var $INDEXED = '/^\\$\\d+$/';
574 var $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
575 var $ESCAPE = '/\\\./';//g
577 var $DELETED = '/\\x01[^\\x01]*\\x01/';//g
579 // http://doc.spip.org/@add
580 function add($expression, $replacement = '') {
581 // count the number of sub-expressions
582 // - add one because each pattern is itself a sub-expression
583 $length = 1 +
preg_match_all($this->GROUPS
, $this->_internalEscape((string)$expression), $out);
585 // treat only strings $replacement
586 if (is_string($replacement)) {
587 // does the pattern deal with sub-expressions?
588 if (preg_match($this->SUB_REPLACE
, $replacement)) {
589 // a simple lookup? (e.g. "$2")
590 if (preg_match($this->INDEXED
, $replacement)) {
591 // store the index (used for fast retrieval of matched strings)
592 $replacement = (int)(substr($replacement, 1)) - 1;
593 } else { // a complicated lookup (e.g. "Hello $2 $1")
594 // build a function to do the lookup
595 $quote = preg_match($this->QUOTE
, $this->_internalEscape($replacement))
597 $replacement = array(
598 'fn' => '_backReferences',
600 'replacement' => $replacement,
608 // pass the modified arguments
609 if (!empty($expression)) $this->_add($expression, $replacement, $length);
610 else $this->_add('/^$/', $replacement, $length);
613 // http://doc.spip.org/@exec
614 function exec($string) {
615 // execute the global replacement
616 $this->_escaped
= array();
618 // simulate the _patterns.toSTring of Dean
620 foreach ($this->_patterns
as $reg) {
621 $regexp .= '(' . substr($reg[$this->EXPRESSION
], 1, -1) . ')|';
623 $regexp = substr($regexp, 0, -1) . '/';
624 $regexp .= ($this->ignoreCase
) ?
'i' : '';
626 $string = $this->_escape($string, $this->escapeChar
);
627 $string = preg_replace_callback(
635 $string = $this->_unescape($string, $this->escapeChar
);
637 return preg_replace($this->DELETED
, '', $string);
640 // http://doc.spip.org/@reset
642 // clear the patterns collection so that this object may be re-used
643 $this->_patterns
= array();
647 var $_escaped = array(); // escaped characters
648 var $_patterns = array(); // patterns stored by index
650 // create and add a new pattern to the patterns collection
651 // http://doc.spip.org/@_add
653 $arguments = func_get_args();
654 $this->_patterns
[] = $arguments;
657 // this is the global replace function (it's quite complicated)
658 // http://doc.spip.org/@_replacement
659 function _replacement($arguments) {
660 if (empty($arguments)) return '';
663 // loop through the patterns
664 while (isset($this->_patterns
[$j])) {
665 $pattern = $this->_patterns
[$j++
];
666 // do we have a result?
667 if (isset($arguments[$i]) && ($arguments[$i] != '')) {
668 $replacement = $pattern[$this->REPLACEMENT
];
670 if (is_array($replacement) && isset($replacement['fn'])) {
672 if (isset($replacement['data'])) $this->buffer
= $replacement['data'];
673 return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
675 } elseif (is_int($replacement)) {
676 return $arguments[$replacement +
$i];
679 $delete = ($this->escapeChar
== '' ||
680 strpos($arguments[$i], $this->escapeChar
) === false)
681 ?
'' : "\x01" . $arguments[$i] . "\x01";
682 return $delete . $replacement;
684 // skip over references to sub-expressions
686 $i +
= $pattern[$this->LENGTH
];
691 // http://doc.spip.org/@_backReferences
692 function _backReferences($match, $offset) {
693 $replacement = $this->buffer
['replacement'];
694 $quote = $this->buffer
['quote'];
695 $i = $this->buffer
['length'];
697 $replacement = str_replace('$'.$i--, $match[$offset +
$i], $replacement);
702 // http://doc.spip.org/@_replace_name
703 function _replace_name($match, $offset){
704 $length = strlen($match[$offset +
2]);
705 $start = $length - max($length - strlen($match[$offset +
3]), 0);
706 return substr($match[$offset +
1], $start, $length) . $match[$offset +
4];
709 // http://doc.spip.org/@_replace_encoded
710 function _replace_encoded($match, $offset) {
711 return $this->buffer
[$match[$offset]];
715 // php : we cannot pass additional data to preg_replace_callback,
716 // and we cannot use &$this in create_function, so let's go to lower level
719 // encode escaped characters
720 // http://doc.spip.org/@_escape
721 function _escape($string, $escapeChar) {
723 $this->buffer
= $escapeChar;
724 return preg_replace_callback(
725 '/\\' . $escapeChar . '(.)' .'/',
726 array(&$this, '_escapeBis'),
734 // http://doc.spip.org/@_escapeBis
735 function _escapeBis($match) {
736 $this->_escaped
[] = $match[1];
737 return $this->buffer
;
740 // decode escaped characters
741 // http://doc.spip.org/@_unescape
742 function _unescape($string, $escapeChar) {
744 $regexp = '/'.'\\'.$escapeChar.'/';
745 $this->buffer
= array('escapeChar'=> $escapeChar, 'i' => 0);
746 return preg_replace_callback
749 array(&$this, '_unescapeBis'),
757 // http://doc.spip.org/@_unescapeBis
758 function _unescapeBis() {
759 if (!empty($this->_escaped
[$this->buffer
['i']])) {
760 $temp = $this->_escaped
[$this->buffer
['i']];
764 $this->buffer
['i']++
;
765 return $this->buffer
['escapeChar'] . $temp;
768 // http://doc.spip.org/@_internalEscape
769 function _internalEscape($string) {
770 return preg_replace($this->ESCAPE
, '', $string);