Completed
Push — master ( 052b0b...20e2c2 )
by Pol
02:33
created

Directory::rmdir()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 11
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 23
ccs 10
cts 12
cp 0.8333
crap 6.1666
rs 9.2222
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
        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 1
            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 15
    public function containsAttributeId(string $id): ?FilesystemNodeInterface
38
    {
39
        /** @var \drupol\phpvfs\Node\FilesystemNodeInterface $child */
40 15
        foreach ($this->children() as $child) {
41 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

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