Packages

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • HtmlNode
  • LatteCompiler
  • LatteFilter
  • LatteToken
  • MacroNode
  • MacroTokenizer
  • Parser
  • PhpWriter

Interfaces

  • IMacro

Exceptions

  • CompileException
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  * @package Nette\Latte
 11:  */
 12: 
 13: 
 14: 
 15: /**
 16:  * PHP code generator helpers.
 17:  *
 18:  * @author     David Grudl
 19:  * @package Nette\Latte
 20:  */
 21: class PhpWriter extends Object
 22: {
 23:     /** @var MacroTokenizer */
 24:     private $argsTokenizer;
 25: 
 26:     /** @var string */
 27:     private $modifiers;
 28: 
 29:     /** @var LatteCompiler */
 30:     private $compiler;
 31: 
 32: 
 33:     public static function using(MacroNode $node, LatteCompiler $compiler = NULL)
 34:     {
 35:         return new self($node->tokenizer, $node->modifiers, $compiler);
 36:     }
 37: 
 38: 
 39:     public function __construct(MacroTokenizer $argsTokenizer, $modifiers = NULL, LatteCompiler $compiler = NULL)
 40:     {
 41:         $this->argsTokenizer = $argsTokenizer;
 42:         $this->modifiers = $modifiers;
 43:         $this->compiler = $compiler;
 44:     }
 45: 
 46: 
 47:     /**
 48:      * Expands %node.word, %node.array, %node.args, %escape(), %modify(), %var, %raw in code.
 49:      * @param  string
 50:      * @return string
 51:      */
 52:     public function write($mask)
 53:     {
 54:         $args = func_get_args();
 55:         array_shift($args);
 56:         $word = strpos($mask, '%node.word') === FALSE ? NULL : $this->argsTokenizer->fetchWord();
 57:         $me = $this;
 58:         $mask = Strings::replace($mask, '#%escape(\(([^()]*+|(?1))+\))#', create_function('$m', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('me'=>$me)).'-1], EXTR_REFS);
 59:             return $me->escape(substr($m[1], 1, -1));
 60:         '));
 61:         $mask = Strings::replace($mask, '#%modify(\(([^()]*+|(?1))+\))#', create_function('$m', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('me'=>$me)).'-1], EXTR_REFS);
 62:             return $me->formatModifiers(substr($m[1], 1, -1));
 63:         '));
 64: 
 65:         return Strings::replace($mask, '#([,+]\s*)?%(node\.word|node\.array|node\.args|var|raw)(\?)?(\s*\+\s*)?()#',
 66:         create_function('$m', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('me'=>$me,'word'=> $word, 'args'=>& $args)).'-1], EXTR_REFS);
 67:             list(, $l, $macro, $cond, $r) = $m;
 68: 
 69:             switch ($macro) {
 70:                 case \'node.word\':
 71:                     $code = $me->formatWord($word); break;
 72:                 case \'node.args\':
 73:                     $code = $me->formatArgs(); break;
 74:                 case \'node.array\':
 75:                     $code = $me->formatArray();
 76:                     $code = $cond && $code === \'array()\' ? \'\' : $code; break;
 77:                 case \'var\':
 78:                     $code = var_export(array_shift($args), TRUE); break;
 79:                 case \'raw\':
 80:                     $code = (string) array_shift($args); break;
 81:             }
 82: 
 83:             if ($cond && $code === \'\') {
 84:                 return $r ? $l : $r;
 85:             } else {
 86:                 return $l . $code . $r;
 87:             }
 88:         '));
 89:     }
 90: 
 91: 
 92:     /**
 93:      * Formats modifiers calling.
 94:      * @param  string
 95:      * @return string
 96:      */
 97:     public function formatModifiers($var)
 98:     {
 99:         $modifiers = ltrim($this->modifiers, '|');
100:         if (!$modifiers) {
101:             return $var;
102:         }
103: 
104:         $tokenizer = $this->preprocess(new MacroTokenizer($modifiers));
105:         $inside = FALSE;
106:         while ($token = $tokenizer->fetchToken()) {
107:             if ($token['type'] === MacroTokenizer::T_WHITESPACE) {
108:                 $var = rtrim($var) . ' ';
109: 
110:             } elseif (!$inside) {
111:                 if ($token['type'] === MacroTokenizer::T_SYMBOL) {
112:                     if ($this->compiler && $token['value'] === 'escape') {
113:                         $var = $this->escape($var);
114:                         $tokenizer->fetch('|');
115:                     } else {
116:                         $var = "\$template->" . $token['value'] . "($var";
117:                         $inside = TRUE;
118:                     }
119:                 } else {
120:                     throw new CompileException("Modifier name must be alphanumeric string, '$token[value]' given.");
121:                 }
122:             } else {
123:                 if ($token['value'] === ':' || $token['value'] === ',') {
124:                     $var = $var . ', ';
125: 
126:                 } elseif ($token['value'] === '|') {
127:                     $var = $var . ')';
128:                     $inside = FALSE;
129: 
130:                 } else {
131:                     $var .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
132:                 }
133:             }
134:         }
135:         return $inside ? "$var)" : $var;
136:     }
137: 
138: 
139:     /**
140:      * Formats macro arguments to PHP code.
141:      * @return string
142:      */
143:     public function formatArgs()
144:     {
145:         $out = '';
146:         $tokenizer = $this->preprocess();
147:         while ($token = $tokenizer->fetchToken()) {
148:             $out .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
149:         }
150:         return $out;
151:     }
152: 
153: 
154:     /**
155:      * Formats macro arguments to PHP array.
156:      * @return string
157:      */
158:     public function formatArray()
159:     {
160:         $out = '';
161:         $expand = NULL;
162:         $tokenizer = $this->preprocess();
163:         while ($token = $tokenizer->fetchToken()) {
164:             if ($token['value'] === '(expand)' && $token['depth'] === 0) {
165:                 $expand = TRUE;
166:                 $out .= '),';
167: 
168:             } elseif ($expand && ($token['value'] === ',') && !$token['depth']) {
169:                 $expand = FALSE;
170:                 $out .= ', array(';
171:             } else {
172:                 $out .= $this->canQuote($tokenizer) ? "'$token[value]'" : $token['value'];
173:             }
174:         }
175:         if ($expand === NULL) {
176:             return "array($out)";
177:         } else {
178:             return "array_merge(array($out" . ($expand ? ', array(' : '') ."))";
179:         }
180:     }
181: 
182: 
183:     /**
184:      * Formats parameter to PHP string.
185:      * @param  string
186:      * @return string
187:      */
188:     public function formatWord($s)
189:     {
190:         return (is_numeric($s) || preg_match('#^\$|[\'"]|^true\z|^false\z|^null\z#i', $s))
191:             ? $s : '"' . $s . '"';
192:     }
193: 
194: 
195:     /**
196:      * @return bool
197:      */
198:     public function canQuote(MacroTokenizer $tokenizer)
199:     {
200:         return $tokenizer->isCurrent(MacroTokenizer::T_SYMBOL)
201:             && (!$tokenizer->hasPrev() || $tokenizer->isPrev(',', '(', '[', '=', '=>', ':', '?'))
202:             && (!$tokenizer->hasNext() || $tokenizer->isNext(',', ')', ']', '=', '=>', ':', '|'));
203:     }
204: 
205: 
206:     /**
207:      * Preprocessor for tokens.
208:      * @return MacroTokenizer
209:      */
210:     public function preprocess(MacroTokenizer $tokenizer = NULL)
211:     {
212:         $tokenizer = $tokenizer === NULL ? $this->argsTokenizer : $tokenizer;
213:         $inTernary = $prev = NULL;
214:         $tokens = $arrays = array();
215:         while ($token = $tokenizer->fetchToken()) {
216:             $token['depth'] = $depth = count($arrays);
217: 
218:             if ($token['type'] === MacroTokenizer::T_COMMENT) {
219:                 continue; // remove comments
220: 
221:             } elseif ($token['type'] === MacroTokenizer::T_WHITESPACE) {
222:                 $tokens[] = $token;
223:                 continue;
224:             }
225: 
226:             if ($token['value'] === '?') { // short ternary operators without :
227:                 $inTernary = $depth;
228: 
229:             } elseif ($token['value'] === ':') {
230:                 $inTernary = NULL;
231: 
232:             } elseif ($inTernary === $depth && ($token['value'] === ',' || $token['value'] === ')' || $token['value'] === ']')) { // close ternary
233:                 $tokens[] = MacroTokenizer::createToken(':') + array('depth' => $depth);
234:                 $tokens[] = MacroTokenizer::createToken('null') + array('depth' => $depth);
235:                 $inTernary = NULL;
236:             }
237: 
238:             if ($token['value'] === '[') { // simplified array syntax [...]
239:                 if ($arrays[] = $prev['value'] !== ']' && $prev['value'] !== ')' && $prev['type'] !== MacroTokenizer::T_SYMBOL
240:                     && $prev['type'] !== MacroTokenizer::T_VARIABLE && $prev['type'] !== MacroTokenizer::T_KEYWORD
241:                 ) {
242:                     $tokens[] = MacroTokenizer::createToken('array') + array('depth' => $depth);
243:                     $token = MacroTokenizer::createToken('(');
244:                 }
245:             } elseif ($token['value'] === ']') {
246:                 if (array_pop($arrays) === TRUE) {
247:                     $token = MacroTokenizer::createToken(')');
248:                 }
249:             } elseif ($token['value'] === '(') { // only count
250:                 $arrays[] = '(';
251: 
252:             } elseif ($token['value'] === ')') { // only count
253:                 array_pop($arrays);
254:             }
255: 
256:             $tokens[] = $prev = $token;
257:         }
258: 
259:         if ($inTernary !== NULL) { // close ternary
260:             $tokens[] = MacroTokenizer::createToken(':') + array('depth' => count($arrays));
261:             $tokens[] = MacroTokenizer::createToken('null') + array('depth' => count($arrays));
262:         }
263: 
264:         $tokenizer = clone $tokenizer;
265:         $tokenizer->reset();
266:         $tokenizer->tokens = $tokens;
267:         return $tokenizer;
268:     }
269: 
270: 
271:     public function escape($s)
272:     {
273:         switch ($this->compiler->getContentType()) {
274:             case LatteCompiler::CONTENT_XHTML:
275:             case LatteCompiler::CONTENT_HTML:
276:                 $context = $this->compiler->getContext();
277:                 switch ($context[0]) {
278:                     case LatteCompiler::CONTEXT_SINGLE_QUOTED:
279:                     case LatteCompiler::CONTEXT_DOUBLE_QUOTED:
280:                         if ($context[1] === LatteCompiler::CONTENT_JS) {
281:                             $s = "TemplateHelpers::escapeJs($s)";
282:                         } elseif ($context[1] === LatteCompiler::CONTENT_CSS) {
283:                             $s = "TemplateHelpers::escapeCss($s)";
284:                         }
285:                         $quote = $context[0] === LatteCompiler::CONTEXT_DOUBLE_QUOTED ? '' : ', ENT_QUOTES';
286:                         return "htmlSpecialChars($s$quote)";
287:                     case LatteCompiler::CONTEXT_COMMENT:
288:                         return "TemplateHelpers::escapeHtmlComment($s)";
289:                     case LatteCompiler::CONTENT_JS:
290:                     case LatteCompiler::CONTENT_CSS:
291:                         return 'TemplateHelpers::escape' . ucfirst($context[0]) . "($s)";
292:                     default:
293:                         return "TemplateHelpers::escapeHtml($s, ENT_NOQUOTES)";
294:                 }
295:             case LatteCompiler::CONTENT_XML:
296:             case LatteCompiler::CONTENT_JS:
297:             case LatteCompiler::CONTENT_CSS:
298:             case LatteCompiler::CONTENT_ICAL:
299:                 return 'TemplateHelpers::escape' . ucfirst($this->compiler->getContentType()) . "($s)";
300:             case LatteCompiler::CONTENT_TEXT:
301:                 return $s;
302:             default:
303:                 return "\$template->escape($s)";
304:         }
305:     }
306: 
307: }
308: 
Nette Framework 2.0.11 (for PHP 5.2, un-prefixed) API API documentation generated by ApiGen 2.8.0