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