source: trunk/admin/inc/Controller.class.php @ 2364

Revision 2364, 29.2 KB checked in by amuller, 14 years ago (diff)

Ticket #1008 - Adicionando licença aos arquivos php

  • Property svn:executable set to *
Line 
1<?php
2
3        /***************************************************************************
4        * Expresso Livre                                                           *
5        * http://www.expressolivre.org                                             *
6        * --------------------------------------------                             *
7        *  This program is free software; you can redistribute it and/or modify it *
8        *  under the terms of the GNU General Public License as published by the   *
9        *  Free Software Foundation; either version 2 of the License, or (at your  *
10        *  option) any later version.                                              *
11        \**************************************************************************/
12
13class Controller
14{
15        const __CONTROLLER_SECURITY__                           = 'controller-security';
16        const __CONTROLLER_CONTENTES__                          = 'controller-contentes';
17        const __CONTROLLER_SECTIONS__                           = 'controller-sections';
18
19        const __STRING_ACCESS__                                         = 'string-access';
20        const __STRING_DELIMITER__                                      = 'string-delimiter';
21
22        const __CONTROLLER_CONTENTES_ITEM__                     = 'item';
23        const __CONTROLLER_CONTENTES_ITEM_PARAM__       = 'param';
24        const __CONTROLLER_CONTENTES_ITEM_SECTION__     = 'section';
25
26        const __CONTROLLER_SECTIONS_ITEM__                      = 'item';
27
28        private $fatalError                                                     = false;
29
30        public function __construct()
31        {
32                try
33                {
34                        $controler_xml = dirname(__FILE__) . '/controller.xml';
35                        if ( !file_exists($controler_xml) )
36                                throw new Exception(__CLASS__ . ' [ ERROR ] :: the configuration file does not exist');
37
38                        $this->dom = new DOMDocument;
39                        $this->dom->preserveWhiteSpace = FALSE;
40                        $this->dom->load($controler_xml);
41
42                        unset($controler_xml);
43
44                        $this->controller_security = $this->dom->getElementsByTagName(self::__CONTROLLER_SECURITY__);
45                        if ( $this->controller_security->length === (int)0 )
46                                throw new Exception(__CLASS__ . ' [ ERROR #0 ] :: the tag "' . self::__CONTROLLER_SECURITY__ . '" does not exist');
47                        if ( $this->controller_security->length !== (int)1 )
48                                throw new Exception(__CLASS__ . ' [ ERROR #1 ] :: exists more of a tag "' . self::__CONTROLLER_SECURITY__ . '"');
49
50                        $this->controller_contentes = $this->dom->getElementsByTagName(self::__CONTROLLER_CONTENTES__);
51                        if ( $this->controller_contentes->length === (int)0 )
52                                throw new Exception(__CLASS__ . ' [ ERROR #2 ] :: the tag "' . self::__CONTROLLER_CONTENTES__ . '" does not exist');
53                        if ( $this->controller_contentes->length !== (int)1 )
54                                throw new Exception(__CLASS__ . ' [ ERROR #3 ] :: exists more of a tag "' . self::__CONTROLLER_CONTENTES__ . '"');
55                        $this->controller_contentes = $this->controller_contentes->item(0);
56
57                        $this->controller_sections = $this->dom->getElementsByTagName("controller-sections");
58                        if ( $this->controller_sections->length === (int)0 )
59                                throw new Exception(__CLASS__ . ' [ ERROR #4 ] :: the tag "' . self::__CONTROLLER_SECTIONS__ . '" does not exist');
60                        if ( $this->controller_sections->length !== (int)1 )
61                                throw new Exception(__CLASS__ . ' [ ERROR #5 ] :: exists more of a tag "' . self::__CONTROLLER_SECTIONS__ . '"');
62                        $this->controller_sections = $this->controller_sections->item(0);
63
64                        $this->string_access = $this->controller_security->item(0)->getElementsByTagName(self::__STRING_ACCESS__);
65                        if ( $this->string_access->length === (int)0 )
66                                throw new Exception(__CLASS__ . ' [ ERROR #6 ] :: the tag "' . self::__STRING_ACCESS__ . '" does not exist');
67                        if ( $this->string_access->length !== (int)1 )
68                                throw new Exception(__CLASS__ . ' [ ERROR #7 ] :: exists more of a tag "' . self::__STRING_ACCESS__ . '"');
69                        $this->string_access = $this->string_access->item(0)->nodeValue;
70
71                        $this->string_delimiter = $this->controller_security->item(0)->getElementsByTagName(self::__STRING_DELIMITER__);
72                        ( $this->string_delimiter->length === (int)0 )
73                                and die(__CLASS__ . ' [ ERROR #8 ] :: the tag "' . self::__STRING_DELIMITER__ . '" does not exist');
74                        if ( $this->string_delimiter->length !== (int)1 )
75                                throw new Exception(__CLASS__ . ' [ ERROR #9 ] :: exists more of a tag "' . self::__STRING_DELIMITER__ . '"');
76                        $this->string_delimiter = $this->string_delimiter->item(0)->nodeValue;
77                }
78                catch(Exception $e)
79                {
80                        $this->fatalError = true;
81                        return $e->getMessage();
82                }
83        }
84
85        public function __call($name, $arguments)
86        {
87                if ( !$this->fatalError )
88                        switch ( $name )
89                        {
90                                case 'exec' :
91                                        return $this->_exec($arguments[0]);
92                                break;
93                                default : return "Method not avaible";
94                        }
95        }
96
97        public function __toString()
98        {
99                return __CLASS__;
100        }
101
102        private final function _exec(array &$pRequest)
103        {
104                ( $pRequest[$this->string_access] )
105                        or die(__CLASS__ . ' [ ERROR #10 ] :: bad string action argument');
106
107                list($section_name, $ref, $alias) = explode($this->string_delimiter, $pRequest[$this->string_access]);
108                unset($pRequest[$this->string_access]);
109
110                $contents_itens = $this->controller_contentes->getElementsByTagName(self::__CONTROLLER_CONTENTES_ITEM__);
111
112                for ( $i = 0; $i < $contents_itens->length && $contents_itens->item($i)->getAttribute(self::__CONTROLLER_CONTENTES_ITEM_PARAM__) != $section_name; $i++ );
113                ( !($i < $contents_itens->length) )
114                        and die(__CLASS__ . ' [ ERROR #11 ] :: invalid section "' . $section_name . '"');
115
116                $section_name = $contents_itens->item($i)->getAttribute(self::__CONTROLLER_CONTENTES_ITEM_SECTION__);
117
118                $section = $this->controller_sections->getElementsByTagName($section_name);
119                ( $section->length === (int)0 )
120                        and die(__CLASS__ . ' [ ERROR #12 ] :: the tag "' . $section_name . '" does not exist');
121                ( $section->length === (int)1 )
122                        or die(__CLASS__ . ' [ ERROR #13 ] :: exists more of a tag "' . $section_name . '"');
123                $section = $section->item(0);
124
125                $section_itens = $section->getElementsByTagName(self::__CONTROLLER_SECTIONS_ITEM__);
126
127                if ( empty($alias) && $alias !== '0' )
128                        for ( $i = 0; $i < $section_itens->length && $section_itens->item($i)->getAttribute('ref') != $ref; $i++ );
129                else
130                        for ( $i = 0; $i < $section_itens->length && ( $section_itens->item($i)->getAttribute('ref') != $ref || $section_itens->item($i)->getAttribute('alias') !== $alias); $i++ );
131
132                ( !($i < $section_itens->length) )
133                        and die(__CLASS__ . ' [ ERROR #14 ] :: invalid reference "' . $ref . '"');
134
135                $path = $section_itens->item($i)->getAttribute('path')
136                        or $path = $section->getAttribute('path')
137                        or die(__CLASS__ . ' [ ERROR #15 ] :: bad path argument');
138
139                $prefix = $section_itens->item($i)->getAttribute('prefix')
140                        or $prefix = $section->getAttribute('prefix');
141
142                $suffix = $section_itens->item($i)->getAttribute('suffix')
143                        or $suffix = $section->getAttribute('suffix')
144                        or die(__CLASS__ . ' [ ERROR #16 ] :: bad suffix argument');
145
146                return $this->$section_name(array("pSectionItem" => $section_itens->item($i), "pPath" => $path, "pPrefix" => $prefix, "pSuffix" => $suffix, "pRequest" => $pRequest));
147        }
148
149        private final function php()
150        {
151                $params = func_get_args();
152                extract($params[0]);
153
154                $class = $pSectionItem->getAttribute('class')
155                        and $method = $pSectionItem->getAttribute('method')
156                        or die(__CLASS__ . ' [ ERROR #17 ] :: bad class or method argument');
157
158                $file = "{$pPath}/{$pPrefix}{$class}{$pSuffix}";
159
160                file_exists($file)
161                        or die(__CLASS__ . ' [ ERROR #18 ] :: the file that has the class was not opened');
162
163                require_once $file;
164
165                $obj = new ReflectionClass($class);
166
167                if ( $pRequest['classCostructor'] )
168                {
169                        $obj = $obj->newInstance($pRequest['classCostructor']);
170                        unset($pRequest['classCostructor']);
171                }
172                else
173                        $obj = $obj->newInstance();
174
175                $method = new ReflectionMethod($class, $method);
176                $result = $method->invoke($obj, $pRequest);
177
178                #$obj = new $class;
179
180                #if ( $pRequest )
181                #       $result = $obj -> $method($pRequest);
182                #else
183                #       $result = $obj -> $method();
184
185                return $result;
186        }
187
188        private final function js()
189        {
190                $params = func_get_args();
191                extract($params[0]);
192
193                $js = $pSectionItem->getAttribute('js')
194                        or die(__CLASS__ . ' [ ERROR #18 ] :: bad js argument');
195
196                $file = "{$pPath}/{$pPrefix}{$js}{$pSuffix}";
197
198                file_exists($file)
199                        or die(__CLASS__ . ' [ ERROR #19 ] :: the file that has the class was not opened');
200
201                $debug = $pSectionItem->parentNode->getAttribute('debug');
202                if ( $debug && ($debug === 'true') )
203                        return file_get_contents($file);
204
205                $packed_file = "{$pPath}/.packer.{$pPrefix}{$js}{$pSuffix}";
206
207                (
208                        file_exists($packed_file)
209                        and filemtime($packed_file) > filemtime($file)
210                        and $packed = file_get_contents($packed_file)
211                )
212                or
213                (
214                        $packer = new JavaScriptPacker(file_get_contents($file), 'High ASCII', true, true)
215                        and $packed = $packer->pack()
216                        and file_put_contents($packed_file, $packed)
217                );
218
219                return $packed;
220        }
221}
222
223class JavaScriptPacker
224{
225        const IGNORE = '$1';
226
227        private $_script = '';
228        private $_encoding = 62;
229        private $_fastDecode = true;
230        private $_specialChars = false;
231
232        private $_parsers = array();
233
234        private $LITERAL_ENCODING = array(
235                'None' => 0,
236                'Numeric' => 10,
237                'Normal' => 62,
238                'High ASCII' => 95
239        );
240
241        public function __construct ($pScript, $pEncoding = 62, $pFastDecode = true, $pSpecialChars = false)
242        {
243                $this->_script = $pScript . "\n";
244                if ( array_key_exists($pEncoding, $this->LITERAL_ENCODING) )
245                        $pEncoding = $this->LITERAL_ENCODING[$pEncoding];
246
247                $this->_encoding = min((int)$pEncoding, 95);
248                $this->_fastDecode = $pFastDecode;
249                $this->_specialChars = $pSpecialChars;
250        }
251
252        public function pack()
253        {
254                $this->_addParser('_basicCompression');
255
256                if ( $this->_specialChars )
257                        $this->_addParser('_encodeSpecialChars');
258
259                if ( $this->_encoding )
260                        $this->_addParser('_encodeKeywords');
261
262                return $this->_pack($this->_script);
263        }
264
265        private function _pack($script)
266        {
267                for ( $i = 0; isset($this->_parsers[$i]); $i++ )
268                        $script = call_user_func(array(&$this,$this->_parsers[$i]), $script);
269
270                return $script;
271        }
272
273        // keep a list of parsing functions, they'll be executed all at once
274        private function _addParser($parser)
275        {
276                $this->_parsers[] = $parser;
277        }
278
279        // zero encoding - just removal of white space and comments
280        private function _basicCompression($script)
281        {
282                $parser = new ParseMaster();
283                // make safe
284                $parser->escapeChar = '\\';
285                // protect strings
286                $parser->add('/\'[^\'\\n\\r]*\'/', self::IGNORE);
287                $parser->add('/"[^"\\n\\r]*"/', self::IGNORE);
288                // remove comments
289                $parser->add('/\\/\\/[^\\n\\r]*[\\n\\r]/', ' ');
290                $parser->add('/\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\//', ' ');
291                // protect regular expressions
292                $parser->add('/\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)/', '$2'); // IGNORE
293                $parser->add('/[^\\w\\x24\\/\'"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?/', self::IGNORE);
294                // remove: ;;; doSomething();
295                if ($this->_specialChars) $parser->add('/;;;[^\\n\\r]+[\\n\\r]/');
296                // remove redundant semi-colons
297                $parser->add('/\\(;;\\)/', self::IGNORE); // protect for (;;) loops
298                $parser->add('/;+\\s*([};])/', '$2');
299                // apply the above
300                $script = $parser->exec($script);
301
302                // remove white-space
303                $parser->add('/(\\b|\\x24)\\s+(\\b|\\x24)/', '$2 $3');
304                $parser->add('/([+\\-])\\s+([+\\-])/', '$2 $3');
305                $parser->add('/\\s+/', '');
306                // done
307                return $parser->exec($script);
308        }
309
310        private function _encodeSpecialChars($script)
311        {
312                $parser = new ParseMaster();
313                // replace: $name -> n, $$name -> na
314                $parser->add('/((\\x24+)([a-zA-Z$_]+))(\\d*)/',
315                                         array('fn' => '_replace_name')
316                );
317                // replace: _name -> _0, double-underscore (__name) is ignored
318                $regexp = '/\\b_[A-Za-z\\d]\\w*/';
319                // build the word list
320                $keywords = $this->_analyze($script, $regexp, '_encodePrivate');
321                // quick ref
322                $encoded = $keywords['encoded'];
323
324                $parser->add($regexp,
325                        array(
326                                'fn' => '_replace_encoded',
327                                'data' => $encoded
328                        )
329                );
330                return $parser->exec($script);
331        }
332
333        private function _encodeKeywords($script) {
334                // escape high-ascii values already in the script (i.e. in strings)
335                if ($this->_encoding > 62)
336                        $script = $this->_escape95($script);
337                // create the parser
338                $parser = new ParseMaster();
339                $encode = $this->_getEncoder($this->_encoding);
340                // for high-ascii, don't encode single character low-ascii
341                $regexp = ($this->_encoding > 62) ? '/\\w\\w+/' : '/\\w+/';
342                // build the word list
343                $keywords = $this->_analyze($script, $regexp, $encode);
344                $encoded = $keywords['encoded'];
345
346                // encode
347                $parser->add($regexp,
348                        array(
349                                'fn' => '_replace_encoded',
350                                'data' => $encoded
351                        )
352                );
353                if (empty($script)) return $script;
354                else {
355                        //$res = $parser->exec($script);
356                        //$res = $this->_bootStrap($res, $keywords);
357                        //return $res;
358                        return $this->_bootStrap($parser->exec($script), $keywords);
359                }
360        }
361
362        private function _analyze($script, $regexp, $encode) {
363                // analyse
364                // retreive all words in the script
365                $all = array();
366                preg_match_all($regexp, $script, $all);
367                $_sorted = array(); // list of words sorted by frequency
368                $_encoded = array(); // dictionary of word->encoding
369                $_protected = array(); // instances of "protected" words
370                $all = $all[0]; // simulate the javascript comportement of global match
371                if (!empty($all)) {
372                        $unsorted = array(); // same list, not sorted
373                        $protected = array(); // "protected" words (dictionary of word->"word")
374                        $value = array(); // dictionary of charCode->encoding (eg. 256->ff)
375                        $this->_count = array(); // word->count
376                        $i = count($all); $j = 0; //$word = null;
377                        // count the occurrences - used for sorting later
378                        do {
379                                --$i;
380                                $word = '$' . $all[$i];
381                                if (!isset($this->_count[$word])) {
382                                        $this->_count[$word] = 0;
383                                        $unsorted[$j] = $word;
384                                        // make a dictionary of all of the protected words in this script
385                                        //  these are words that might be mistaken for encoding
386                                        //if (is_string($encode) && method_exists($this, $encode))
387                                        $values[$j] = call_user_func(array(&$this, $encode), $j);
388                                        $protected['$' . $values[$j]] = $j++;
389                                }
390                                // increment the word counter
391                                $this->_count[$word]++;
392                        } while ($i > 0);
393                        // prepare to sort the word list, first we must protect
394                        //  words that are also used as codes. we assign them a code
395                        //  equivalent to the word itself.
396                        // e.g. if "do" falls within our encoding range
397                        //      then we store keywords["do"] = "do";
398                        // this avoids problems when decoding
399                        $i = count($unsorted);
400                        do {
401                                $word = $unsorted[--$i];
402                                if (isset($protected[$word]) /*!= null*/) {
403                                        $_sorted[$protected[$word]] = substr($word, 1);
404                                        $_protected[$protected[$word]] = true;
405                                        $this->_count[$word] = 0;
406                                }
407                        } while ($i);
408
409                        // sort the words by frequency
410                        // Note: the javascript and php version of sort can be different :
411                        // in php manual, usort :
412                        // " If two members compare as equal,
413                        // their order in the sorted array is undefined."
414                        // so the final packed script is different of the Dean's javascript version
415                        // but equivalent.
416                        // the ECMAscript standard does not guarantee this behaviour,
417                        // and thus not all browsers (e.g. Mozilla versions dating back to at
418                        // least 2003) respect this.
419                        usort($unsorted, array(&$this, '_sortWords'));
420                        $j = 0;
421                        // because there are "protected" words in the list
422                        //  we must add the sorted words around them
423                        do {
424                                if (!isset($_sorted[$i]))
425                                        $_sorted[$i] = substr($unsorted[$j++], 1);
426                                $_encoded[$_sorted[$i]] = $values[$i];
427                        } while (++$i < count($unsorted));
428                }
429                return array(
430                        'sorted'  => $_sorted,
431                        'encoded' => $_encoded,
432                        'protected' => $_protected);
433        }
434
435        private $_count = array();
436        private function _sortWords($match1, $match2) {
437                return $this->_count[$match2] - $this->_count[$match1];
438        }
439
440        // build the boot function used for loading and decoding
441        private function _bootStrap($packed, $keywords) {
442                $ENCODE = $this->_safeRegExp('$encode\\($count\\)');
443
444                // $packed: the packed script
445                $packed = "'" . $this->_escape($packed) . "'";
446
447                // $ascii: base for encoding
448                $ascii = min(count($keywords['sorted']), $this->_encoding);
449                if ($ascii == 0) $ascii = 1;
450
451                // $count: number of words contained in the script
452                $count = count($keywords['sorted']);
453
454                // $keywords: list of words contained in the script
455                foreach ($keywords['protected'] as $i=>$value) {
456                        $keywords['sorted'][$i] = '';
457                }
458                // convert from a string to an array
459                ksort($keywords['sorted']);
460                $keywords = "'" . implode('|',$keywords['sorted']) . "'.split('|')";
461
462                $encode = ($this->_encoding > 62) ? '_encode95' : $this->_getEncoder($ascii);
463                $encode = $this->_getJSFunction($encode);
464                $encode = preg_replace('/_encoding/','$ascii', $encode);
465                $encode = preg_replace('/arguments\\.callee/','$encode', $encode);
466                $inline = '\\$count' . ($ascii > 10 ? '.toString(\\$ascii)' : '');
467
468                // $decode: code snippet to speed up decoding
469                if ($this->_fastDecode) {
470                        // create the decoder
471                        $decode = $this->_getJSFunction('_decodeBody');
472                        if ($this->_encoding > 62)
473                                $decode = preg_replace('/\\\\w/', '[\\xa1-\\xff]', $decode);
474                        // perform the encoding inline for lower ascii values
475                        elseif ($ascii < 36)
476                                $decode = preg_replace($ENCODE, $inline, $decode);
477                        // special case: when $count==0 there are no keywords. I want to keep
478                        //  the basic shape of the unpacking funcion so i'll frig the code...
479                        if ($count == 0)
480                                $decode = preg_replace($this->_safeRegExp('($count)\\s*=\\s*1'), '$1=0', $decode, 1);
481                }
482
483                // boot function
484                $unpack = $this->_getJSFunction('_unpack');
485                if ($this->_fastDecode) {
486                        // insert the decoder
487                        $this->buffer = $decode;
488                        $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastDecode'), $unpack, 1);
489                }
490                $unpack = preg_replace('/"/', "'", $unpack);
491                if ($this->_encoding > 62) { // high-ascii
492                        // get rid of the word-boundaries for regexp matches
493                        $unpack = preg_replace('/\'\\\\\\\\b\'\s*\\+|\\+\s*\'\\\\\\\\b\'/', '', $unpack);
494                }
495                if ($ascii > 36 || $this->_encoding > 62 || $this->_fastDecode) {
496                        // insert the encode function
497                        $this->buffer = $encode;
498                        $unpack = preg_replace_callback('/\\{/', array(&$this, '_insertFastEncode'), $unpack, 1);
499                } else {
500                        // perform the encoding inline
501                        $unpack = preg_replace($ENCODE, $inline, $unpack);
502                }
503                // pack the boot function too
504                $unpackPacker = new JavaScriptPacker($unpack, 0, false, true);
505                $unpack = $unpackPacker->pack();
506
507                // arguments
508                $params = array($packed, $ascii, $count, $keywords);
509                if ($this->_fastDecode) {
510                        $params[] = 0;
511                        $params[] = '{}';
512                }
513                $params = implode(',', $params);
514
515                // the whole thing
516                return 'eval(' . $unpack . '(' . $params . "))\n";
517        }
518
519        private $buffer;
520        private function _insertFastDecode($match) {
521                return '{' . $this->buffer . ';';
522        }
523        private function _insertFastEncode($match) {
524                return '{$encode=' . $this->buffer . ';';
525        }
526
527        // mmm.. ..which one do i need ??
528        private function _getEncoder($ascii) {
529                return $ascii > 10 ? $ascii > 36 ? $ascii > 62 ?
530                       '_encode95' : '_encode62' : '_encode36' : '_encode10';
531        }
532
533        // zero encoding
534        // characters: 0123456789
535        private function _encode10($charCode) {
536                return $charCode;
537        }
538
539        // inherent base36 support
540        // characters: 0123456789abcdefghijklmnopqrstuvwxyz
541        private function _encode36($charCode) {
542                return base_convert($charCode, 10, 36);
543        }
544
545        // hitch a ride on base36 and add the upper case alpha characters
546        // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
547        private function _encode62($charCode) {
548                $res = '';
549                if ($charCode >= $this->_encoding) {
550                        $res = $this->_encode62((int)($charCode / $this->_encoding));
551                }
552                $charCode = $charCode % $this->_encoding;
553
554                if ($charCode > 35)
555                        return $res . chr($charCode + 29);
556                else
557                        return $res . base_convert($charCode, 10, 36);
558        }
559
560        // use high-ascii values
561        // characters: ¡¢£€¥Š§š©ª«¬­®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãÀåÊçÚéêëìíîïðñòóÎõö÷ÞùúûÌÜß
562        private function _encode95($charCode) {
563                $res = '';
564                if ($charCode >= $this->_encoding)
565                        $res = $this->_encode95($charCode / $this->_encoding);
566
567                return $res . chr(($charCode % $this->_encoding) + 161);
568        }
569
570        private function _safeRegExp($string) {
571                return '/'.preg_replace('/\$/', '\\\$', $string).'/';
572        }
573
574        private function _encodePrivate($charCode) {
575                return "_" . $charCode;
576        }
577
578        // protect characters used by the parser
579        private function _escape($script) {
580                return preg_replace('/([\\\\\'])/', '\\\$1', $script);
581        }
582
583        // protect high-ascii characters already in the script
584        private function _escape95($script) {
585                return preg_replace_callback(
586                        '/[\\xa1-\\xff]/',
587                        array(&$this, '_escape95Bis'),
588                        $script
589                );
590        }
591        private function _escape95Bis($match) {
592                return '\x'.((string)dechex(ord($match)));
593        }
594
595
596        private function _getJSFunction($aName) {
597                if (defined('self::JSFUNCTION'.$aName))
598                        return constant('self::JSFUNCTION'.$aName);
599                else
600                        return '';
601        }
602
603        // JavaScript Functions used.
604        // Note : In Dean's version, these functions are converted
605        // with 'String(aFunctionName);'.
606        // This internal conversion complete the original code, ex :
607        // 'while (aBool) anAction();' is converted to
608        // 'while (aBool) { anAction(); }'.
609        // The JavaScript functions below are corrected.
610
611        // unpacking function - this is the boot strap function
612        //  data extracted from this packing routine is passed to
613        //  this function when decoded in the target
614        // NOTE ! : without the ';' final.
615        const JSFUNCTION_unpack =
616
617'function($packed, $ascii, $count, $keywords, $encode, $decode) {
618    while ($count--) {
619        if ($keywords[$count]) {
620            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
621        }
622    }
623    return $packed;
624}';
625/*
626'function($packed, $ascii, $count, $keywords, $encode, $decode) {
627    while ($count--)
628        if ($keywords[$count])
629            $packed = $packed.replace(new RegExp(\'\\\\b\' + $encode($count) + \'\\\\b\', \'g\'), $keywords[$count]);
630    return $packed;
631}';
632*/
633
634        // code-snippet inserted into the unpacker to speed up decoding
635        const JSFUNCTION_decodeBody =
636//_decode = function() {
637// does the browser support String.replace where the
638//  replacement value is a function?
639
640'    if (!\'\'.replace(/^/, String)) {
641        // decode all the values we need
642        while ($count--) {
643            $decode[$encode($count)] = $keywords[$count] || $encode($count);
644        }
645        // global replacement function
646        $keywords = [function ($encoded) {return $decode[$encoded]}];
647        // generic match
648        $encode = function () {return \'\\\\w+\'};
649        // reset the loop counter -  we are now doing a global replace
650        $count = 1;
651    }
652';
653//};
654/*
655'       if (!\'\'.replace(/^/, String)) {
656        // decode all the values we need
657        while ($count--) $decode[$encode($count)] = $keywords[$count] || $encode($count);
658        // global replacement function
659        $keywords = [function ($encoded) {return $decode[$encoded]}];
660        // generic match
661        $encode = function () {return\'\\\\w+\'};
662        // reset the loop counter -  we are now doing a global replace
663        $count = 1;
664    }';
665*/
666
667         // zero encoding
668         // characters: 0123456789
669         const JSFUNCTION_encode10 =
670'function($charCode) {
671    return $charCode;
672}';//;';
673
674         // inherent base36 support
675         // characters: 0123456789abcdefghijklmnopqrstuvwxyz
676         const JSFUNCTION_encode36 =
677'function($charCode) {
678    return $charCode.toString(36);
679}';//;';
680
681        // hitch a ride on base36 and add the upper case alpha characters
682        // characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
683        const JSFUNCTION_encode62 =
684'function($charCode) {
685    return ($charCode < _encoding ? \'\' : arguments.callee(parseInt($charCode / _encoding))) +
686    (($charCode = $charCode % _encoding) > 35 ? String.fromCharCode($charCode + 29) : $charCode.toString(36));
687}';
688
689        // use high-ascii values
690        // characters: ¡¢£€¥Š§š©ª«¬­®¯°±²³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãÀåÊçÚéêëìíîïðñòóÎõö÷ÞùúûÌÜß
691        const JSFUNCTION_encode95 =
692'function($charCode) {
693    return ($charCode < _encoding ? \'\' : arguments.callee($charCode / _encoding)) +
694        String.fromCharCode($charCode % _encoding + 161);
695}';
696
697}
698
699
700class ParseMaster {
701        public $ignoreCase = false;
702        public $escapeChar = '';
703
704        // constants
705        const EXPRESSION = 0;
706        const REPLACEMENT = 1;
707        const LENGTH = 2;
708
709        // used to determine nesting levels
710        private $GROUPS = '/\\(/';//g
711        private $SUB_REPLACE = '/\\$\\d/';
712        private $INDEXED = '/^\\$\\d+$/';
713        private $TRIM = '/([\'"])\\1\\.(.*)\\.\\1\\1$/';
714        private $ESCAPE = '/\\\./';//g
715        private $QUOTE = '/\'/';
716        private $DELETED = '/\\x01[^\\x01]*\\x01/';//g
717
718        public function add($expression, $replacement = '') {
719                // count the number of sub-expressions
720                //  - add one because each pattern is itself a sub-expression
721                $length = 1 + preg_match_all($this->GROUPS, $this->_internalEscape((string)$expression), $out);
722
723                // treat only strings $replacement
724                if (is_string($replacement)) {
725                        // does the pattern deal with sub-expressions?
726                        if (preg_match($this->SUB_REPLACE, $replacement)) {
727                                // a simple lookup? (e.g. "$2")
728                                if (preg_match($this->INDEXED, $replacement)) {
729                                        // store the index (used for fast retrieval of matched strings)
730                                        $replacement = (int)(substr($replacement, 1)) - 1;
731                                } else { // a complicated lookup (e.g. "Hello $2 $1")
732                                        // build a function to do the lookup
733                                        $quote = preg_match($this->QUOTE, $this->_internalEscape($replacement))
734                                                 ? '"' : "'";
735                                        $replacement = array(
736                                                'fn' => '_backReferences',
737                                                'data' => array(
738                                                        'replacement' => $replacement,
739                                                        'length' => $length,
740                                                        'quote' => $quote
741                                                )
742                                        );
743                                }
744                        }
745                }
746                // pass the modified arguments
747                if (!empty($expression)) $this->_add($expression, $replacement, $length);
748                else $this->_add('/^$/', $replacement, $length);
749        }
750
751        public function exec($string) {
752                // execute the global replacement
753                $this->_escaped = array();
754
755                // simulate the _patterns.toSTring of Dean
756                $regexp = '/';
757                foreach ($this->_patterns as $reg) {
758                        $regexp .= '(' . substr($reg[self::EXPRESSION], 1, -1) . ')|';
759                }
760                $regexp = substr($regexp, 0, -1) . '/';
761                $regexp .= ($this->ignoreCase) ? 'i' : '';
762
763                $string = $this->_escape($string, $this->escapeChar);
764                $string = preg_replace_callback(
765                        $regexp,
766                        array(
767                                &$this,
768                                '_replacement'
769                        ),
770                        $string
771                );
772                $string = $this->_unescape($string, $this->escapeChar);
773
774                return preg_replace($this->DELETED, '', $string);
775        }
776
777        public function reset() {
778                // clear the patterns collection so that this object may be re-used
779                $this->_patterns = array();
780        }
781
782        // private
783        private $_escaped = array();  // escaped characters
784        private $_patterns = array(); // patterns stored by index
785
786        // create and add a new pattern to the patterns collection
787        private function _add() {
788                $arguments = func_get_args();
789                $this->_patterns[] = $arguments;
790        }
791
792        // this is the global replace function (it's quite complicated)
793        private function _replacement($arguments) {
794                if (empty($arguments)) return '';
795
796                $i = 1; $j = 0;
797                // loop through the patterns
798                while (isset($this->_patterns[$j])) {
799                        $pattern = $this->_patterns[$j++];
800                        // do we have a result?
801                        if (isset($arguments[$i]) && ($arguments[$i] != '')) {
802                                $replacement = $pattern[self::REPLACEMENT];
803
804                                if (is_array($replacement) && isset($replacement['fn'])) {
805
806                                        if (isset($replacement['data'])) $this->buffer = $replacement['data'];
807                                        return call_user_func(array(&$this, $replacement['fn']), $arguments, $i);
808
809                                } elseif (is_int($replacement)) {
810                                        return $arguments[$replacement + $i];
811
812                                }
813                                $delete = ($this->escapeChar == '' ||
814                                           strpos($arguments[$i], $this->escapeChar) === false)
815                                        ? '' : "\x01" . $arguments[$i] . "\x01";
816                                return $delete . $replacement;
817
818                        // skip over references to sub-expressions
819                        } else {
820                                $i += $pattern[self::LENGTH];
821                        }
822                }
823        }
824
825        private function _backReferences($match, $offset) {
826                $replacement = $this->buffer['replacement'];
827                $quote = $this->buffer['quote'];
828                $i = $this->buffer['length'];
829                while ($i) {
830                        $replacement = str_replace('$'.$i--, $match[$offset + $i], $replacement);
831                }
832                return $replacement;
833        }
834
835        private function _replace_name($match, $offset){
836                $length = strlen($match[$offset + 2]);
837                $start = $length - max($length - strlen($match[$offset + 3]), 0);
838                return substr($match[$offset + 1], $start, $length) . $match[$offset + 4];
839        }
840
841        private function _replace_encoded($match, $offset) {
842                return $this->buffer[$match[$offset]];
843        }
844
845
846        // php : we cannot pass additional data to preg_replace_callback,
847        // and we cannot use &$this in create_function, so let's go to lower level
848        private $buffer;
849
850        // encode escaped characters
851        private function _escape($string, $escapeChar) {
852                if ($escapeChar) {
853                        $this->buffer = $escapeChar;
854                        return preg_replace_callback(
855                                '/\\' . $escapeChar . '(.)' .'/',
856                                array(&$this, '_escapeBis'),
857                                $string
858                        );
859
860                } else {
861                        return $string;
862                }
863        }
864        private function _escapeBis($match) {
865                $this->_escaped[] = $match[1];
866                return $this->buffer;
867        }
868
869        // decode escaped characters
870        private function _unescape($string, $escapeChar) {
871                if ($escapeChar) {
872                        $regexp = '/'.'\\'.$escapeChar.'/';
873                        $this->buffer = array('escapeChar'=> $escapeChar, 'i' => 0);
874                        return preg_replace_callback
875                        (
876                                $regexp,
877                                array(&$this, '_unescapeBis'),
878                                $string
879                        );
880
881                } else {
882                        return $string;
883                }
884        }
885        private function _unescapeBis() {
886                if (!empty($this->_escaped[$this->buffer['i']])) {
887                         $temp = $this->_escaped[$this->buffer['i']];
888                } else {
889                        $temp = '';
890                }
891                $this->buffer['i']++;
892                return $this->buffer['escapeChar'] . $temp;
893        }
894
895        private function _internalEscape($string) {
896                return preg_replace($this->ESCAPE, '', $string);
897        }
898}
899
900?>
Note: See TracBrowser for help on using the repository browser.