Complex classes like Twig_Tests_TemplateTest 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_Tests_TemplateTest, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
11 | class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase |
||
12 | { |
||
13 | /** |
||
14 | * @dataProvider getAttributeExceptions |
||
15 | */ |
||
16 | public function testGetAttributeExceptions($template, $message, $useExt) |
||
17 | { |
||
18 | $name = 'index_'.($useExt ? 1 : 0); |
||
19 | $templates = array( |
||
20 | $name => $template.$useExt, // appending $useExt makes the template content unique |
||
21 | ); |
||
22 | |||
23 | $env = new Twig_Environment(new Twig_Loader_Array($templates), array('strict_variables' => true)); |
||
24 | if (!$useExt) { |
||
25 | $env->addNodeVisitor(new CExtDisablingNodeVisitor()); |
||
26 | } |
||
27 | $template = $env->loadTemplate($name); |
||
28 | |||
29 | $context = array( |
||
30 | 'string' => 'foo', |
||
31 | 'empty_array' => array(), |
||
32 | 'array' => array('foo' => 'foo'), |
||
33 | 'array_access' => new Twig_TemplateArrayAccessObject(), |
||
34 | 'magic_exception' => new Twig_TemplateMagicPropertyObjectWithException(), |
||
35 | 'object' => new stdClass(), |
||
36 | ); |
||
37 | |||
38 | try { |
||
39 | $template->render($context); |
||
40 | $this->fail('Accessing an invalid attribute should throw an exception.'); |
||
41 | } catch (Twig_Error_Runtime $e) { |
||
42 | $this->assertSame(sprintf($message, $name), $e->getMessage()); |
||
43 | } |
||
44 | } |
||
45 | |||
46 | public function getAttributeExceptions() |
||
47 | { |
||
48 | $tests = array( |
||
49 | array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false), |
||
50 | array('{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false), |
||
51 | array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false), |
||
52 | array('{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), |
||
53 | array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false), |
||
54 | array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false), |
||
55 | array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1', false), |
||
56 | array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false), |
||
57 | array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false), |
||
58 | array('{{ array_access.a }}', 'Method "a" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), |
||
59 | array('{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ _self.foo(array_access) }}', 'Method "missing_method" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false), |
||
60 | array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false), |
||
61 | array('{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1', false), |
||
62 | ); |
||
63 | |||
64 | if (function_exists('twig_template_get_attributes')) { |
||
65 | foreach (array_slice($tests, 0) as $test) { |
||
66 | $test[2] = true; |
||
67 | $tests[] = $test; |
||
68 | } |
||
69 | } |
||
70 | |||
71 | return $tests; |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * @dataProvider getGetAttributeWithSandbox |
||
76 | */ |
||
77 | public function testGetAttributeWithSandbox($object, $item, $allowed, $useExt) |
||
78 | { |
||
79 | $twig = new Twig_Environment(); |
||
80 | $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(/*method*/), array(/*prop*/), array()); |
||
81 | $twig->addExtension(new Twig_Extension_Sandbox($policy, !$allowed)); |
||
82 | $template = new Twig_TemplateTest($twig, $useExt); |
||
83 | |||
84 | try { |
||
85 | $template->getAttribute($object, $item, array(), 'any'); |
||
86 | |||
87 | if (!$allowed) { |
||
88 | $this->fail(); |
||
89 | } |
||
90 | } catch (Twig_Sandbox_SecurityError $e) { |
||
91 | if ($allowed) { |
||
92 | $this->fail(); |
||
93 | } |
||
94 | |||
95 | $this->assertContains('is not allowed', $e->getMessage()); |
||
96 | } |
||
97 | } |
||
98 | |||
99 | public function getGetAttributeWithSandbox() |
||
100 | { |
||
101 | $tests = array( |
||
102 | array(new Twig_TemplatePropertyObject(), 'defined', false, false), |
||
103 | array(new Twig_TemplatePropertyObject(), 'defined', true, false), |
||
104 | array(new Twig_TemplateMethodObject(), 'defined', false, false), |
||
105 | array(new Twig_TemplateMethodObject(), 'defined', true, false), |
||
106 | ); |
||
107 | |||
108 | if (function_exists('twig_template_get_attributes')) { |
||
109 | foreach (array_slice($tests, 0) as $test) { |
||
110 | $test[3] = true; |
||
111 | $tests[] = $test; |
||
112 | } |
||
113 | } |
||
114 | |||
115 | return $tests; |
||
116 | } |
||
117 | |||
118 | /** |
||
119 | * @dataProvider getGetAttributeWithTemplateAsObject |
||
120 | */ |
||
121 | public function testGetAttributeWithTemplateAsObject($useExt) |
||
122 | { |
||
123 | $template = new Twig_TemplateTest(new Twig_Environment(), $useExt); |
||
124 | $template1 = new Twig_TemplateTest(new Twig_Environment(), false); |
||
125 | |||
126 | $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string')); |
||
127 | $this->assertEquals('some_string', $template->getAttribute($template1, 'string')); |
||
128 | |||
129 | $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true')); |
||
130 | $this->assertEquals('1', $template->getAttribute($template1, 'true')); |
||
131 | |||
132 | $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero')); |
||
133 | $this->assertEquals('0', $template->getAttribute($template1, 'zero')); |
||
134 | |||
135 | $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty')); |
||
136 | $this->assertSame('', $template->getAttribute($template1, 'empty')); |
||
137 | } |
||
138 | |||
139 | public function getGetAttributeWithTemplateAsObject() |
||
140 | { |
||
141 | $bools = array( |
||
142 | array(false), |
||
143 | ); |
||
144 | |||
145 | if (function_exists('twig_template_get_attributes')) { |
||
146 | $bools[] = array(true); |
||
147 | } |
||
148 | |||
149 | return $bools; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * @dataProvider getTestsDependingOnExtensionAvailability |
||
154 | */ |
||
155 | public function testGetAttributeOnArrayWithConfusableKey($useExt = false) |
||
156 | { |
||
157 | $template = new Twig_TemplateTest( |
||
158 | new Twig_Environment(), |
||
159 | $useExt |
||
160 | ); |
||
161 | |||
162 | $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros'); |
||
163 | |||
164 | $this->assertSame('Zero', $array[false]); |
||
165 | $this->assertSame('One', $array[true]); |
||
166 | $this->assertSame('One', $array[1.5]); |
||
167 | $this->assertSame('One', $array['1']); |
||
168 | $this->assertSame('MinusOne', $array[-1.5]); |
||
169 | $this->assertSame('FloatButString', $array['1.5']); |
||
170 | $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']); |
||
171 | $this->assertSame('EmptyString', $array[null]); |
||
172 | |||
173 | $this->assertSame('Zero', $template->getAttribute($array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)'); |
||
174 | $this->assertSame('One', $template->getAttribute($array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)'); |
||
175 | $this->assertSame('One', $template->getAttribute($array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)'); |
||
176 | $this->assertSame('One', $template->getAttribute($array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)'); |
||
177 | $this->assertSame('MinusOne', $template->getAttribute($array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)'); |
||
178 | $this->assertSame('FloatButString', $template->getAttribute($array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)'); |
||
179 | $this->assertSame('IntegerButStringWithLeadingZeros', $template->getAttribute($array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)'); |
||
180 | $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)'); |
||
181 | } |
||
182 | |||
183 | public function getTestsDependingOnExtensionAvailability() |
||
184 | { |
||
185 | if (function_exists('twig_template_get_attributes')) { |
||
186 | return array(array(false), array(true)); |
||
187 | } |
||
188 | |||
189 | return array(array(false)); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * @dataProvider getGetAttributeTests |
||
194 | */ |
||
195 | public function testGetAttribute($defined, $value, $object, $item, $arguments, $type, $useExt = false) |
||
196 | { |
||
197 | $template = new Twig_TemplateTest(new Twig_Environment(), $useExt); |
||
198 | |||
199 | $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type)); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * @dataProvider getGetAttributeTests |
||
204 | */ |
||
205 | public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false, $exceptionMessage = null) |
||
223 | |||
224 | /** |
||
225 | * @dataProvider getGetAttributeTests |
||
226 | */ |
||
227 | public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type, $useExt = false) |
||
228 | { |
||
229 | $template = new Twig_TemplateTest(new Twig_Environment(), $useExt); |
||
230 | |||
231 | $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true)); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * @dataProvider getGetAttributeTests |
||
236 | */ |
||
237 | public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false) |
||
243 | |||
244 | /** |
||
245 | * @dataProvider getTestsDependingOnExtensionAvailability |
||
246 | */ |
||
247 | public function testGetAttributeCallExceptions($useExt = false) |
||
255 | |||
256 | public function getGetAttributeTests() |
||
389 | } |
||
390 | |||
667 |
Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a
@return
annotation as described here.