123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- <?php
- /*
- Copyright (c) 2009-2014 F3::Factory/Bong Cosca, All rights reserved.
- This file is part of the Fat-Free Framework (http://fatfree.sf.net).
- THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
- ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
- PURPOSE.
- Please see the license.txt file for more information.
- */
- //! XML-style template engine
- class Template extends Preview {
- //@{ Error messages
- const
- E_Method='Call to undefined method %s()';
- //@}
- protected
- //! Template tags
- $tags,
- //! Custom tag handlers
- $custom=array();
- /**
- * Template -set- tag handler
- * @return string
- * @param $node array
- **/
- protected function _set(array $node) {
- $out='';
- foreach ($node['@attrib'] as $key=>$val)
- $out.='$'.$key.'='.
- (preg_match('/\{\{(.+?)\}\}/',$val)?
- $this->token($val):
- Base::instance()->stringify($val)).'; ';
- return '<?php '.$out.'?>';
- }
- /**
- * Template -include- tag handler
- * @return string
- * @param $node array
- **/
- protected function _include(array $node) {
- $attrib=$node['@attrib'];
- $hive=isset($attrib['with']) &&
- ($attrib['with']=preg_match('/\{\{(.+?)\}\}/',$attrib['with'])?$this->token($attrib['with']):Base::instance()->stringify($attrib['with'])) &&
- preg_match_all('/(\w+)\h*=\h*(.+?)(?=,|$)/',$attrib['with'],$pairs,PREG_SET_ORDER)?
- 'array('.implode(',',array_map(function($pair){return "'$pair[1]'=>$pair[2]";},$pairs)).')+get_defined_vars()':
- 'get_defined_vars()';
- return
- '<?php '.(isset($attrib['if'])?
- ('if ('.$this->token($attrib['if']).') '):'').
- ('echo $this->render('.
- (preg_match('/\{\{(.+?)\}\}/',$attrib['href'])?
- $this->token($attrib['href']):
- Base::instance()->stringify($attrib['href'])).','.
- '$this->mime,'.$hive.'); ?>');
- }
- /**
- * Template -exclude- tag handler
- * @return string
- **/
- protected function _exclude() {
- return '';
- }
- /**
- * Template -ignore- tag handler
- * @return string
- * @param $node array
- **/
- protected function _ignore(array $node) {
- return $node[0];
- }
- /**
- * Template -loop- tag handler
- * @return string
- * @param $node array
- **/
- protected function _loop(array $node) {
- $attrib=$node['@attrib'];
- unset($node['@attrib']);
- return
- '<?php for ('.
- $this->token($attrib['from']).';'.
- $this->token($attrib['to']).';'.
- $this->token($attrib['step']).'): ?>'.
- $this->build($node).
- '<?php endfor; ?>';
- }
- /**
- * Template -repeat- tag handler
- * @return string
- * @param $node array
- **/
- protected function _repeat(array $node) {
- $attrib=$node['@attrib'];
- unset($node['@attrib']);
- return
- '<?php '.
- (isset($attrib['counter'])?
- (($ctr=$this->token($attrib['counter'])).'=0; '):'').
- 'foreach (('.
- $this->token($attrib['group']).'?:array()) as '.
- (isset($attrib['key'])?
- ($this->token($attrib['key']).'=>'):'').
- $this->token($attrib['value']).'):'.
- (isset($ctr)?(' '.$ctr.'++;'):'').' ?>'.
- $this->build($node).
- '<?php endforeach; ?>';
- }
- /**
- * Template -check- tag handler
- * @return string
- * @param $node array
- **/
- protected function _check(array $node) {
- $attrib=$node['@attrib'];
- unset($node['@attrib']);
- // Grab <true> and <false> blocks
- foreach ($node as $pos=>$block)
- if (isset($block['true']))
- $true=array($pos,$block);
- elseif (isset($block['false']))
- $false=array($pos,$block);
- if (isset($true,$false) && $true[0]>$false[0])
- // Reverse <true> and <false> blocks
- list($node[$true[0]],$node[$false[0]])=array($false[1],$true[1]);
- return
- '<?php if ('.$this->token($attrib['if']).'): ?>'.
- $this->build($node).
- '<?php endif; ?>';
- }
- /**
- * Template -true- tag handler
- * @return string
- * @param $node array
- **/
- protected function _true(array $node) {
- return $this->build($node);
- }
- /**
- * Template -false- tag handler
- * @return string
- * @param $node array
- **/
- protected function _false(array $node) {
- return '<?php else: ?>'.$this->build($node);
- }
- /**
- * Template -switch- tag handler
- * @return string
- * @param $node array
- **/
- protected function _switch(array $node) {
- $attrib=$node['@attrib'];
- unset($node['@attrib']);
- foreach ($node as $pos=>$block)
- if (is_string($block) && !preg_replace('/\s+/','',$block))
- unset($node[$pos]);
- return
- '<?php switch ('.$this->token($attrib['expr']).'): ?>'.
- $this->build($node).
- '<?php endswitch; ?>';
- }
- /**
- * Template -case- tag handler
- * @return string
- * @param $node array
- **/
- protected function _case(array $node) {
- $attrib=$node['@attrib'];
- unset($node['@attrib']);
- return
- '<?php case '.(preg_match('/\{\{(.+?)\}\}/',$attrib['value'])?
- $this->token($attrib['value']):
- Base::instance()->stringify($attrib['value'])).': ?>'.
- $this->build($node).
- '<?php '.(isset($attrib['break'])?
- 'if ('.$this->token($attrib['break']).') ':'').
- 'break; ?>';
- }
- /**
- * Template -default- tag handler
- * @return string
- * @param $node array
- **/
- protected function _default(array $node) {
- return
- '<?php default: ?>'.
- $this->build($node).
- '<?php break; ?>';
- }
- /**
- * Assemble markup
- * @return string
- * @param $node array|string
- **/
- protected function build($node) {
- if (is_string($node))
- return parent::build($node);
- $out='';
- foreach ($node as $key=>$val)
- $out.=is_int($key)?$this->build($val):$this->{'_'.$key}($val);
- return $out;
- }
- /**
- * Extend template with custom tag
- * @return NULL
- * @param $tag string
- * @param $func callback
- **/
- function extend($tag,$func) {
- $this->tags.='|'.$tag;
- $this->custom['_'.$tag]=$func;
- }
- /**
- * Call custom tag handler
- * @return string|FALSE
- * @param $func callback
- * @param $args array
- **/
- function __call($func,array $args) {
- if ($func[0]=='_')
- return call_user_func_array($this->custom[$func],$args);
- if (method_exists($this,$func))
- return call_user_func_array(array($this,$func),$args);
- user_error(sprintf(self::E_Method,$func));
- }
- /**
- * Parse string for template directives and tokens
- * @return string|array
- * @param $text string
- **/
- function parse($text) {
- // Build tree structure
- for ($ptr=0,$len=strlen($text),$tree=array(),$node=&$tree,
- $stack=array(),$depth=0,$tmp='';$ptr<$len;)
- if (preg_match('/^<(\/?)(?:F3:)?'.
- '('.$this->tags.')\b((?:\h+[\w-]+'.
- '(?:\h*=\h*(?:"(?:.+?)"|\'(?:.+?)\'))?|'.
- '\h*\{\{.+?\}\})*)\h*(\/?)>/is',
- substr($text,$ptr),$match)) {
- if (strlen($tmp))
- $node[]=$tmp;
- // Element node
- if ($match[1]) {
- // Find matching start tag
- $save=$depth;
- $found=FALSE;
- while ($depth>0) {
- $depth--;
- foreach ($stack[$depth] as $item)
- if (is_array($item) && isset($item[$match[2]])) {
- // Start tag found
- $found=TRUE;
- break 2;
- }
- }
- if (!$found)
- // Unbalanced tag
- $depth=$save;
- $node=&$stack[$depth];
- }
- else {
- // Start tag
- $stack[$depth]=&$node;
- $node=&$node[][$match[2]];
- if ($match[3]) {
- // Process attributes
- preg_match_all(
- '/(?:\b([\w-]+)\h*'.
- '(?:=\h*(?:"(.+?)"|\'(.+?)\'))?|'.
- '(\{\{.+?\}\}))/s',
- $match[3],$attr,PREG_SET_ORDER);
- foreach ($attr as $kv)
- if (isset($kv[4]))
- $node['@attrib'][]=$kv[4];
- else
- $node['@attrib'][$kv[1]]=
- (empty($kv[2])?
- (empty($kv[3])?NULL:$kv[3]):$kv[2]);
- }
- if ($match[4])
- // Empty tag
- $node=&$stack[$depth];
- else
- $depth++;
- }
- $tmp='';
- $ptr+=strlen($match[0]);
- }
- else {
- // Text node
- $tmp.=substr($text,$ptr,1);
- $ptr++;
- }
- if (strlen($tmp))
- // Append trailing text
- $node[]=$tmp;
- // Break references
- unset($node);
- unset($stack);
- return $tree;
- }
- /**
- * Class constructor
- * return object
- **/
- function __construct() {
- $ref=new ReflectionClass(__CLASS__);
- $this->tags='';
- foreach ($ref->getmethods() as $method)
- if (preg_match('/^_(?=[[:alpha:]])/',$method->name))
- $this->tags.=(strlen($this->tags)?'|':'').
- substr($method->name,1);
- }
- }
|