Smarty_Internal_Compile_Foreach::compile()   F
last analyzed

Complexity

Conditions 51
Paths > 20000

Size

Total Lines 171

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 51
nc 4294967295
nop 2
dl 0
loc 171
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
     *
84
     * @return string compiled code
85
     * @throws \SmartyCompilerException
86
     * @throws \SmartyException
87
     */
88
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
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(
123
                        "'{$a}' and 'from' may not have same variable name '{$fromName}'",
124
                        null,
125
                        true
126
                    );
127
                }
128
            }
129
        }
130
        $itemVar = "\$_smarty_tpl->tpl_vars['{$item}']";
131
        $local = '$__foreach_' . $attributes[ 'item' ] . '_' . $this->counter++ . '_';
132
        // search for used tag attributes
133
        $itemAttr = array();
134
        $namedAttr = array();
135
        $this->scanForProperties($attributes, $compiler);
136
        if (!empty($this->matchResults[ 'item' ])) {
137
            $itemAttr = $this->matchResults[ 'item' ];
138
        }
139
        if (!empty($this->matchResults[ 'named' ])) {
140
            $namedAttr = $this->matchResults[ 'named' ];
141
        }
142
        if (isset($_attr[ 'properties' ]) && preg_match_all('/[\'](.*?)[\']/', $_attr[ 'properties' ], $match)) {
143
            foreach ($match[ 1 ] as $prop) {
144
                if (in_array($prop, $this->itemProperties)) {
145
                    $itemAttr[ $prop ] = true;
146
                } else {
147
                    $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
148
                }
149
            }
150
            if ($this->isNamed) {
151
                foreach ($match[ 1 ] as $prop) {
152
                    if (in_array($prop, $this->nameProperties)) {
153
                        $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...
154
                    } else {
155
                        $compiler->trigger_template_error("Invalid property '{$prop}'", null, true);
156
                    }
157
                }
158
            }
159
        }
160
        if (isset($itemAttr[ 'first' ])) {
161
            $itemAttr[ 'index' ] = true;
162
        }
163
        if (isset($namedAttr[ 'first' ])) {
164
            $namedAttr[ 'index' ] = true;
165
        }
166
        if (isset($namedAttr[ 'last' ])) {
167
            $namedAttr[ 'iteration' ] = true;
168
            $namedAttr[ 'total' ] = true;
169
        }
170
        if (isset($itemAttr[ 'last' ])) {
171
            $itemAttr[ 'iteration' ] = true;
172
            $itemAttr[ 'total' ] = true;
173
        }
174
        if (isset($namedAttr[ 'show' ])) {
175
            $namedAttr[ 'total' ] = true;
176
        }
177
        if (isset($itemAttr[ 'show' ])) {
178
            $itemAttr[ 'total' ] = true;
179
        }
180
        $keyTerm = '';
181
        if (isset($attributes[ 'key' ])) {
182
            $keyTerm = "\$_smarty_tpl->tpl_vars['{$key}']->value => ";
183
        }
184
        if (isset($itemAttr[ 'key' ])) {
185
            $keyTerm = "{$itemVar}->key => ";
186
        }
187
        if ($this->isNamed) {
188
            $foreachVar = "\$_smarty_tpl->tpl_vars['__smarty_foreach_{$attributes['name']}']";
189
        }
190
        $needTotal = isset($itemAttr[ 'total' ]);
191
        // Register tag
192
        $this->openTag(
193
            $compiler,
194
            'foreach',
195
            array('foreach', $compiler->nocache, $local, $itemVar, empty($itemAttr) ? 1 : 2)
196
        );
197
        // maybe nocache because of nocache variables
198
        $compiler->nocache = $compiler->nocache | $compiler->tag_nocache;
199
        // generate output code
200
        $output = "<?php\n";
201
        $output .= "\$_from = \$_smarty_tpl->smarty->ext->_foreach->init(\$_smarty_tpl, $from, " .
202
                   var_export($item, true);
203
        if ($name || $needTotal || $key) {
204
            $output .= ', ' . var_export($needTotal, true);
205
        }
206
        if ($name || $key) {
207
            $output .= ', ' . var_export($key, true);
208
        }
209
        if ($name) {
210
            $output .= ', ' . var_export($name, true) . ', ' . var_export($namedAttr, true);
211
        }
212
        $output .= ");\n";
213
        if (isset($itemAttr[ 'show' ])) {
214
            $output .= "{$itemVar}->show = ({$itemVar}->total > 0);\n";
215
        }
216
        if (isset($itemAttr[ 'iteration' ])) {
217
            $output .= "{$itemVar}->iteration = 0;\n";
218
        }
219
        if (isset($itemAttr[ 'index' ])) {
220
            $output .= "{$itemVar}->index = -1;\n";
221
        }
