Packages

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

Classes

  • LatteFilter
  • MacroNode
  • MacroTokenizer
  • Parser
  • PhpWriter

Interfaces

  • IMacro

Exceptions

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