ArrayFs::getEntry()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 6
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 8
rs 10
1
<?php
2
3
/**
4
 * This file is part of the sj-i/php-fuse package.
5
 *
6
 * (c) sji <[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
declare(strict_types=1);
13
14
use Fuse\FilesystemDefaultImplementationTrait;
15
use Fuse\FilesystemInterface;
16
use Fuse\Libc\Errno\Errno;
17
use Fuse\Libc\Fuse\FuseFileInfo;
18
use Fuse\Libc\Fuse\FuseFillDir;
19
use Fuse\Libc\Fuse\FuseReadDirBuffer;
20
use Fuse\Libc\String\CBytesBuffer;
21
use Fuse\Libc\Sys\Stat\Stat;
22
use Fuse\Mounter;
23
24
require 'vendor/autoload.php';
25
26
/**
27
 * @psalm-type NodeType=scalar|array|null
28
 */
29
class ArrayFs implements FilesystemInterface
30
{
31
    use FilesystemDefaultImplementationTrait;
32
33
    /** @var NodeType[] */
34
    private array $array;
35
36
    /**
37
     * ArrayFs constructor.
38
     * @param NodeType[] $array
39
     */
40
    public function __construct(array $array)
41
    {
42
        $this->array = $array;
43
    }
44
45
    public function getArray(): array
46
    {
47
        return $this->array;
48
    }
49
50
    public function getattr(string $path, Stat $stat): int
51
    {
52
        echo "attr read {$path}" . PHP_EOL;
53
54
        if ($path === '/') {
55
            $stat->st_mode = Stat::S_IFDIR | 0777;
56
            $stat->st_nlink = 2;
57
            $stat->st_uid = getmyuid();
58
            $stat->st_gid = getmygid();
59
            return 0;
60
        }
61
62
        $element = $this->getEntry($path);
63
        if (is_null($element)) {
0 ignored issues
show
introduced by
The condition is_null($element) is always false.
Loading history...
64
            return -Errno::ENOENT;
65
        }
66
        if (is_array($element)) {
0 ignored issues
show
introduced by
The condition is_array($element) is always true.
Loading history...
67
            $stat->st_mode = Stat::S_IFDIR | 0777;
68
            $stat->st_nlink = 2;
69
            $stat->st_uid = getmyuid();
70
            $stat->st_gid = getmygid();
71
            return 0;
72
        }
73
        $stat->st_mode = Stat::S_IFREG | 0777;
74
        $stat->st_nlink = 1;
75
        $stat->st_size = strlen((string)$element);
76
        $stat->st_uid = getmyuid();
77
        $stat->st_gid = getmygid();
78
        return 0;
79
    }
80
81
    /**
82
     * @param NodeType[] $array
83
     * @param list<string> $offsets
0 ignored issues
show
Bug introduced by
The type list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
84
     * @param callable(array, string):NodeType $operation
85
     * @return NodeType
0 ignored issues
show
Bug introduced by
The type NodeType was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
86
     */
87
    private function &getRecursive(&$array, array $offsets, ?callable $operation = null)
88
    {
89
        $null = null;
90
91
        $count = count($offsets);
92
93
        if ($count === 0) {
94
            return $null;
95
        }
96
        if ($count === 1) {
97
            if (isset($array[$offsets[0]])) {
98
                if (!is_null($operation)) {
99
                    return $operation($array, $offsets[0]);
100
                } else {
101
                    /** @var NodeType */
102
                    return $array[$offsets[0]];
103
                }
104
            } else {
105
                return $null;
106
            }
107
        }
108
109
        $offset = array_shift($offsets);
110
        if (is_array($array[$offset])) {
0 ignored issues
show
introduced by
The condition is_array($array[$offset]) is always false.
Loading history...
111
            /** @var NodeType[] $next_array */
112
            $next_array =& $array[$offset];
113
            return $this->getRecursive($next_array, $offsets);
114
        } else {
115
            return $null;
116
        }
117
    }
118
119
    /**
120
     * @param string $path
121
     * @return scalar|array|null
122
     */
123
    private function &getEntry(string $path)
124
    {
125
        if ($path === '/') {
126
            return $this->array;
127
        }
128
        $splitted = explode('/', $path);
129
        array_shift($splitted);
130
        return $this->getRecursive($this->array, $splitted);
131
    }
132
133
    /**
134
     * @param string $path
135
     * @return NodeType
136
     */
137
    private function &getParentEntry(string $path)
138
    {
139
        $splitted = explode('/', $path);
140
        array_shift($splitted);
141
        array_pop($splitted);
142
        if (count($splitted) === 0) {
143
            return $this->array;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->array returns the type array which is incompatible with the documented return type NodeType.
Loading history...
144
        }
145
        return $this->getRecursive($this->array, $splitted);
146
    }
147
148
    /**
149
     * @param string $path
150
     */
151
    private function unsetEntry(string $path): void
152
    {
153
        $splitted = explode('/', $path);
154
        array_shift($splitted);
155
        $this->getRecursive(
156
            $this->array,
157
            $splitted,
158
            function &(array &$array, string $index) {
159
                $null = null;
160
                unset($array[$index]);
161
                return $null;
162
            }
163
        );
164
    }
165
166
    public function readdir(
167
        string $path,
168
        FuseReadDirBuffer $buf,
169
        FuseFillDir $filler,
170
        int $offset,
171
        FuseFileInfo $fuse_file_info
172
    ): int {
173
        $filler($buf, '.', null, 0);
174
        $filler($buf, '..', null, 0);
175
        $entry = $this->getEntry($path);
176
        if (!is_array($entry)) {
0 ignored issues
show
introduced by
The condition is_array($entry) is always true.
Loading history...
177
            return -Errno::ENOTDIR;
178
        }
179
        foreach ($entry as $key => $_) {
180
            $filler($buf, (string)$key, null, 0);
181
        }
182
183
        return 0;
184
    }
185
186
    public function open(string $path, FuseFileInfo $fuse_file_info): int
187
    {
188
        $entry = $this->getEntry($path);
189
        if (!is_scalar($entry)) {
0 ignored issues
show
introduced by
The condition is_scalar($entry) is always false.
Loading history...
190
            return Errno::ENOENT;
191
        }
192
193
        echo "open {$path}" . PHP_EOL;
194
        return 0;
195
    }
196
197
    public function read(string $path, CBytesBuffer $buffer, int $size, int $offset, FuseFileInfo $fuse_file_info): int
198
    {
199
        $entry = $this->getEntry($path);
200
201
        echo "read {$path}" . PHP_EOL;
202
203
        assert(!is_array($entry));
204
        $len = strlen((string)$entry);
205
206
        if ($offset + $size > $len) {
207
            $size = ($len - $offset);
208
        }
209
210
        $content = substr((string)$entry, $offset, $size);
211
        $buffer->write($content, $size);
212
213
        return $size;
214
    }
215
216
    public function write(string $path, string $buffer, int $size, int $offset, FuseFileInfo $fuse_file_info): int
217
    {
218
        $entry = &$this->getEntry($path);
219
        assert(!is_array($entry));
220
        $entry = substr_replace((string)$entry, $buffer, $offset, $size);
221
222
        return $size;
223
    }
224
225
    public function create(string $path, int $mode, FuseFileInfo $fuse_file_info): int
226
    {
227
        $entry = &$this->getParentEntry($path);
228
        if (is_array($entry)) {
0 ignored issues
show
introduced by
The condition is_array($entry) is always false.
Loading history...
229
            $segments = explode('/', $path);
230
            $filename = array_pop($segments);
231
            $entry[$filename] = '';
232
            return 0;
233
        } else {
234
            return Errno::ENOENT;
235
        }
236
    }
237
238
    public function unlink(string $path): int
239
    {
240
        $this->unsetEntry($path);
241
        return 0;
242
    }
243
244
    public function rename(string $from, string $to): int
245
    {
246
        $fromValue = $this->getEntry($from);
247
        $parent_entry = &$this->getParentEntry($to);
248
        if (is_array($parent_entry)) {
0 ignored issues
show
introduced by
The condition is_array($parent_entry) is always false.
Loading history...
249
            $segments = explode('/', $to);
250
            $filename = array_pop($segments);
251
            $parent_entry[$filename] = $fromValue;
252
            $this->unsetEntry($from);
253
            return 0;
254
        } else {
255
            return -Errno::ENOENT;
256
        }
257
    }
258
}
259
260
$e = new \DateTimeImmutable();
261
262
$mounter = new Mounter();
263
/** @psalm-suppress MixedArgumentTypeCoercion */
264
$array_fs = new ArrayFs([
265
    1,
266
    2,
267
    'foo' => 'bar',
268
    'e' => json_decode(json_encode($e), true)
269
]);
270
$result = $mounter->mount('/tmp/example/', $array_fs);
271
/** @psalm-suppress ForbiddenCode */
272
var_dump($array_fs->getArray());
273
return $result;