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

Classes

Interfaces

Exceptions

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