Completed
Branch master (e379bd)
by Pierre-Henry
33:06
created

Smarty_Internal_Compile_Foreach::compileRestore()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Smarty Internal Plugin Compile Foreach
4
 * Compiles the {foreach} {foreachelse} {/foreach} tags
5
 *
6
 * @package    Smarty
7
 * @subpackage Compiler
8
 * @author     Uwe Tews
9
 */
10
11
/**
12
 * Smarty Internal Plugin Compile Foreach Class
13
 *
14
 * @package    Smarty
15
 * @subpackage Compiler
16
 */
17
class Smarty_Internal_Compile_Foreach extends Smarty_Internal_Compile_Private_ForeachSection
18
{
19
    /**
20
     * Attribute definition: Overwrites base class.
21
     *
22
     * @var array
23
     * @see Smarty_Internal_CompileBase
24
     */
25
    public $required_attributes = array('from', 'item');
26
27
    /**
28
     * Attribute definition: Overwrites base class.
29
     *
30
     * @var array
31
     * @see Smarty_Internal_CompileBase
32
     */
33
    public $optional_attributes = array('name', 'key', 'properties');
34
35
    /**
36
     * Attribute definition: Overwrites base class.
37
     *
38
     * @var array
39
     * @see Smarty_Internal_CompileBase
40
     */
41
    public $shorttag_order = array('from', 'item', 'key', 'name');
42
43
    /**
44
     * counter
45
     *
46
     * @var int
47
     */
48
    public $counter = 0;
49
50
    /**
51
     * Name of this tag
52
     *
53
     * @var string
54
     */
55
    public $tagName = 'foreach';
56
57
    /**
58
     * Valid properties of $smarty.foreach.name.xxx variable
59
     *
60
     * @var array
61
     */
62
    public $nameProperties = array('first', 'last', 'index', 'iteration', 'show', 'total');
63
64
    /**
65
     * Valid properties of $item@xxx variable
66
     *
67
     * @var array
68
     */
69
    public $itemProperties = array('first', 'last', 'index', 'iteration', 'show', 'total', 'key');
70
71
    /**
72
     * Flag if tag had name attribute
73
     *
74
     * @var bool
75
     */
76
    public $isNamed = false;
77
78
    /**
79
     * Compiles code for the {foreach} tag
80
     *
81
     * @param  array                                $args      array with attributes from parser
82
     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
83
     * @param  array                                $parameter array with compilation parameter
84
     *
85
     * @return string compiled code
86
     * @throws \SmartyCompilerException
87
     */
88
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
0 ignored issues
show
Unused Code introduced by
The parameter $parameter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
89
    {
90
        $compiler->loopNesting ++;
91
        // init
92
        $this->isNamed = false;
93
        // check and get attributes
94
        $_attr = $this->getAttributes($compiler, $args);
95
        $from = $_attr[ 'from' ];
96
        $item = $compiler->getId($_attr[ 'item' ]);
97
        if ($item === false) {
98
            $item = $compiler->getVariableName($_attr[ 'item' ]);
99
        }
100
        $key = $name = null;
101
        $attributes = array('item' => $item);
102
        if (isset($_attr[ 'key' ])) {
103
            $key = $compiler->getId($_attr[ 'key' ]);
104
            if ($key === false) {
105
                $key = $compiler->getVariableName($_attr[ 'key' ]);
106
            }
107
            $attributes[ 'key' ] = $key;
108
        }
109
        if (isset($_attr[ 'name' ])) {
110
            $this->isNamed = true;
111
            $name = $attributes[ 'name' ] = $compiler->getId($_attr[ 'name' ]);
112
        }
113
        foreach ($attributes as $a => $v) {
114
            if ($v === false) {
115
                $compiler->trigger_template_error("'{$a}' attribute/variable has illegal value", null, true);
116
            }
117
        }
118
        $fromName = $compiler->getVariableName($_attr[ 'from' ]);
119
        if ($fromName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $fromName of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120
            foreach (array('item', 'key') as $a) {
121
                if (isset($attributes[ $a ]) && $attributes[ $a ] == $fromName) {
122
                    $compiler->trigger_template_error("'{$a}' and 'from' may not have same variable name '{$fromName}'",
123
                                                      null, true);
124
                }
125
            }
126
        }
127
128
        $itemVar = "\$_smarty_tpl->tpl_vars['{$item}']";
129
        $local = '$__foreach_' . $attributes[ 'item' ] . '_' . $this->counter ++ . '_';
130
        // search for used tag attributes
131
        $itemAttr = array();
132
        $namedAttr = array();
133
        $this->scanForProperties($attributes, $compiler);
134
        if (!empty($this->matchResults[ 'item' ])) {
135
            $itemAttr = $this->matchResults[ 'item' ];
136
        }
137
        if (!empty($this->matchResults[ 'named' ])) {
138
            $namedAttr = $this->matchResults[ 'named' ];
139
        }
140
        if (isset($_attr[ 'properties' ]) && preg_match_all("/['](.*?)[']/", $_attr[ 'properties' ], $match)) {
141
            foreach ($match[ 1 ] as $prop) {
142
                if (in_array($prop, $this->itemProperties)) {
143
                    $itemAttr[ $prop ] = true;
144
                } else {
145
                    $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
146
                }
147
            }
148
            if ($this->isNamed) {
149
                foreach ($match[ 1 ] as $prop) {
150
                    if (in_array($prop, $this->nameProperties)) {
151
                        $nameAttr[ $prop ] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$nameAttr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $nameAttr = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
152
                    } else {
153
                        $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
154
                    }
155
                }
156
            }
157
        }
158
        if (isset($itemAttr[ 'first' ])) {
159
            $itemAttr[ 'index' ] = true;
160
        }
161
        if (isset($namedAttr[ 'first' ])) {
162
            $namedAttr[ 'index' ] = true;
163
        }
164
        if (isset($namedAttr[ 'last' ])) {
165
            $namedAttr[ 'iteration' ] = true;
166
            $namedAttr[ 'total' ] = true;
167
        }
168
        if (isset($itemAttr[ 'last' ])) {
169
            $itemAttr[ 'iteration' ] = true;
170
            $itemAttr[ 'total' ] = true;
171
        }
172
        if (isset($namedAttr[ 'show' ])) {
173
            $namedAttr[ 'total' ] = true;
174
        }
175
        if (isset($itemAttr[ 'show' ])) {
176
            $itemAttr[ 'total' ] = true;
177
        }
178
        $keyTerm = '';
179
        if (isset($attributes[ 'key' ])) {
180
            $keyTerm = "\$_smarty_tpl->tpl_vars['{$key}']->value => ";
181
        }
182
        if (isset($itemAttr[ 'key' ])) {
183
            $keyTerm = "{$itemVar}->key => ";
184
        }
185
        if ($this->isNamed) {
186
            $foreachVar = "\$_smarty_tpl->tpl_vars['__smarty_foreach_{$attributes['name']}']";
187
        }
188
        $needTotal = isset($itemAttr[ 'total' ]);
189
        // Register tag
190
        $this->openTag($compiler, 'foreach',
191
                       array('foreach', $compiler->nocache, $local, $itemVar, empty($itemAttr) ? 1 : 2));
192
        // maybe nocache because of nocache variables
193
        $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
194
        // generate output code
195
        $output = "<?php\n";
196
        $output .= "\$_from = \$_smarty_tpl->smarty->ext->_foreach->init(\$_smarty_tpl, $from, " .
197
                   var_export($item, true);
198
        if ($name || $needTotal || $key) {
199
            $output .= ', ' . var_export($needTotal, true);
200
        }
201
        if ($name || $key) {
202
            $output .= ', ' . var_export($key, true);
203
        }
204
        if ($name) {
205
            $output .= ', ' . var_export($name, true) . ', ' . var_export($namedAttr, true);
206
        }
207
        $output .= ");\n";
208
        if (isset($itemAttr[ 'show' ])) {
209
            $output .= "{$itemVar}->show = ({$itemVar}->total > 0);\n";
210
        }
211
        if (isset($itemAttr[ 'iteration' ])) {
212
            $output .= "{$itemVar}->iteration = 0;\n";
213
        }
214
        if (isset($itemAttr[ 'index' ])) {
215
            $output .= "{$itemVar}->index = -1;\n";
216
        }
217
        $output .= "if (\$_from !== null) {\n";
218
        $output .= "foreach (\$_from as {$keyTerm}{$itemVar}->value) {\n";
219
        if (isset($attributes[ 'key' ]) && isset($itemAttr[ 'key' ])) {
220
            $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = {$itemVar}->key;\n";
221
        }
222
        if (isset($itemAttr[ 'iteration' ])) {
223
            $output .= "{$itemVar}->iteration++;\n";
224
        }
225
        if (isset($itemAttr[ 'index' ])) {
226
            $output .= "{$itemVar}->index++;\n";
227
        }
228
        if (isset($itemAttr[ 'first' ])) {
229
            $output .= "{$itemVar}->first = !{$itemVar}->index;\n";
230
        }
231
        if (isset($itemAttr[ 'last' ])) {
232
            $output .= "{$itemVar}->last = {$itemVar}->iteration == {$itemVar}->total;\n";
233
        }
234
        if (isset($foreachVar)) {
235
            if (isset($namedAttr[ 'iteration' ])) {
236
                $output .= "{$foreachVar}->value['iteration']++;\n";
237
            }
238
            if (isset($namedAttr[ 'index' ])) {
239
                $output .= "{$foreachVar}->value['index']++;\n";
240
            }
241
            if (isset($namedAttr[ 'first' ])) {
242
                $output .= "{$foreachVar}->value['first'] = !{$foreachVar}->value['index'];\n";
243
            }
244
            if (isset($namedAttr[ 'last' ])) {
245
                $output .= "{$foreachVar}->value['last'] = {$foreachVar}->value['iteration'] == {$foreachVar}->value['total'];\n";
246
            }
247
        }
248
        if (!empty($itemAttr)) {
249
            $output .= "{$local}saved = {$itemVar};\n";
250
        }
251
        $output .= "?>";
252
253
        return $output;
254
    }
255
256
    /**
257
     * Compiles code for to restore saved template variables
258
     *
259
     * @param int $levels number of levels to restore
260
     *
261
     * @return string compiled code
262
     */
263
    public function compileRestore($levels)
264
    {
265
        return "\$_smarty_tpl->smarty->ext->_foreach->restore(\$_smarty_tpl, {$levels});\n";
266
    }
267
}
268
269
/**
270
 * Smarty Internal Plugin Compile Foreachelse Class
271
 *
272
 * @package    Smarty
273
 * @subpackage Compiler
274
 */
