tree   A
last analyzed

Complexity

Total Complexity 33

Size/Duplication

Total Lines 242
Duplicated Lines 7.85 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 54.64%

Importance

Changes 0
Metric Value
dl 19
loc 242
ccs 53
cts 97
cp 0.5464
rs 9.76
c 0
b 0
f 0
wmc 33
lcom 1
cbo 3

11 Methods

Rating   Name   Duplication   Size   Complexity  
A parents() 0 10 2
A sort() 0 16 3
A collapse() 0 11 1
B expand() 0 36 7
A dive() 0 15 5
A ls() 10 10 2
A search() 0 15 4
A map() 0 15 3
A reduce() 9 9 2
A filter() 0 8 2
A getNodeName() 0 10 2

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 Ariadne Component Library.
5
 *
6
 * (c) Muze <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace arc;
13
14
/**
15
 *	Utility methods to handle common path related tasks, cleaning, changing relative to absolute, etc.
16
 */
17
class tree
18
{
19
    /**
20
     * Create a simple array from a tree. Each non-null nodeValue will be added with the path to its node as the key
21
     * @param  \arc\tree\Node $node
22
     * @param  string         $root
23
     * @param  string         $nodeName
24
     * @return array          [ $path => $data, ... ]
25
     */
26 10
    public static function collapse($node, $root = '', $nodeName = 'nodeName')
27
    {
28 10
        return \arc\tree::map(
29 10
            $node,
30 10
            function ($child) {
31 10
                return $child->nodeValue;
32 10
            },
33
            $root,
34
            $nodeName
35
        );
36
    }
37
38
    /**
39
     * Creates a NamedNode tree from an array with path => nodeValue entries.
40
     * @param  array               $tree The collapsed tree: [ $path => $data, ... ]
41
     * @return \arc\tree\NamedNode an object tree with parent/children relations
42
     */
43 16
    public static function expand($tree = null)
44
    {
45 16
        if (is_object( $tree ) && isset( $tree->childNodes )) {
46
            return $tree; //FIXME: should we clone the tree to avoid shared state?
47
        }
48 16
        $root = new \arc\tree\NamedNode();
49 16
        if (!is_array($tree)) {
50 12
            return $root; // empty tree
51
        }
52 4
        $previousParent = $root;
53 4
        foreach ($tree as $path => $data) {
54 4
            $previousPath = $previousParent->getPath();
55 4
            $subPath = \arc\path::diff( $previousPath, $path );
56 4
            if ($subPath) {
57
                // create missing parent nodes, input tree may be sparsely filled
58 4
                $node = \arc\path::reduce(
59 4
                    $subPath,
60 4
                    function ($previous, $name) {
61 4
                        if ($name == '..') {
62 4
                            return $previous->parentNode;
63
                        }
64
65 4
                        return $previous->appendChild( $name );
66 4
                    },
67 4
                    $previousParent
68
                );
69
            } else {
70
                // means the previousParent is equal to the current path, e.g. the root
71
                $node = $previousParent;
72
            }
73 4
            $node->nodeValue = $data;
74 4
            $previousParent = $node;
75
        }
76
77 4
        return $root;
78
    }
79
80
    /**
81
     * Calls the first callback method on each successive parent until a non-null value is returned. Then
82
     * calls all the parents from that point back to this node with the second callback in reverse order.
83
     * The first callback (dive) must accept one parameter, the node.
84
     * The second callback (rise) must accept two parameters, the nde and the result up to that point.
85
     * @param  \arc\tree\Node $node         A tree node, must have traversable childNodes property and a parentNode property
86
     * @param  callable       $diveCallback The callback for the dive phase.
87
     * @param  callable       $riseCallback The callback for the rise phase.
88
     * @return mixed
89
     */
90 8
    public static function dive($node, $diveCallback = null, $riseCallback = null)
91
    {
92 8
        $result = null;
93 8
        if (is_callable( $diveCallback )) {
94 6
            $result = call_user_func( $diveCallback, $node );
95
        }
96 8
        if (!isset( $result ) && $node->parentNode) {
97 4
            $result = \arc\tree::dive( $node->parentNode, $diveCallback, $riseCallback );
0 ignored issues
show
Bug introduced by
The property parentNode does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
98
        }
99 8
        if (is_callable( $riseCallback )) {
100 4
            return call_user_func( $riseCallback, $node, $result );
101
        } else {
102 6
            return $result;
103
        }
104
    }
105
106
    /**
107
     * Calls the callback method on each parent of the given node, starting at the root.
108
     * @param  \arc\tree\Node $node     A tree node, must have traversable childNodes property and a parentNode property
109
     * @param  callable       $callback The callback function applied to each parent.
110
     * @return mixed
111
     */
112 4
    public static function parents($node, $callback = null)
113
    {
114 4
        if (!isset( $callback )) {
115
            $callback = function ($node, $result) {
116
                return ( (array) $result ) + array( $node );
117
            };
118
        }
119
120 4
        return self::dive( $node, null, $callback );
121
    }
122
123
    /**
124
     * Calls the callback method on each of the direct child nodes of the given node.
125
     * @param  \arc\tree\Node $node
126
     * @param  callable       $callback The callback function applied to each child node
127
     * @param  mixed          $nodeName The name of the 'name' property or a function that returns the name of a node.
128
     * @return array
129
     */
130 View Code Duplication
    public static function ls($node, $callback, $nodeName = 'nodeName')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
131
    {
132
        $result = [];
133
        foreach ($node->childNodes as $child) {
0 ignored issues
show
Bug introduced by
The property childNodes does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
134
            $name = self::getNodeName( $child, $nodeName );
135
            $result[ $name ] = call_user_func( $callback, $child );
136
        }
137
138
        return $result;
139
    }
140
141
    /**
142
     * Calls the callback method on each child of the current node, including the node itself, until a non-null
143
     * result is returned. Returns that result. The tree is searched depth first.
144
     * @param  \arc\tree\Node $node
145
     * @param  callable       $callback The callback function applied to each child node
146
     * @return mixed
147
     */
148
    public static function search($node, $callback)
149
    {
150
        $result = call_user_func( $callback, $node );
151
        if (isset( $result )) {
152
            return $result;
153
        }
154
        foreach ($node->childNodes as $child) {
0 ignored issues
show
Bug introduced by
The property childNodes does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
155
            $result = self::search( $child, $callback );
156
            if (isset( $result )) {
157
                return $result;
158
            }
159
        }
160
161
        return null;
162
    }
163
164
    /**
165
     * Calls the callback method on each child of the current node, including the node itself. Any non-null result
166
     * is added to the result array, with the path to the node as the key.
167
     * @param  \arc\tree\Node $node
168
     * @param  callable       $callback The callback function applied to each child node
169
     * @param  string         $root
170
     * @param  mixed          $nodeName The name of the 'name' property or a function that returns the name of a node.
171
     * @return array
172
     */
173 10
    public static function map($node, $callback, $root = '', $nodeName = 'nodeName')
174
    {
175 10
        $result = [];
176 10
        $name = self::getNodeName( $node, $nodeName );
177 10
        $path = $root . $name . '/';
178 10
        $callbackResult = call_user_func( $callback, $node );
179 10
        if (isset($callbackResult)) {
180 10
            $result[ $path ] = $callbackResult;
181
        }
182 10
        foreach ($node->childNodes as $child) {
0 ignored issues
show
Bug introduced by
The property childNodes does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
183 10
            $result += self::map( $child, $callback, $path, $nodeName );
184
        }
185
186 10
        return $result;
187
    }
188
189
    /**
190
     * Calls the callback method on all child nodes of the given node, including the node itself. The result of each
191
     * call is passed on as the first argument to each succesive call.
192
     * @param  \arc\tree\Node $node
193
     * @param  callable       $callback
194
     * @param  mixed          $initial  The value to pass to the first callback call.
195
     * @return mixed
196
     */
197 View Code Duplication
    public static function reduce($node, $callback, $initial = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
198
    {
199
        $result = call_user_func( $callback, $initial, $node );
200
        foreach ($node->childNodes as $child) {
0 ignored issues
show
Bug introduced by
The property childNodes does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
201
            $result = self::reduce( $child, $callback, $result );
202
        }
203
204
        return $result;
205
    }
206
207
    /**
208
     * Filters the tree using a callback method. If the callback method returns true, the node's value is included
209
     * in the result, otherwise it is skipped. Filter returns a collapsed tree: [ path => nodeValue ]
210
     * The callback method must take one argument: the current node.
211
     * @param  \arc\tree\Node $node
212
     * @param  callable       $callback
213
     * @return array
214
     */
215
    public static function filter($node, $callback, $root = '', $nodeName = 'nodeName')
216
    {
217
        return self::map( $node, function ($node) use ($callback) {
218
            if (call_user_func( $callback, $node )) {
219
                return $node->nodeValue;
220
            }
221
        }, $root, $nodeName );
222
    }
223
224
    /**
225
     * Sorts the childNodes list of the node, recursively.
226
     * @param  \arc\tree\Node   $node
227
     * @param  callable         $callback
228
     * @param  mixed            $nodeName
229
     * @throws UnknownError
230
     */
231
    public static function sort($node, $callback, $nodeName = 'nodeName')
232
    {
233
        if (is_array($node->childNodes)) {
234
            $sort = function ($node) use ($callback) {
235
                uasort( $node->childNodes, $callback );
236
            };
237
        } elseif ($node->childNodes instanceof \ArrayObject) {
0 ignored issues
show
Bug introduced by
The property childNodes does not seem to exist in arc\tree\Node.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
238
            $sort = function ($node) use ($callback) {
239
                $node->childNodes->uasort( $callback );
240
            };
241
        } else {
242
            throw new \arc\UnknownError( 'Cannot sort this tree - no suitable sort method found',
243
                \arc\exceptions::OBJECT_NOT_FOUND);
244
        }
245
        self::map( $node, $sort, '', $nodeName );
246
    }
247
248 10
    private static function getNodeName($node, $nodeName)
249
    {
250 10
        if (is_callable($nodeName)) {
251
            $name = call_user_func( $nodeName, $node );
252
        } else {
253 10
            $name = $node->{$nodeName};
254
        }
255
256 10
        return $name;
257
    }
258
}
259