ContainerBuilder   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 279
Duplicated Lines 3.23 %

Coupling/Cohesion

Components 4
Dependencies 13

Test Coverage

Coverage 96.19%

Importance

Changes 0
Metric Value
wmc 32
c 0
b 0
f 0
lcom 4
cbo 13
dl 9
loc 279
ccs 101
cts 105
cp 0.9619
rs 9.6

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A addExpressionPath() 0 4 1
A isExpressionPath() 0 11 4
A build() 0 13 1
A createNodeGathererTraverser() 0 14 1
A createTaskNode() 0 4 1
A createDefinitionNode() 0 4 1
A createDeclarationNode() 0 4 1
A createArgNode() 0 11 2
A createOptNode() 0 4 1
A createSetNode() 0 4 1
A createExpressionNode() 0 4 1
A createScriptNode() 0 4 1
C createNodeCreatorTraverser() 9 70 15

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
 * For licensing information, please see the LICENSE file accompanied with this file.
4
 *
5
 * @author Gerard van Helden <[email protected]>
6
 * @copyright 2012 Gerard van Helden <http://melp.nl>
7
 */
8
namespace Zicht\Tool\Container;
9
10
use Zicht\Tool\Script\Node\Node;
11
use Zicht\Tool\Script\Node\Task\OptNode;
12
use Zicht\Tool\Script\Compiler;
13
use Zicht\Tool\Script\Node\Task\SetNode;
14
use Zicht\Tool\Debug;
15
use Zicht\Tool\Script\Parser\Expression as ExpressionParser;
16
use Zicht\Tool\Script\Tokenizer\Expression as ExpressionTokenizer;
17
use Zicht\Tool\Script\Node\Task\ArgNode;
18
19
/**
20
 * The container builder converts a config tree into a compilable ContainerNode
21
 */
