source: companies/celepar/admin/inc/Controller.class.php @ 763

Revision 763, 28.4 KB checked in by niltonneto, 15 years ago (diff)

Importação inicial do Expresso da Celepar

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