Namespaces

  • 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

  • Compiler
  • Engine
  • HtmlNode
  • MacroNode
  • MacroTokenizer
  • Parser
  • PhpWriter
  • Token

Interfaces

  • IMacro

Exceptions

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