275
class Smarty_Internal_Compile_Foreachelse extends Smarty_Internal_CompileBase
276
{
277
    /**
278
     * Compiles code for the {foreachelse} tag
279
     *
280
     * @param  array                                $args      array with attributes from parser
281
     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
282
     * @param  array                                $parameter array with compilation parameter
283
     *
284
     * @return string compiled code
285
     */
286
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
0 ignored issues
show
Unused Code introduced by
The parameter $parameter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
287
    {
288
        // check and get attributes
289
        $_attr = $this->getAttributes($compiler, $args);
0 ignored issues
show
Unused Code introduced by
$_attr is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
290
291
        list($openTag, $nocache, $local, $itemVar, $restore) = $this->closeTag($compiler, array('foreach'));
0 ignored issues
show
Unused Code introduced by
The assignment to $openTag is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
292
        $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $local, $itemVar, 0));
293
        $output = "<?php\n";
294
        if ($restore == 2) {
295
            $output .= "{$itemVar} = {$local}saved;\n";
296
        }
297
        $output .= "}\n} else {\n?>\n";
298
        return $output;
299
    }
300
}
301
302
/**
303
 * Smarty Internal Plugin Compile Foreachclose Class
304
 *
305
 * @package    Smarty
306
 * @subpackage Compiler
307
 */
