Complex classes like Twig_Parser often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Twig_Parser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
18 | class Twig_Parser implements Twig_ParserInterface |
||
|
|||
19 | { |
||
20 | protected $stack = array(); |
||
21 | protected $stream; |
||
22 | protected $parent; |
||
23 | protected $handlers; |
||
24 | protected $visitors; |
||
25 | protected $expressionParser; |
||
26 | protected $blocks; |
||
27 | protected $blockStack; |
||
28 | protected $macros; |
||
29 | protected $env; |
||
30 | protected $reservedMacroNames; |
||
31 | protected $importedSymbols; |
||
32 | protected $traits; |
||
33 | protected $embeddedTemplates = array(); |
||
34 | |||
35 | /** |
||
36 | * Constructor. |
||
37 | * |
||
38 | * @param Twig_Environment $env A Twig_Environment instance |
||
39 | */ |
||
40 | public function __construct(Twig_Environment $env) |
||
44 | |||
45 | public function getEnvironment() |
||
49 | |||
50 | public function getVarName() |
||
54 | |||
55 | public function getFilename() |
||
59 | |||
60 | /** |
||
61 | * {@inheritdoc} |
||
62 | */ |
||
63 | public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false) |
||
64 | { |
||
65 | // push all variables into the stack to keep the current state of the parser |
||
66 | $vars = get_object_vars($this); |
||
67 | unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']); |
||
68 | $this->stack[] = $vars; |
||
69 | |||
70 | // tag handlers |
||
71 | if (null === $this->handlers) { |
||
72 | $this->handlers = $this->env->getTokenParsers(); |
||
73 | $this->handlers->setParser($this); |
||
74 | } |
||
75 | |||
76 | // node visitors |
||
77 | if (null === $this->visitors) { |
||
78 | $this->visitors = $this->env->getNodeVisitors(); |
||
79 | } |
||
80 | |||
81 | if (null === $this->expressionParser) { |
||
82 | $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators()); |
||
83 | } |
||
84 | |||
85 | $this->stream = $stream; |
||
86 | $this->parent = null; |
||
87 | $this->blocks = array(); |
||
88 | $this->macros = array(); |
||
89 | $this->traits = array(); |
||
90 | $this->blockStack = array(); |
||
91 | $this->importedSymbols = array(array()); |
||
92 | $this->embeddedTemplates = array(); |
||
93 | |||
94 | try { |
||
95 | $body = $this->subparse($test, $dropNeedle); |
||
96 | |||
97 | if (null !== $this->parent) { |
||
98 | if (null === $body = $this->filterBodyNodes($body)) { |
||
99 | $body = new Twig_Node(); |
||
100 | } |
||
101 | } |
||
102 | } catch (Twig_Error_Syntax $e) { |
||
103 | if (!$e->getTemplateFile()) { |
||
104 | $e->setTemplateFile($this->getFilename()); |
||
105 | } |
||
106 | |||
107 | if (!$e->getTemplateLine()) { |
||
108 | $e->setTemplateLine($this->stream->getCurrent()->getLine()); |
||
109 | } |
||
110 | |||
111 | throw $e; |
||
112 | } |
||
113 | |||
114 | $node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename()); |
||
115 | |||
116 | $traverser = new Twig_NodeTraverser($this->env, $this->visitors); |
||
117 | |||
118 | $node = $traverser->traverse($node); |
||
119 | |||
120 | // restore previous stack so previous parse() call can resume working |
||
121 | foreach (array_pop($this->stack) as $key => $val) { |
||
122 | $this->$key = $val; |
||
123 | } |
||
124 | |||
125 | return $node; |
||
126 | } |
||
127 | |||
128 | public function subparse($test, $dropNeedle = false) |
||
129 | { |
||
130 | $lineno = $this->getCurrentToken()->getLine(); |
||
131 | $rv = array(); |
||
132 | while (!$this->stream->isEOF()) { |
||
133 | switch ($this->getCurrentToken()->getType()) { |
||
134 | case Twig_Token::TEXT_TYPE: |
||
135 | $token = $this->stream->next(); |
||
136 | $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine()); |
||
137 | break; |
||
138 | |||
139 | case Twig_Token::VAR_START_TYPE: |
||
140 | $token = $this->stream->next(); |
||
141 | $expr = $this->expressionParser->parseExpression(); |
||
142 | $this->stream->expect(Twig_Token::VAR_END_TYPE); |
||
143 | $rv[] = new Twig_Node_Print($expr, $token->getLine()); |
||
144 | break; |
||
145 | |||
146 | case Twig_Token::BLOCK_START_TYPE: |
||
147 | $this->stream->next(); |
||
148 | $token = $this->getCurrentToken(); |
||
149 | |||
150 | if ($token->getType() !== Twig_Token::NAME_TYPE) { |
||
151 | throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename()); |
||
152 | } |
||
153 | |||
154 | if (null !== $test && call_user_func($test, $token)) { |
||
155 | if ($dropNeedle) { |
||
156 | $this->stream->next(); |
||
157 | } |
||
158 | |||
159 | if (1 === count($rv)) { |
||
160 | return $rv[0]; |
||
161 | } |
||
162 | |||
163 | return new Twig_Node($rv, array(), $lineno); |
||
164 | } |
||
165 | |||
166 | $subparser = $this->handlers->getTokenParser($token->getValue()); |
||
167 | if (null === $subparser) { |
||
168 | if (null !== $test) { |
||
169 | $error = sprintf('Unexpected tag name "%s"', $token->getValue()); |
||
170 | if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) { |
||
171 | $error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno); |
||
172 | } |
||
173 | |||
174 | throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename()); |
||
175 | } |
||
176 | |||
177 | $message = sprintf('Unknown tag name "%s"', $token->getValue()); |
||
178 | if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) { |
||
179 | $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives)); |
||
180 | } |
||
181 | |||
182 | throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename()); |
||
183 | } |
||
184 | |||
185 | $this->stream->next(); |
||
186 | |||
187 | $node = $subparser->parse($token); |
||
188 | if (null !== $node) { |
||
189 | $rv[] = $node; |
||
190 | } |
||
191 | break; |
||
192 | |||
193 | default: |
||
194 | throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename()); |
||
195 | } |
||
196 | } |
||
197 | |||
198 | if (1 === count($rv)) { |
||
199 | return $rv[0]; |
||
200 | } |
||
201 | |||
202 | return new Twig_Node($rv, array(), $lineno); |
||
203 | } |
||
204 | |||
205 | public function addHandler($name, $class) |
||
209 | |||
210 | public function addNodeVisitor(Twig_NodeVisitorInterface $visitor) |
||
214 | |||
215 | public function getBlockStack() |
||
219 | |||
220 | public function peekBlockStack() |
||
224 | |||
225 | public function popBlockStack() |
||
229 | |||
230 | public function pushBlockStack($name) |
||
234 | |||
235 | public function hasBlock($name) |
||
239 | |||
240 | public function getBlock($name) |
||
244 | |||
245 | public function setBlock($name, Twig_Node_Block $value) |
||
249 | |||
250 | public function hasMacro($name) |
||
254 | |||
255 | public function setMacro($name, Twig_Node_Macro $node) |
||
271 | |||
272 | public function addTrait($trait) |
||
276 | |||
277 | public function hasTraits() |
||
281 | |||
282 | public function embedTemplate(Twig_Node_Module $template) |
||
283 | { |
||
284 | $template->setIndex(mt_rand()); |
||
285 | |||
286 | $this->embeddedTemplates[] = $template; |
||
287 | } |
||
288 | |||
289 | public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null) |
||
293 | |||
294 | public function getImportedSymbol($type, $alias) |
||
295 | { |
||
296 | foreach ($this->importedSymbols as $functions) { |
||
297 | if (isset($functions[$type][$alias])) { |
||
298 | return $functions[$type][$alias]; |
||
299 | } |
||
300 | } |
||
301 | } |
||
302 | |||
303 | public function isMainScope() |
||
307 | |||
308 | public function pushLocalScope() |
||
312 | |||
313 | public function popLocalScope() |
||
317 | |||
318 | /** |
||
319 | * Gets the expression parser. |
||
320 | * |
||
321 | * @return Twig_ExpressionParser The expression parser |
||
322 | */ |
||
323 | public function getExpressionParser() |
||
327 | |||
328 | public function getParent() |
||
332 | |||
333 | public function setParent($parent) |
||
337 | |||
338 | /** |
||
339 | * Gets the token stream. |
||
340 | * |
||
341 | * @return Twig_TokenStream The token stream |
||
342 | */ |
||
343 | public function getStream() |
||
347 | |||
348 | /** |
||
349 | * Gets the current token. |
||
350 | * |
||
351 | * @return Twig_Token The current token |
||
352 | */ |
||
353 | public function getCurrentToken() |
||
357 | |||
358 | protected function filterBodyNodes(Twig_NodeInterface $node) |
||
359 | { |
||
360 | // check that the body does not contain non-empty output nodes |
||
361 | if ( |
||
390 | } |
||
391 |
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.