Passed
Push — master ( 16d5db...b99896 )
by Pol
02:19
created

Directory::cd()   B

Complexity

Conditions 7
Paths 10

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7.0222

Importance

Changes 0
Metric Value
cc 7
eloc 12
nc 10
nop 1
dl 0
loc 23
ccs 12
cts 13
cp 0.9231
crap 7.0222
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpvfs\Node;
6
7
use drupol\phpvfs\Utils\Path;
8
9
/**
10
 * Class Directory.
11
 */
12
class Directory extends FilesystemNode implements DirectoryInterface
13
{
14
    /**
15
     * @param string $id
16
     *
17
     * @throws \Exception
18
     *
19
     * @return \drupol\phpvfs\Node\DirectoryInterface
20
     */
21 1
    public function cd(string $id): DirectoryInterface
22
    {
23 1
        if (!$this->exist($id)) {
24
            throw new \Exception(\sprintf('Cannot change directory to %s: No such file or directory.', $id));
25
        }
26
27 1
        $path = Path::fromString($id);
28
29 1
        if ($path->isRoot() && ($root = $this->root()) instanceof DirectoryInterface) {
30 1
            return $root;
31
        }
32
33
        /** @var \drupol\phpvfs\Node\DirectoryInterface $cwd */
34 1
        $cwd = $path->isAbsolute() ?
35 1
            $this->root() :
36 1
            $this;
37
38 1
        foreach ($path->getIterator() as $pathPart) {
39 1
            $cwd = $cwd->containsAttributeId($pathPart);
40
        }
41
42 1
        if ($cwd instanceof DirectoryInterface) {
43 1
            return $cwd;
44
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 42 is false. This is incompatible with the type-hinted return drupol\phpvfs\Node\DirectoryInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
45
    }
46
47
    /**
48
     * @param string $id
49
     *
50
     * @return null|\drupol\phpvfs\Node\DirectoryInterface|\drupol\phpvfs\Node\FileInterface|\drupol\phpvfs\Node\FilesystemNodeInterface
51
     */
52 15
    public function containsAttributeId(string $id): ?FilesystemNodeInterface
53
    {
54
        /** @var \drupol\phpvfs\Node\FilesystemNodeInterface $child */
55 15
        foreach ($this->children() as $child) {
56 15
            if ($child->getAttribute('id') === $id) {
0 ignored issues
show
Bug introduced by
The method getAttribute() does not exist on ArrayObject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

56
            if ($child->/** @scrutinizer ignore-call */ getAttribute('id') === $id) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
57 15
                return $child;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $child returns the type ArrayObject which is incompatible with the type-hinted return drupol\phpvfs\Node\FilesystemNodeInterface|null.
Loading history...
58
            }
59
        }
60
61 8
        return null;
62
    }
63
64
    /**
65
     * @param string $id
66
     * @param array $attributes
67
     *
68
     * @throws \Exception
69
     *
70
     * @return \drupol\phpvfs\Node\Directory
71
     */
72 24
    public static function create(string $id, array $attributes = [])
73
    {
74 24
        $path = Path::fromString($id);
75
76 24
        if (\DIRECTORY_SEPARATOR !== $id && false !== \strpos($id, \DIRECTORY_SEPARATOR)) {
77 22
            if ($path->isAbsolute()) {
78 20
                $firstPart = \DIRECTORY_SEPARATOR;
79
            } else {
80 4
                $firstPart = $path->shift();
81
            }
82
83 22
            $root = self::create($firstPart, $attributes);
84
85 22
            foreach ($path->getIterator() as $pathPart) {
86 22
                $child = new self(['id' => $pathPart]);
87 22
                $root->add($child);
88 22
                $root = $child;
89
            }
90
91 22
            return $root;
92
        }
93
94 24
        $attributes = ['id' => $id] + $attributes;
95
96 24
        return new self($attributes);
97
    }
98
99
    /**
100
     * @param string ...$ids
101
     *
102
     * @return bool
103
     */
104 4
    public function exist(string ...$ids): bool
105
    {
106 4
        $exist = true;
107 4
        $existId = true;
108
109 4
        foreach ($ids as $id) {
110 4
            $path = Path::fromString($id);
111
112
            /** @var \drupol\phpvfs\Node\DirectoryInterface $cwd */
113 4
            $cwd = $path->isAbsolute() ?
114 2
                $this->root() :
115 4
                $this;
116
117 4
            foreach ($path->getIterator() as $pathPart) {
118 4
                $pathPartExist = false;
119
120 4
                if (\DIRECTORY_SEPARATOR === $pathPart) {
121 1
                    $pathPartExist = true;
122 4
                } elseif (null !== $child = $cwd->containsAttributeId($pathPart)) {
123 4
                    $pathPartExist = true;
124 4
                    $cwd = $child;
125
                }
126
127 4
                $existId = $existId && $pathPartExist;
0 ignored issues
show
introduced by
The condition $pathPartExist is always true.
Loading history...
128
            }
129
130 4
            $exist = $exist && $existId;
131
        }
132
133 4
        return $exist;
134
    }
135
136
    /**
137
     * @param string $id
138
     *
139
     * @throws \Exception
140
     *
141
     * @return \drupol\phpvfs\Node\FilesystemNodeInterface
142
     */
143 1
    public function get(string $id)
144
    {
145 1
        $path = Path::fromString($id);
146
147 1
        if ($path->isRoot()) {
148 1
            return $this->root();
149
        }
150
151
        /** @var \drupol\phpvfs\Node\DirectoryInterface $cwd */
152 1
        $child = $path->isAbsolute() ?
153 1
            $this->root() :
154 1
            $this;
155
156 1
        foreach ($path->getIterator() as $pathPart) {
157 1
            $child = $child->containsAttributeId($pathPart);
158
        }
159
160 1
        return $child;
161
    }
162
163
    /**
164
     * @param string $id
165
     *
166
     * @throws \Exception
167
     *
168
     * @return \drupol\phptree\Node\NodeInterface|\drupol\phpvfs\Node\DirectoryInterface
169
     */
170 2
    public function mkdir(string $id)
171
    {
172 2
        $dir = self::create($id);
173
174 2
        return $this->add($dir->root());
175
    }
176
177
    /**
178
     * @param string $id
179
     *
180
     * @throws \Exception
181
     *
182
     * @return \drupol\phpvfs\Node\DirectoryInterface
183
     */
184 3
    public function rmdir(string $id): DirectoryInterface
185
    {
186 3
        if (!$this->exist($id)) {
187
            throw new \Exception(\sprintf('Cannot remove %s: No such file or directory.', $id));
188
        }
189
190 3
        $path = Path::fromString($id);
191
192 3
        if ($path->isRoot()) {
193
            throw new \Exception(\sprintf('Cannot remove root directory.'));
194
        }
195
196
        /** @var \drupol\phpvfs\Node\DirectoryInterface $cwd */
197 3
        $cwd = $path->isAbsolute() ?
198 1
            $this->root() :
199 3
            $this;
200
201 3
        $last = $path->getLastPart();
202
203 3
        foreach ($cwd->all() as $child) {
204 3
            if (!($child instanceof DirectoryInterface)) {
205 1
                continue;
206
            }
207
208 3
            if ($child->getAttribute('id') !== $last) {
209 3
                continue;
210
            }
211
212 2
            if (null !== $cwd = $child->getParent()) {
213 2
                $cwd->remove($child);
214
            }
215
        }
216
217 3
        if ($cwd instanceof DirectoryInterface) {
218 3
            return $cwd;
219
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 217 is false. This is incompatible with the type-hinted return drupol\phpvfs\Node\DirectoryInterface. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
220
    }
221
}
222