GimmeListNode   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 198
Duplicated Lines 10.61 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 4
dl 21
loc 198
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 51 8
F compile() 21 121 13

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
/*
4
 * This file is part of the Superdesk Web Publisher Templates System.
5
 *
6
 * Copyright 2015 Sourcefabric z.ú. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2015 Sourcefabric z.ú
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace SWP\Component\TemplatesSystem\Twig\Node;
16
17
use Twig\Node\ForLoopNode;
18
use Twig\Node\IfNode;
19
20
/**
21
 * Gimme twig node.
22
 */
23
class GimmeListNode extends \Twig\Node\Node
24
{
25
    protected static $count = 1;
26
27
    protected $loop;
28
29
    /**
30
     * GimmeListNode constructor.
31
     *
32
     * @param \Twig\Node\Node      $variable
33
     * @param \Twig\Node\Node      $collectionType
34
     * @param \Twig\Node\Node|null $collectionFilters
35
     * @param \Twig\Node\Node|null $withParameters
36
     * @param \Twig\Node\Node|null $withoutParameters
37
     * @param \Twig\Node\Node|null $ignoreContext
38
     * @param \Twig\Node\Node|null $ifExpression
39
     * @param \Twig\Node\Node|null $else
40
     * @param \Twig\Node\Node      $body
41
     * @param int                  $lineno
42
     * @param null                 $tag
43
     */
44
    public function __construct(
45
        \Twig\Node\Node $variable,
46
        \Twig\Node\Node $collectionType,
47
        \Twig\Node\Expression\FilterExpression $collectionFilters = null,
48
        \Twig\Node\Expression\AbstractExpression $withParameters = null,
49
        \Twig\Node\Expression\AbstractExpression $withoutParameters = null,
50
        \Twig\Node\Expression\AbstractExpression $ignoreContext = null,
51
        \Twig\Node\Expression\AbstractExpression $ifExpression = null,
52
        \Twig\Node\Node $else = null,
53
        \Twig\Node\Node $body,
54
        $lineno,
55
        $tag = null
56
    ) {
57
        $body = new \Twig\Node\Node([$body, $this->loop = new ForLoopNode($lineno, $tag)]);
58
59
        if (null !== $ifExpression) {
60
            $body = new IfNode(new \Twig\Node\Node([$ifExpression, $body]), null, $lineno, $tag);
61
        }
62
63
        $nodes = [
64
            'variable' => $variable,
65
            'collectionType' => $collectionType,
66
            'body' => $body,
67
        ];
68
69
        if (!is_null($withParameters)) {
70
            $nodes['withParameters'] = $withParameters;
71
        }
72
73
        if (!is_null($withoutParameters)) {
74
            $nodes['withoutParameters'] = $withoutParameters;
75
        }
76
77
        if (!is_null($ignoreContext)) {
78
            $nodes['ignoreContext'] = $ignoreContext;
79
        }
80
81
        if (!is_null($collectionFilters)) {
82
            $nodes['collectionFilters'] = $collectionFilters;
83
        }
84
85
        if (!is_null($ifExpression)) {
86
            $nodes['ifExpression'] = $ifExpression;
87
        }
88
89
        if (!is_null($else)) {
90
            $nodes['else'] = $else;
91
        }
92
93
        parent::__construct($nodes, ['with_loop' => true, 'ifexpr' => null !== $ifExpression], $lineno, $tag);
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function compile(\Twig\Compiler $compiler)
100
    {
101
        $i = self::$count++;
102
103
        $collectionTypeName = $this->getNode('collectionType')->getNode(0)->getAttribute('name');
104
105
        $compiler
106
            ->addDebugInfo($this);
107
108
        if ($this->hasNode('collectionFilters')) {
109
            $compiler->write("\$context['_collection_type_filters'] = [];\n");
110
            $compiler->write("\$context['".$collectionTypeName."'] = null;\n");
111
            $compiler->write("\$context['_collection_type_filters'] = ")->subcompile($this->getNode('collectionFilters'))->raw("['_collection_type_filters']; unset(\$context['".$collectionTypeName."']['_collection_type_filters']);\n");
112
113 View Code Duplication
            if ($this->hasNode('withParameters')) {
114
                $compiler->write('$withParameters = array_merge(')->subcompile($this->getNode('withParameters'))->raw(", \$context['_collection_type_filters']);\n");
115
            } else {
116
                $compiler->write("\$withParameters = \$context['_collection_type_filters'];\n");
117
            }
118 View Code Duplication
        } else {
119
            if ($this->hasNode('withParameters')) {
120
                $compiler->raw('$withParameters = ')->subcompile($this->getNode('withParameters'))->raw(";\n");
121
            } else {
122
                $compiler->raw("\$withParameters = [];\n");
123
            }
124
        }
125
126 View Code Duplication
        if ($this->hasNode('withoutParameters')) {
127
            $compiler->raw('$withoutParameters = ')->subcompile($this->getNode('withoutParameters'))->raw(";\n");
128
        } else {
129
            $compiler->raw("\$withoutParameters = [];\n");
130
        }
131
132
        $compiler->write('$swpCollectionMetaLoader'.$i." = \$this->env->getExtension('SWP\Component\TemplatesSystem\Twig\Extension\GimmeExtension')->getLoader();\n");
133 View Code Duplication
        if ($this->hasNode('ignoreContext')) {
134
            $compiler->write('$swpContext'.$i."GimmeList = \$this->env->getExtension('SWP\Component\TemplatesSystem\Twig\Extension\GimmeExtension')->getContext();\n");
135
            $compiler->write('$swpIgnoreContext'.$i.'GimmeList = $swpContext'.$i.'GimmeList->temporaryUnset(')->subcompile($this->getNode('ignoreContext'))->raw(");\n");
136
        }
137
        $compiler->write('')->subcompile($this->getNode('collectionType'))->raw(' = twig_ensure_traversable($swpCollectionMetaLoader'.$i.'->load("')->raw($collectionTypeName)->raw('", ');
138
        $compiler->raw('$withParameters, $withoutParameters, \SWP\Component\TemplatesSystem\Gimme\Loader\LoaderInterface::COLLECTION));')->raw("\n");
139
140
        // the (array) cast bypasses a PHP 5.2.6 bug
141
        $compiler->write("\$context['_parent'] = (array) \$context;\n");
142
143
        if ($this->hasNode('else')) {
144
            $compiler->write("\$context['_iterated'] = false;\n");
145
        }
146
147
        if ($this->getAttribute('with_loop')) {
148
            $compiler
149
                ->write("\$context['loop'] = array(\n")
150
                ->write("  'parent' => \$context['_parent'],\n")
151
                ->write("  'index0' => 0,\n")
152
                ->write("  'index'  => 1,\n")
153
                ->write("  'first'  => true,\n")
154
                ->write(");\n");
155
156
            if (!$this->getAttribute('ifexpr')) {
157
                $compiler
158
                    ->write('if (is_array(')->subcompile($this->getNode('collectionType'))->raw(') || (is_object(')->subcompile($this->getNode('collectionType'))->raw(') && ')->subcompile($this->getNode('collectionType'))->raw(" instanceof Countable)) {\n")
159
                    ->indent()
160
                    ->write('$length = count(')->subcompile($this->getNode('collectionType'))->raw(");\n")
161
                    ->write("\$context['loop']['revindex0'] = \$length - 1;\n")
162
                    ->write("\$context['loop']['revindex'] = \$length;\n")
163
                    ->write("\$context['loop']['length'] = \$length;\n")
164
                    ->write("\$context['loop']['totalLength'] = \$length;\n")
165
                    ->write("\$context['loop']['last'] = 1 === \$length;\n")
166
                    ->outdent()
167
                    ->write("}\n");
168
169
                $compiler
170
                    ->write('if(is_object(')->subcompile($this->getNode('collectionType'))->raw(') && ')->subcompile($this->getNode('collectionType'))->raw(" instanceof \SWP\Component\TemplatesSystem\Gimme\Meta\MetaCollection) {\n")
171
                    ->indent()
172
                    ->write('$context[\'loop\'][\'totalLength\'] = ')->subcompile($this->getNode('collectionType'))->raw("->getTotalItemsCount();\n")
173
                    ->outdent()
174
                    ->write("}\n");
175
            }
176
        }
177
178
        $this->loop->setAttribute('else', $this->hasNode('else'));
179
        $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop'));
180
        $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr'));
181
182
        if (null !== $this->getNode('collectionType')) {
183
            $compiler
184
                ->write('foreach (')
185
                ->subcompile($this->getNode('collectionType'))
186
                ->raw(' as $_key')
187
                ->raw(' => ')
188
                ->subcompile($this->getNode('variable'))
189
                ->raw(") {\n")
190
                ->indent()
191
                ->subcompile($this->getNode('body'))
192
                ->outdent()
193
                ->write("}\n");
194
        }
195
196
        if ($this->hasNode('else')) {
197
            $compiler
198
                ->write("if (!\$context['_iterated']) {\n")
199
                ->indent()
200
                ->subcompile($this->getNode('else'))
201
                ->outdent()
202
                ->write("}\n");
203
        }
204
205
        if ($this->hasNode('ignoreContext')) {
206
            $compiler->write('$swpContext'.$i.'GimmeList->restoreTemporaryUnset($swpIgnoreContext'.$i."GimmeList);\n");
207
        }
208
        $compiler->write("\$_parent = \$context['_parent'];\n");
209
210
        // remove some "private" loop variables (needed for nested loops)
211
        $compiler->write('unset($context[\''.$this->getNode('variable')->getNode(0)->getAttribute('name').'\'], $context[\'_iterated\'], $context[\''.$collectionTypeName.'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
212
213
        if ($this->hasNode('collectionFilters')) {
214
            $compiler->write("unset(\$context['_collection_type_filters']);\n");
215
        }
216
217
        // keep the values set in the inner context for variables defined in the outer context
218
        $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
219
    }
220
}
221