308
class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase
309
{
310
    /**
311
     * Compiles code for the {/foreach} tag
312
     *
313
     * @param  array                                $args      array with attributes from parser
314
     * @param \Smarty_Internal_TemplateCompilerBase $compiler  compiler object
315
     * @param  array                                $parameter array with compilation parameter
316
     *
317
     * @return string compiled code
318
     */
319
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler, $parameter)
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $parameter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
320
    {
321
        $compiler->loopNesting --;
322
        // must endblock be nocache?
323
        if ($compiler->nocache) {
324
            $compiler->tag_nocache = true;
325
        }
326
327
        list($openTag, $compiler->nocache, $local, $itemVar, $restore) =
0 ignored issues
show
Unused Code introduced by
The assignment to $openTag is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
328
            $this->closeTag($compiler, array('foreach', 'foreachelse'));
329
        $output = "<?php\n";
330
331
        if ($restore == 2) {
332
            $output .= "{$itemVar} = {$local}saved;\n";
333
        }
334
        if ($restore > 0) {
335
            $output .= "}\n";
336
        }
337
        $output .= "}\n";
338
        /* @var Smarty_Internal_Compile_Foreach $foreachCompiler */
339
        $foreachCompiler = $compiler->getTagCompiler('foreach');
340
        $output .= $foreachCompiler->compileRestore(1);
341
        $output .= "?>\n";
342
        return $output;
343
    }
344
}
345