Passed
Push — master ( de351b...604f37 )
by Pol
02:29
created

Directory::mkdir()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 2
rs 10
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 4
    public function cd(string $id): DirectoryInterface
22
    {
23 4
        if (!$this->exist($id)) {
24 1
            throw new \Exception(\sprintf('Cannot change directory to %s: No such file or directory.', $id));
25
        }
26
27 3
        $cwd = $this->get($id);
28
29 3
        if ($cwd instanceof DirectoryInterface) {
0 ignored issues
show
introduced by
$cwd is always a sub-type of drupol\phpvfs\Node\DirectoryInterface.
Loading history...
30 3
            return $cwd;
31
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 29 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...
32
    }
33
34
    /**
35
     * @param string $id
36
     *
37
     * @return null|\drupol\phpvfs\Node\DirectoryInterface|\drupol\phpvfs\Node\FileInterface|\drupol\phpvfs\Node\FilesystemNodeInterface
38
     */
39 15
    public function containsAttributeId(string $id): ?FilesystemNodeInterface
40
    {
41
        /** @var \drupol\phpvfs\Node\FilesystemNodeInterface $child */
42 15
        foreach ($this->children() as $child) {
43 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

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