22
class ContainerBuilder
23
{
24
    protected $expressionPaths = array();
25
    /**
26
     * Constructor.
27
     *
28
     * @param array $config
29
     */
30 11
    public function __construct($config)
31
    {
32 11
        $this->config = $config;
0 ignored issues
show
Bug introduced by
The property config does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
33
34 11
        $this->exprcompiler = new Compiler(new ExpressionParser(), new ExpressionTokenizer());
0 ignored issues
show
Bug introduced by
The property exprcompiler does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
35 11
        $this->scriptcompiler = new Compiler();
0 ignored issues
show
Bug introduced by
The property scriptcompiler does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
36 11
    }
37
38
39
    /**
40
     * Adds a callable to decide if a path should be an expression node.
41
     *
42
     * @param callable $callable
43
     * @return void
44
     */
45 1
    public function addExpressionPath($callable)
46
    {
47 1
        $this->expressionPaths[] = $callable;
48 1
    }
49
50
51
    /**
52
     * Decides if a node should be an expression.
53
     *
54
     * @param array $path
55
     * @param mixed $node
56
     * @return bool
57
     */
58 10
    public function isExpressionPath($path, $node)
59
    {
60 10
        if (is_scalar($node)) {
61 2
            foreach ($this->expressionPaths as $callable) {
62 1
                if (call_user_func($callable, $path)) {
63 1
                    return true;
64
                }
65 1
            }
66 1
        }
67 10
        return false;
68
    }
69
70
71
    /**
72
     * Build the container node
73
     *
74
     * @return ContainerNode
75
     */
76 11
    public function build()
77
    {
78 11
        Debug::enterScope('build');
79 11
        $traverser = $this->createNodeCreatorTraverser($this->config);
80 11
        $result = $traverser->traverse();
81
82 11
        $node = new ContainerNode();
83 11
        $gatherer = $this->createNodeGathererTraverser($result, $node);
84 11
        $gatherer->traverse();
85 11
        Debug::exitScope('build');
86
87 11
        return $node;
88
    }
89
90
91
    /**
92
     * Creates the traverser that gathers all nodes (i.e. Node instances) that are specified in the tree.
93
     *
94
     * @param array $result
95
     * @param \Zicht\Tool\Script\Node\Branch $containerNode
96
     * @return Traverser
97
     */
98 11
    public function createNodeGathererTraverser($result, $containerNode)
99
    {
100 11
        $gatherer = new Traverser($result);
101 11
        $gatherer->addVisitor(
102
            function ($path, $node) use($containerNode) {
103 9
                $containerNode->append($node);
104 11
            },
105
            function ($path, $node) {
106 10
                return $node instanceof Node;
107 11
            },
108
            Traverser::AFTER
109 11
        );
110 11
        return $gatherer;
111
    }
112
113
114
    /**
115
     * Creates a task node for the specified path
116
     *
117
     * @param array $path
118
     * @param array $node
119
     * @return Task
120
     */
121 7
    public function createTaskNode($path, $node)
122
    {
123 7
        return new Task($path, $node);
124
    }
125
126
127
    /**
128
     * Creates a definition node for the specified path
129
     *
130
     * @param array $path
131
     * @param array $node
132
     * @return Definition
133
     */
134 1
    public function createDefinitionNode($path, $node)
135
    {
136 1
        return new Definition($path, $node);
137
    }
138
139
140
    /**
141
     * Creates a declaration node for the specified path
142
     *
143
     * @param array $path
144
     * @param array $node
145
     * @return Declaration
146
     */
147 1
    public function createDeclarationNode($path, $node)
148
    {
149 1
        return new Declaration($path, $this->exprcompiler->parse($node));
150
    }
151
152
153
    /**
154
     * Creates a node for the 'args' definition of the task.
155
     *
156
     * @param array $path
157
     * @param string $node
158
     * @return \Zicht\Tool\Script\Node\Task\ArgNode
159
     */
160 2
    public function createArgNode($path, $node)
161
    {
162 2
        $v = trim($node);
163 2
        if (substr($v, 0, 1) == '?') {
164 1
            $conditional = true;
165 1
            $v = ltrim(substr($v, 1));
166 1
        } else {
167 1
            $conditional = false;
168
        }
169 2
        return new ArgNode(end($path), $this->exprcompiler->parse($v), $conditional);
170
    }
171
172
    /**
173
     * Creates a node for the 'opts' definition of the task.
174
     *
175
     * @param array $path
176
     * @param string $node
177
     * @return \Zicht\Tool\Script\Node\Task\OptNode
178
     */
179
    public function createOptNode($path, $node)
180
    {
181
        return new OptNode(end($path), $this->exprcompiler->parse($node));
182
    }
183
184
185
    /**
186
     * Creates a node for the 'set' definition of the task.
187
     *
188
     * @param array $path
189
     * @param string $node
190
     * @return SetNode
191
     */
192
    public function createSetNode($path, $node)
193
    {
194
        return new SetNode(end($path), $this->exprcompiler->parse($node));
195
    }
196
197
198
    /**
199
     * Creates an expression node at the specified path.
200
     *
201
     * @param array $path
202
     * @param array $node
203
     * @return \Zicht\Tool\Script\Node\Node
204
     */
205 3
    public function createExpressionNode($path, $node)
206
    {
207 3
        return $this->exprcompiler->parse($node);
208
    }
209
210
211
    /**
212
     * Creates a script node at the specified path.
213
     *
214
     * @param array $path
215
     * @param array $node
216
     * @return \Zicht\Tool\Script\Node\Node
217
     */
218 1
    public function createScriptNode($path, $node)
219
    {
220 1
        return $this->scriptcompiler->parse(trim($node));
221
    }
222
223
224
    /**
225
     * Creates the traverser that creates relevant nodes at all known paths.
226
     *
227
     * @param array $config
228
     * @return Traverser
229
     */
230 11
    public function createNodeCreatorTraverser($config)
231
    {
232 11
        $traverser = new Traverser($config);
233
234 11
        $traverser->addVisitor(
235 11
            array($this, 'createArgNode'),
236 View Code Duplication
            function ($path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237 10
                return (count($path) == 4 && $path[0] == 'tasks' && $path[2] == 'args');
238 11
            },
239
            Traverser::BEFORE
240 11
        );
241 11
        $traverser->addVisitor(
242 11
            array($this, 'createOptNode'),
243 View Code Duplication
            function ($path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
244 10
                return (count($path) == 4 && $path[0] == 'tasks' && $path[2] == 'opts');
245 11
            },
246
            Traverser::BEFORE
247 11
        );
248 11
        $traverser->addVisitor(
249 11
            array($this, 'createSetNode'),
250 View Code Duplication
            function ($path) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251 10
                return (count($path) == 4 && $path[0] == 'tasks' && $path[2] == 'set');
252 11
            },
253
            Traverser::BEFORE
254 11
        );
255
256 11
        $traverser->addVisitor(
257 11
            array($this, 'createExpressionNode'),
258
            function ($path) {
259
                return
260 10
                    count($path) === 3
261 10
                    && $path[0] == 'tasks'
262 10
                    && in_array($path[2], array('unless', 'assert', 'yield', 'if'))
263 10
                    ;
264 11
            },
265
            Traverser::BEFORE
266 11
        );
267 11
        $traverser->addVisitor(
268 11
            array($this, 'createScriptNode'),
269
            function ($path) {
270
                return
271 10
                    count($path) == 4
272 10
                    && $path[0] == 'tasks'
273 10
                    && in_array($path[2], array('do', 'pre', 'post'))
274 10
                ;
275 11
            },
276
            Traverser::BEFORE
277 11
        );
278 11
        $traverser->addVisitor(
279 11
            array($this, 'createTaskNode'),
280
            function ($path) {
281 10
                return count($path) == 2 && $path[0] == 'tasks';
282 11
            },
283
            Traverser::AFTER
284 11
        );
285 11
        $traverser->addVisitor(
286 11
            array($this, 'createDeclarationNode'),
287 11
            array($this, 'isExpressionPath'),
288
            Traverser::AFTER
289 11
        );
290 11
        $traverser->addVisitor(
291 11
            array($this, 'createDefinitionNode'),
292 11
            function ($path, $node) {
293 10
                return $path[0] !== 'tasks' && (is_scalar($node) || (is_array($node) && count($node) === 0));
294 11
            },
295
            Traverser::AFTER
296 11
        );
297
298 11
        return $traverser;
299
    }
300
}
301