Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • Compiler
  • ControlNode
  • Engine
  • EvaluateNode
  • ExpressionNode
  • Hook
  • Hooks
  • HTMLParser
  • Node
  • Template
  • TextHole
  • TextNode
  • TranslateNode
  • URLNode

Functions

  • by_columns
  • render
  • tr
  1 <?php
  2 
  3 /*
  4  * This file is part of the Patron package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace Patron;
 13 
 14 use ICanBoogie\Debug;
 15 use ICanBoogie\Exception;
 16 
 17 class TextHole
 18 {
 19     public function __construct()
 20     {
 21         $this->context = $this->contextInit();
 22     }
 23 
 24     protected $functions = array();
 25 
 26     public function addFunction($name, $callback)
 27     {
 28         #
 29         # FIXME-20080203: should check overrides
 30         #
 31 
 32         $this->functions[$name] = $callback;
 33     }
 34 
 35     public function findFunction($name)
 36     {
 37         /*
 38         // TODO: move to Engine
 39 
 40         $hook = null;
 41 
 42         try
 43         {
 44             $hook = Hook::find('patron.functions', $name);
 45         }
 46         catch (\Exception $e) { }
 47 
 48         if ($hook)
 49         {
 50             return $hook;
 51         }
 52         */
 53 
 54         // /
 55 
 56         #
 57         #
 58         #
 59 
 60         if (isset($this->functions[$name]))
 61         {
 62             return $this->functions[$name];
 63         }
 64 
 65         $try = 'ICanBoogie\\' . $name;
 66 
 67         if (function_exists($try))
 68         {
 69             return $try;
 70         }
 71 
 72         $try = 'ICanBoogie\I18n\\' . $name;
 73 
 74         if (function_exists($try))
 75         {
 76             return $try;
 77         }
 78 
 79         #
 80         # 'wd' pseudo namespace // COMPAT
 81         #
 82 
 83         $try = 'wd_' . str_replace('-', '_', $name);
 84 
 85         if (function_exists($try))
 86         {
 87             return $try;
 88         }
 89 
 90         $try = 'Patron\\' . $name;
 91 
 92         if (function_exists($try))
 93         {
 94             return $try;
 95         }
 96     }
 97 
 98     /*
 99     **
100 
101     CONTEXT
102 
103     **
104     */
105 
106     public $context = array();
107 
108     protected function contextInit()
109     {
110         return array
111         (
112             '$server' => &$_SERVER,
113             '$request' => &$_REQUEST
114         );
115     }
116 
117     /*
118     **
119 
120     SET & GET
121 
122     **
123     */
124 
125     #
126     # support
127     #
128 
129     static private function dotsToBrackets($string)
130     {
131         #
132         # this function converts dot separated keys to PHP array notation e.g.
133         # a.2.c => ['a'][2]['c']
134         #
135 
136         $parts = explode('.', $string);
137 
138         $rc = NULL;
139 
140         foreach ($parts as $part)
141         {
142             $rc .= is_numeric($part) ? "[$part]" : "['$part']";
143         }
144 
145         return $rc;
146     }
147 
148     /*
149     **
150 
151     PUBLISH
152 
153     **
154     */
155 
156     public function publish($template, $bind=null, array $options=array())
157     {
158         if ($bind !== null)
159         {
160             $this->context['this'] = $bind;
161         }
162 
163         foreach ($options as $option => $value)
164         {
165             switch ((string) $option)
166             {
167                 /*
168                 case 'bind':
169                 {
170                     $this->context['this'] = $options['bind'];
171                 }
172                 break;
173                 */
174                 case 'variables':
175                 {
176                     $this->context = array_merge($this->context, $value);
177                 }
178                 break;
179 
180                 default:
181                 {
182                     Debug::trigger('Suspicious option: %option :value', array('%option' => $option, ':value' => $value));
183                 }
184                 break;
185             }
186         }
187 
188         return $this->resolve($template);
189     }
190 
191     protected function resolve($text)
192     {
193         $text = preg_replace_callback
194         (
195             '#\#\{(?!\s)([^\}]+)\}#', array($this, 'resolve_callback'), $text
196         );
197 
198         return $text;
199     }
200 
201     protected function resolve_callback($matches)
202     {
203         $expression = $matches[1];
204 
205         $rc = $this->evaluate($expression);
206 
207         if (is_object($rc))
208         {
209             if (!method_exists($rc, '__toString'))
210             {
211                 $this->error('%expression was resolved to an object of the class %class', array('%expression' => $expression, '%class' => get_class($rc)));
212             }
213 
214             $rc = (string) $rc;
215         }
216         else if (is_array($rc))
217         {
218             $this->error('%expression was resolved to an array with the following keys: :keys', array('%expression' => $expression, ':keys' => implode(', ', array_keys($rc))));
219         }
220 
221         return $rc;
222     }
223 
224     /*
225      * Decode Javascript style function chain into an array of identifiers and functions
226      */
227 
228     const TOKEN_TYPE = 'type';
229     const TOKEN_TYPE_FUNCTION = 'function';
230     const TOKEN_TYPE_IDENTIFIER = 'identifier';
231     const TOKEN_VALUE = 'value';
232     const TOKEN_ARGS = 'args';
233     const TOKEN_ARGS_EVALUATE = 'args-evaluate';
234 
235     static protected function parseExpression_callback($str)
236     {
237         $str .= '.';
238 
239         $length = strlen($str);
240 
241         $quote = null;
242         $quote_closed = null;
243         $part = null;
244         $escape = false;
245 
246         $function = null;
247         $args = array();
248         $args_evaluate = array();
249         $args_count = 0;
250 
251         $parts = array();
252 
253         for ($i = 0 ; $i < $length ; $i++)
254         {
255             $c = $str{$i};
256 
257             if ($escape)
258             {
259                 $part .= $c;
260 
261                 $escape = false;
262 
263                 continue;
264             }
265 
266             if ($c == '\\')
267             {
268                 //echo "found escape: [$c] @$i<br />";
269 
270                 $escape = true;
271 
272                 continue;
273             }
274 
275             if ($c == '"' || $c == '\'' || $c == '`')
276             {
277                 if ($quote && $quote == $c)
278                 {
279                     //echo "found closing quote: [$c]<br />";
280 
281                     $quote = null;
282                     $quote_closed = $c;
283 
284                     if ($function)
285                     {
286                         continue;
287                     }
288                 }
289                 else if (!$quote)
290                 {
291                     //echo "found opening quote: [$c]<br />";
292 
293                     $quote = $c;
294 
295                     if ($function)
296                     {
297                         continue;
298                     }
299                 }
300             }
301 
302             if ($quote)
303             {
304                 $part .= $c;
305 
306                 continue;
307             }
308 
309             #
310             # we are not in a quote
311             #
312 
313             if ($c == '.')
314             {
315                 //echo "end of part: [$part]<br />";
316 
317                 // FIXME: added strlen() because of '0' e.g. items.0.price
318                 // could a part be null ??
319 
320                 if (strlen($part))
321                 {
322                     $parts[] = array
323                     (
324                         self::TOKEN_TYPE => self::TOKEN_TYPE_IDENTIFIER,
325                         self::TOKEN_VALUE => $part
326                     );
327                 }
328 
329                 $part = null;
330 
331                 continue;
332             }
333 
334             if ($c == '(')
335             {
336                 //echo "function [$part] begin: @$i<br />";
337 
338                 $function = $part;
339 
340                 $args = array();
341                 $args_count = 0;
342 
343                 $part = null;
344 
345                 continue;
346             }
347 
348             if (($c == ',' || $c == ')') && $function)
349             {
350                 //echo "function push argument [$part] q=[$quote_closed]<br />";
351 
352                 if ($part !== null)
353                 {
354                     if ($quote_closed == '`')
355                     {
356                         //echo "we should evaluate [$part][$args_count]<br />";
357 
358                         $args_evaluate[] = $args_count;
359                     }
360 
361                     if (!$quote_closed)
362                     {
363                         #
364                         # end of an unquoted part.
365                         # it might be an integer, a float, or maybe a constant !
366                         #
367 
368                         $part_back = $part;
369 
370                         switch ($part)
371                         {
372                             case 'true':
373                             case 'TRUE':
374                             {
375                                 $part = true;
376                             }
377                             break;
378 
379                             case 'false':
380                             case 'FALSE':
381                             {
382                                 $part = false;
383                             }
384                             break;
385 
386                             case 'null':
387                             case 'NULL':
388                             {
389                                 $part = null;
390                             }
391                             break;
392 
393                             default:
394                             {
395                                 if (is_numeric($part))
396                                 {
397                                     $part = (int) $part;
398                                 }
399                                 else if (is_float($part))
400                                 {
401                                     $part = (float) $part;
402                                 }
403                                 else
404                                 {
405                                     $part = constant($part);
406                                 }
407                             }
408                             break;
409                         }
410 
411                         //\ICanBoogie\log('part: [\1] == [\2]', $part_back, $part);
412                     }
413 
414                     $args[] = $part;
415                     $args_count++;
416 
417                     $part = null;
418                 }
419 
420                 $quote_closed = null;
421 
422                 if ($c != ')')
423                 {
424                     continue;
425                 }
426             }
427 
428             if ($c == ')' && $function)
429             {
430                 //echo "function end: [$part] @$i<br />";
431 
432                 $parts[] = array
433                 (
434                     self::TOKEN_TYPE => self::TOKEN_TYPE_FUNCTION,
435                     self::TOKEN_VALUE => $function,
436                     self::TOKEN_ARGS => $args,
437                     self::TOKEN_ARGS_EVALUATE => $args_evaluate
438                 );
439 
440                 continue;
441             }
442 
443             if ($c == ' ' && $function)
444             {
445                 continue;
446             }
447 
448             $part .= $c;
449         }
450 
451         return $parts;
452     }
453 
454     protected static $function_chain_cache;
455     protected static $function_chain_cache_usage;
456 
457     protected function parseExpression($expression)
458     {
459         $parsed = null;
460 
461         if (isset(self::$function_chain_cache[$expression]))
462         {
463             $parsed = self::$function_chain_cache[$expression];
464         }
465 
466         if (!$parsed)
467         {
468             $parsed = self::parseExpression_callback($expression);
469 
470             self::$function_chain_cache[$expression] = $parsed;
471         }
472 
473         if (empty(self::$function_chain_cache_usage[$expression]))
474         {
475             self::$function_chain_cache_usage[$expression] = 0;
476         }
477 
478         self::$function_chain_cache_usage[$expression]++;
479 
480         return $parsed;
481     }
482 
483     public function evaluate($expression, $silent=false)
484     {
485         if (!$expression)
486         {
487             $this->error('Empty expression');
488 
489             return;
490         }
491 
492         $value = $this->context;
493         $previous_identifier = 'context';
494         $work_expression = $expression;
495 
496         #
497         # `@` (this) handling
498         #
499 
500         if ($expression{0} == '@')
501         {
502             #
503             # this is an instance variable, shortcut for "this."
504             #
505 
506             if (!isset($this->context['this']))
507             {
508                 $this->error
509                 (
510                     'Using <q>this</q> property when no <q>this</q> is defined: %identifier', array
511                     (
512                         '%identifier' => $expression
513                     )
514                 );
515 
516                 return;
517             }
518 
519             $value = $this->context['this'];
520             $previous_identifier = 'this';
521             $work_expression = substr($expression, 1);
522         }
523         else if ($expression{0} == '$')
524         {
525             $value = $GLOBALS;
526             $previous_identifier = 'globals';
527             $work_expression = substr($expression, 1);
528 
529             if (substr($work_expression, 0, 7) == 'request')
530             {
531                 $value = $_REQUEST;
532                 $previous_identifier = 'request';
533                 $work_expression = substr($expression, 8);
534             }
535             else if (substr($work_expression, 0, 6) == 'server')
536             {
537                 $value = $_SERVER;
538                 $previous_identifier = 'server';
539                 $work_expression = substr($expression, 7);
540             }
541             else if (substr($work_expression, 0, 6) == 'shared')
542             {
543                 $value = $this->context_shared;
544                 $previous_identifier = 'shared';
545                 $work_expression = substr($expression, 7);
546             }
547         }
548 
549         #
550         #
551         #
552 
553         $parts = self::parseExpression($work_expression);
554 
555         foreach ($parts as $part)
556         {
557             $identifier = $part[self::TOKEN_VALUE];
558 
559             switch ($part[self::TOKEN_TYPE])
560             {
561                 case self::TOKEN_TYPE_IDENTIFIER:
562                 {
563                     if (!is_array($value) && !is_object($value))
564                     {
565                         $this->error
566                         (
567                             'Unexpected variable type: %type (%value) for %identifier in expression %expression, should be either an array or an object', array
568                             (
569                                 '%type' => gettype($value),
570                                 '%value' => $value,
571                                 '%identifier' => $identifier,
572                                 '%expression' => $expression
573                             )
574                         );
575 
576                         return;
577                     }
578 
579                     $exists = false;
580 
581                     if (is_array($value))
582                     {
583                         $exists = array_key_exists($identifier, $value);
584                     }
585                     else
586                     {
587                         $exists = property_exists($value, $identifier);
588 
589                         if (!$exists && method_exists($value, 'has_property'))
590                         {
591                             $exists = $value->has_property($identifier);
592                         }
593                         else
594                         {
595                             if (!$exists && method_exists($value, 'offsetExists'))
596                             {
597                                 $exists = $value->offsetExists($identifier);
598                             }
599 
600                             if (!$exists && method_exists($value, '__get'))
601                             {
602                                 $exists = true;
603                             }
604                         }
605                     }
606 
607                     if (!$exists)
608                     {
609                         if (!$silent)
610                         {
611                             $this->error
612                             (
613                                 '%identifier of expression %expression does not exists in %var (defined: :keys) in: !value', array
614                                 (
615                                     '%identifier' => $identifier,
616                                     '%expression' => $expression,
617                                     '%var' => $previous_identifier,
618                                     ':keys' => implode(', ', array_keys((array) $value)),
619                                     '!value' => $value
620                                 )
621                             );
622                         }
623 
624                         return;
625                     }
626 
627                     $value = (is_array($value) || method_exists($value, 'offsetExists')) ? $value[$identifier] : $value->$identifier;
628                     $previous_identifier = $identifier;
629                 }
630                 break;
631 
632                 case self::TOKEN_TYPE_FUNCTION:
633                 {
634                     $method = $identifier;
635                     $args = $part[self::TOKEN_ARGS];
636                     $args_evaluate = $part[self::TOKEN_ARGS_EVALUATE];
637 
638                     if ($args_evaluate)
639                     {
640                         $this->error('we should evaluate %eval', array('%eval' => $args_evaluate));
641                     }
642 
643                     #
644                     # if value is an object, we check if the object has the method
645                     #
646 
647                     if (is_object($value) && method_exists($value, $method))
648                     {
649                         $value = call_user_func_array(array($value, $method), $args);
650 
651                         break;
652                     }
653 
654                     #
655                     # well, the object didn't have the method,
656                     # we check internal functions
657                     #
658 
659                     $callback = $this->findFunction($method);
660 
661                     #
662                     # if no internal function matches, we try string and array functions
663                     # depending on the type of the value
664                     #
665 
666                     if (!$callback)
667                     {
668                         if (is_string($value))
669                         {
670                             if (function_exists('str' . $method))
671                             {
672                                 $callback = 'str' . $method;
673                             }
674                             else if (function_exists('str_' . $method))
675                             {
676                                 $callback = 'str_' . $method;
677                             }
678                         }
679                         else if (is_array($value) || is_object($value))
680                         {
681                             if (function_exists('ICanBoogie\array_' . $method))
682                             {
683                                 $callback = 'ICanBoogie\array_' . $method;
684                             }
685                             else if (function_exists('array_' . $method))
686                             {
687                                 $callback = 'array_' . $method;
688                             }
689                         }
690                     }
691 
692                     #
693                     # our last hope is to try the function "as is"
694                     #
695 
696                     if (!$callback)
697                     {
698                         if (function_exists($method))
699                         {
700                             $callback = $method;
701                         }
702                     }
703 
704                     if (!$callback)
705                     {
706                         if (is_object($value) && method_exists($value, '__call'))
707                         {
708                             $value = call_user_func_array(array($value, $method), $args);
709 
710                             break;
711                         }
712                     }
713 
714                     #
715                     #
716                     #
717 
718                     if (!$callback)
719                     {
720                         $this->error
721                         (
722                             'Unknown method: %method for: %expression', array
723                             (
724                                 'method' => $method,
725                                 'expression' => $expression
726                             )
727                         );
728 
729                         return;
730                     }
731 
732                     #
733                     # create evaluation
734                     #
735 
736                     array_unshift($args, $value);
737 
738                     if (PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2))
739                     {
740                         if ($callback == 'array_shift')
741                         {
742                             $value = array_shift($value);
743                         }
744                         else
745                         {
746                             $value = call_user_func_array($callback, $args);
747                         }
748                     }
749                     else
750                     {
751                         $value = call_user_func_array($callback, $args);
752                     }
753                 }
754                 break;
755             }
756         }
757 
758         return $value;
759     }
760 
761     public function error($message, array $args=array())
762     {
763         Debug::trigger($message, $args);
764     }
765 }
Autodoc API documentation generated by ApiGen 2.8.0