Completed
Push — master ( 23d46a...ee003d )
by Pol
03:02
created

Directory   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Test Coverage

Coverage 91.78%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 66
c 2
b 0
f 0
dl 0
loc 188
ccs 67
cts 73
cp 0.9178
rs 9.76
wmc 33

7 Methods

Rating   Name   Duplication   Size   Complexity  
A cd() 0 8 3
A create() 0 25 5
A containsAttributeId() 0 10 3
B exist() 0 30 8
A get() 0 22 6
A mkdir() 0 8 2
A rmdir() 0 23 6
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 3
    public function cd(string $id): DirectoryInterface
22
    {
23 3
        if (!$this->exist($id)) {
24
            throw new \Exception(\sprintf('Cannot change directory to %s: No such file or directory.', $id));
25
        }
26
27 3
        if (($cwd = $this->get($id)) instanceof DirectoryInterface) {
0 ignored issues
show
introduced by
$cwd = $this->get($id) is always a sub-type of drupol\phpvfs\Node\DirectoryInterface.
Loading history...
28 3
            return $cwd;
29
        }
0 ignored issues
show
Bug Best Practice introduced by
The function implicitly returns null when the if condition on line 27 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...
30
    }
31
32
    /**
33
     * @param string $id
34
     *
35
     * @return null|\drupol\phpvfs\Node\DirectoryInterface|\drupol\phpvfs\Node\FileInterface|\drupol\phpvfs\Node\FilesystemNodeInterface
36
     */
37 11
    public function containsAttributeId(string $id): ?FilesystemNodeInterface
38
    {
39
        /** @var \drupol\phpvfs\Node\FilesystemNodeInterface $child */
40 11
        foreach ($this->children() as $child) {
41 11
            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

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