1+ <?php
2+ /**
3+ * Smarty PHPunit tests for object chain functionality after function calls
4+ * Tests the new feature: {$x = collect($data)->filter()->values()->toJson()}
5+ *
6+ * @author Smarty Vibe
7+ */
8+
9+ /**
10+ * Helper class to simulate a chainable collection object
11+ */
12+ class ChainableCollection
13+ {
14+ private $ data ;
15+
16+ public function __construct ($ data )
17+ {
18+ $ this ->data = $ data ;
19+ }
20+
21+ public function filter ()
22+ {
23+ $ this ->data = array_filter ($ this ->data );
24+ return $ this ;
25+ }
26+
27+ public function values ()
28+ {
29+ $ this ->data = array_values ($ this ->data );
30+ return $ this ;
31+ }
32+
33+ public function toJson ()
34+ {
35+ return json_encode ($ this ->data );
36+ }
37+
38+ public function toArray ()
39+ {
40+ return $ this ->data ;
41+ }
42+
43+ public function count ()
44+ {
45+ return count ($ this ->data );
46+ }
47+ }
48+
49+ /**
50+ * Modifier plugin that returns a chainable object (simulates collect())
51+ */
52+ function smarty_modifier_collect ($ data )
53+ {
54+ return new ChainableCollection ($ data );
55+ }
56+
57+ /**
58+ * Modifier plugin that returns an object with methods
59+ */
60+ function smarty_modifier_create_object ($ value = null )
61+ {
62+ return new class {
63+ public function getName () {
64+ return 'TestObject ' ;
65+ }
66+
67+ public function getNext () {
68+ return new class {
69+ public function getValue () {
70+ return 'ChainedValue ' ;
71+ }
72+ };
73+ }
74+ };
75+ }
76+
77+ /**
78+ * class for function object chain tests
79+ *
80+ * @preserveGlobalState disabled
81+ *
82+ */
83+ class FunctionObjectChainTest extends PHPUnit_Smarty
84+ {
85+ public function setUp (): void
86+ {
87+ $ this ->setUpSmarty (__DIR__ );
88+
89+ // Register modifier plugins that return chainable objects
90+ // These are called like functions: {collect($data)}
91+ $ this ->smarty ->registerPlugin (\Smarty \Smarty::PLUGIN_MODIFIER , 'collect ' , 'smarty_modifier_collect ' );
92+ $ this ->smarty ->registerPlugin (\Smarty \Smarty::PLUGIN_MODIFIER , 'create_object ' , 'smarty_modifier_create_object ' );
93+ }
94+
95+ public function testInit ()
96+ {
97+ $ this ->cleanDirs ();
98+ }
99+
100+ /**
101+ * Test the NEW feature: function call followed by method chain
102+ * This is the core test for: {$x = collect($data)->filter()->values()->toJson()}
103+ */
104+ public function testFunctionCallWithMethodChain ()
105+ {
106+ $ data = [1 , 2 , 0 , 3 , null , 4 , '' , 5 ];
107+ $ this ->smarty ->assign ('data ' , $ data );
108+
109+ // Test the new syntax: function()->method()->method()->method()
110+ $ result = $ this ->smarty ->fetch ('string:{collect($data)->filter()->values()->toJson()} ' );
111+ $ this ->assertEquals ('[1,2,3,4,5] ' , $ result );
112+ }
113+
114+ /**
115+ * Test function call with method chain assigned to a variable
116+ * This tests: {$x = collect($data)->filter()->values()->toJson()}
117+ */
118+ public function testFunctionCallWithMethodChainAssignment ()
119+ {
120+ $ data = ['a ' , 'b ' , '' , 'c ' , null , 'd ' ];
121+ $ this ->smarty ->assign ('data ' , $ data );
122+
123+ // Test assignment with chained methods
124+ $ result = $ this ->smarty ->fetch ('string:{$result = collect($data)->filter()->values()->toJson()}{$result} ' );
125+ $ this ->assertEquals ('["a","b","c","d"] ' , $ result );
126+ }
127+
128+ /**
129+ * Test function call with single method chain
130+ */
131+ public function testFunctionCallWithSingleMethod ()
132+ {
133+ $ data = [1 , 2 , 3 ];
134+ $ this ->smarty ->assign ('data ' , $ data );
135+
136+ $ result = $ this ->smarty ->fetch ('string:{collect($data)->count()} ' );
137+ $ this ->assertEquals ('3 ' , $ result );
138+ }
139+
140+ /**
141+ * Test function call with nested method chains
142+ */
143+ public function testFunctionCallWithNestedChain ()
144+ {
145+ $ result = $ this ->smarty ->fetch ('string:{create_object("")->getNext()->getValue()} ' );
146+ $ this ->assertEquals ('ChainedValue ' , $ result );
147+ }
148+
149+ /**
150+ * Test that old syntax still works (two-step process)
151+ */
152+ public function testOldSyntaxStillWorks ()
153+ {
154+ $ data = [1 , 2 , 0 , 3 ];
155+ $ this ->smarty ->assign ('data ' , $ data );
156+
157+ // Old syntax: assign to variable first, then chain
158+ $ result = $ this ->smarty ->fetch ('string:{$x = collect($data)}{$x->filter()->values()->toJson()} ' );
159+ $ this ->assertEquals ('[1,2,3] ' , $ result );
160+ }
161+
162+ /**
163+ * Test object chain functionality using template file
164+ */
165+ public function testFunctionObjectChainFromTemplateFile ()
166+ {
167+ $ data = ['foo ' , '' , 'bar ' , null , 'baz ' ];
168+ $ this ->smarty ->assign ('data ' , $ data );
169+
170+ $ result = $ this ->smarty ->fetch ('test_function_chain.tpl ' );
171+ // Expected: JSON of filtered array and count
172+ $ this ->assertStringContainsString ('["foo","bar","baz"] ' , $ result );
173+ $ this ->assertStringContainsString ('3 ' , $ result );
174+ }
175+
176+ /**
177+ * Test complex chaining with multiple operations
178+ * This demonstrates the power of the new feature
179+ */
180+ public function testComplexChaining ()
181+ {
182+ $ data = [1 , 2 , 0 , 3 , '' , 4 , null , 5 , false , 6 ];
183+ $ this ->smarty ->assign ('data ' , $ data );
184+
185+ // Complex chain: collect -> filter -> values -> toArray
186+ $ result = $ this ->smarty ->fetch ('string:{$filtered = collect($data)->filter()->values()->toArray()}{$filtered|@json_encode} ' );
187+ $ this ->assertEquals ('[1,2,3,4,5,6] ' , $ result );
188+ }
189+
190+ /**
191+ * Test that demonstrates the benefit: one line vs multiple lines
192+ */
193+ public function testNewSyntaxVsOldSyntax ()
194+ {
195+ $ data = [10 , 20 , 0 , 30 ];
196+ $ this ->smarty ->assign ('data ' , $ data );
197+
198+ // NEW syntax (single line)
199+ $ new = $ this ->smarty ->fetch ('string:{collect($data)->filter()->count()} ' );
200+
201+ // OLD syntax (multiple steps)
202+ $ old = $ this ->smarty ->fetch ('string:{$temp = collect($data)}{$temp = $temp->filter()}{$temp->count()} ' );
203+
204+ // Both should produce the same result
205+ $ this ->assertEquals ('3 ' , $ new );
206+ $ this ->assertEquals ($ new , $ old );
207+ }
208+ }
0 commit comments