222
        $output .= "if (\$_from !== null) {\n";
223
        $output .= "foreach (\$_from as {$keyTerm}{$itemVar}->value) {\n";
224
        if (isset($attributes[ 'key' ]) && isset($itemAttr[ 'key' ])) {
225
            $output .= "\$_smarty_tpl->tpl_vars['{$key}']->value = {$itemVar}->key;\n";
226
        }
227
        if (isset($itemAttr[ 'iteration' ])) {
228
            $output .= "{$itemVar}->iteration++;\n";
229
        }
230
        if (isset($itemAttr[ 'index' ])) {
231
            $output .= "{$itemVar}->index++;\n";
232
        }
233
        if (isset($itemAttr[ 'first' ])) {
234
            $output .= "{$itemVar}->first = !{$itemVar}->index;\n";
235
        }
236
        if (isset($itemAttr[ 'last' ])) {
237
            $output .= "{$itemVar}->last = {$itemVar}->iteration === {$itemVar}->total;\n";
238
        }
239
        if (isset($foreachVar)) {
240
            if (isset($namedAttr[ 'iteration' ])) {
241
                $output .= "{$foreachVar}->value['iteration']++;\n";
242
            }
243
            if (isset($namedAttr[ 'index' ])) {
244
                $output .= "{$foreachVar}->value['index']++;\n";
245
            }
246
            if (isset($namedAttr[ 'first' ])) {
247
                $output .= "{$foreachVar}->value['first'] = !{$foreachVar}->value['index'];\n";
248
            }
249
            if (isset($namedAttr[ 'last' ])) {
250
                $output .= "{$foreachVar}->value['last'] = {$foreachVar}->value['iteration'] === {$foreachVar}->value['total'];\n";
251
            }
252
        }
253
        if (!empty($itemAttr)) {
254
            $output .= "{$local}saved = {$itemVar};\n";
255
        }
256
        $output .= '?>';
257
        return $output;
258
    }
259
260
    /**
261
     * Compiles code for to restore saved template variables
262
     *
263
     * @param int $levels number of levels to restore
264
     *
265
     * @return string compiled code
266
     */
267
    public function compileRestore($levels)
268
    {
269
        return "\$_smarty_tpl->smarty->ext->_foreach->restore(\$_smarty_tpl, {$levels});";
270
    }
271
}
272
273
/**
274
 * Smarty Internal Plugin Compile Foreachelse Class
275
 *
276
 * @package    Smarty
277
 * @subpackage Compiler
278
 */
279
class Smarty_Internal_Compile_Foreachelse extends Smarty_Internal_CompileBase
280
{
281
    /**
282
     * Compiles code for the {foreachelse} tag
283
     *
284
     * @param array                                 $args     array with attributes from parser
285
     * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
286
     *
287
     * @return string compiled code
288
     */
289
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
290
    {
291
        // check and get attributes
292
        $_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...
293
        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...
294
        $this->openTag($compiler, 'foreachelse', array('foreachelse', $nocache, $local, $itemVar, 0));
295
        $output = "<?php\n";
296
        if ($restore === 2) {
297
            $output .= "{$itemVar} = {$local}saved;\n";
298
        }
299
        $output .= "}\n} else {\n?>";
300
        return $output;
301
    }
302
}
303
304
/**
305
 * Smarty Internal Plugin Compile Foreachclose Class
306
 *
307
 * @package    Smarty
308
 * @subpackage Compiler
309
 */
310
class Smarty_Internal_Compile_Foreachclose extends Smarty_Internal_CompileBase
311
{
312
    /**
313
     * Compiles code for the {/foreach} tag
314
     *
315
     * @param array                                 $args     array with attributes from parser
316
     * @param \Smarty_Internal_TemplateCompilerBase $compiler compiler object
317
     *
318
     * @return string compiled code
319
     * @throws \SmartyCompilerException
320
     */
321
    public function compile($args, Smarty_Internal_TemplateCompilerBase $compiler)
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...
322
    {
323
        $compiler->loopNesting--;
324
        // must endblock be nocache?
325
        if ($compiler->nocache) {
326
            $compiler->tag_nocache = true;
327
        }
328
        list(
329
            $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...
330
            ) = $this->closeTag($compiler, array('foreach', 'foreachelse'));
331
        $output = "<?php\n";
332
        if ($restore === 2) {
333
            $output .= "{$itemVar} = {$local}saved;\n";
334
        }
335
        if ($restore > 0) {
336
            $output .= "}\n";
337
        }
338
        $output .= "}\n";
339
        /* @var Smarty_Internal_Compile_Foreach $foreachCompiler */
340
        $foreachCompiler = $compiler->getTagCompiler('foreach');
341
        $output .= $foreachCompiler->compileRestore(1);
342
        $output .= "?>";
343
        return $output;
344
    }
345
}
346