1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types = 1); |
4
|
|
|
|
5
|
|
|
namespace drupol\phpvfs; |
6
|
|
|
|
7
|
|
|
use drupol\phpvfs\Commands\Cd; |
8
|
|
|
use drupol\phpvfs\Commands\Exist; |
9
|
|
|
use drupol\phpvfs\Commands\Get; |
10
|
|
|
use drupol\phpvfs\Filesystem\Filesystem; |
11
|
|
|
use drupol\phpvfs\Filesystem\FilesystemInterface; |
12
|
|
|
use drupol\phpvfs\Node\File; |
13
|
|
|
use drupol\phpvfs\Node\FileInterface; |
14
|
|
|
use drupol\phpvfs\Utils\Path; |
15
|
|
|
|
16
|
|
|
class PhpVfs |
17
|
|
|
{ |
18
|
|
|
public const SCHEME = 'phpvfs'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var array |
22
|
|
|
*/ |
23
|
|
|
public $context; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var null|\drupol\phpvfs\Node\FileInterface |
27
|
|
|
*/ |
28
|
|
|
private $currentFile; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @return \drupol\phpvfs\Filesystem\FilesystemInterface |
32
|
|
|
*/ |
33
|
1 |
|
public static function fs(): FilesystemInterface |
34
|
|
|
{ |
35
|
1 |
|
$options = \stream_context_get_options( |
36
|
1 |
|
\stream_context_get_default() |
37
|
|
|
); |
38
|
|
|
|
39
|
1 |
|
return $options[static::SCHEME]['filesystem']; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @param \drupol\phpvfs\Filesystem\Filesystem $filesystem |
44
|
|
|
* @param array $options |
45
|
|
|
*/ |
46
|
1 |
|
public static function register(Filesystem $filesystem, array $options = []) |
47
|
|
|
{ |
48
|
|
|
$options = [ |
49
|
1 |
|
static::SCHEME => [ |
50
|
1 |
|
'filesystem' => $filesystem, |
51
|
1 |
|
] + $options, |
52
|
|
|
]; |
53
|
|
|
|
54
|
1 |
|
\stream_context_set_default($options); |
55
|
1 |
|
\stream_wrapper_register(self::SCHEME, __CLASS__); |
56
|
1 |
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @param string $from |
60
|
|
|
* @param string $to |
61
|
|
|
* |
62
|
|
|
* @throws \Exception |
63
|
|
|
* |
64
|
|
|
* @return bool |
65
|
|
|
*/ |
66
|
1 |
|
public function rename(string $from, string $to): bool |
67
|
|
|
{ |
68
|
1 |
|
$from = $this->stripScheme($from); |
69
|
1 |
|
$to = $this->stripScheme($to); |
70
|
|
|
|
71
|
1 |
|
if (Exist::exec($this::fs(), $from)) { |
72
|
1 |
|
$from = Get::exec($this::fs(), $from); |
73
|
|
|
} else { |
74
|
|
|
throw new \Exception('Source resource does not exist.'); |
75
|
|
|
|
76
|
|
|
return false; |
|
|
|
|
77
|
|
|
} |
78
|
|
|
|
79
|
1 |
|
if (Exist::exec($this::fs(), $to)) { |
80
|
|
|
throw new \Exception('Destination already exist.'); |
81
|
|
|
|
82
|
|
|
return false; |
83
|
|
|
} |
84
|
|
|
|
85
|
1 |
|
$toPath = Path::fromString($to); |
86
|
|
|
|
87
|
1 |
|
$this::fs() |
88
|
1 |
|
->getCwd() |
89
|
1 |
|
->mkdir($toPath->dirname()); |
90
|
|
|
|
91
|
1 |
|
if (null !== $parent = $from->getParent()) { |
92
|
1 |
|
$parent->delete($from); |
93
|
|
|
} |
94
|
|
|
|
95
|
1 |
|
Cd::exec($this::fs(), $toPath->dirname()); |
96
|
|
|
|
97
|
1 |
|
$from->setAttribute('id', $toPath->basename()); |
98
|
|
|
|
99
|
1 |
|
if ($from instanceof FileInterface) { |
100
|
1 |
|
$from->setPosition(0); |
101
|
|
|
} |
102
|
|
|
|
103
|
1 |
|
$this::fs() |
104
|
1 |
|
->getCwd() |
105
|
1 |
|
->add($from); |
106
|
|
|
|
107
|
1 |
|
return true; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @see http://php.net/streamwrapper.stream-close |
112
|
|
|
*/ |
113
|
1 |
|
public function stream_close() // phpcs:ignore |
114
|
|
|
{ |
115
|
1 |
|
if (null !== $this->currentFile) { |
116
|
1 |
|
$this->currentFile->setPosition(0); |
117
|
|
|
} |
118
|
|
|
|
119
|
1 |
|
$this->currentFile = null; |
120
|
1 |
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* @return bool |
124
|
|
|
* |
125
|
|
|
* @see http://php.net/streamwrapper.stream-eof |
126
|
|
|
*/ |
127
|
|
|
public function stream_eof(): bool // phpcs:ignore |
128
|
|
|
{ |
129
|
|
|
return true; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* @param string $resource |
134
|
|
|
* |
135
|
|
|
* @throws \Exception |
136
|
|
|
* |
137
|
|
|
* @return bool |
138
|
|
|
*/ |
139
|
1 |
|
public function stream_open(string $resource) // phpcs:ignore |
140
|
|
|
{ |
141
|
1 |
|
$resource = $this->stripScheme($resource); |
142
|
|
|
|
143
|
1 |
|
if (true === $this::fs()->exist($resource)) { |
144
|
|
|
$file = $this::fs()->get($resource); |
145
|
|
|
|
146
|
|
|
if ($file instanceof FileInterface) { |
147
|
|
|
$this->currentFile = $file; |
148
|
|
|
} |
149
|
|
|
} else { |
150
|
1 |
|
$this->currentFile = File::create($resource); |
151
|
1 |
|
$this::fs()->getCwd()->add($this->currentFile->root()); |
152
|
|
|
} |
153
|
|
|
|
154
|
1 |
|
if (null === $this->currentFile) { |
155
|
|
|
return false; |
156
|
|
|
} |
157
|
|
|
|
158
|
1 |
|
$this->currentFile->setPosition(0); |
159
|
|
|
|
160
|
1 |
|
return true; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @see http://php.net/streamwrapper.stream-read |
165
|
|
|
* |
166
|
|
|
* @param int $bytes |
167
|
|
|
* |
168
|
|
|
* @return mixed |
169
|
|
|
*/ |
170
|
|
|
public function stream_read(int $bytes) // phpcs:ignore |
171
|
|
|
{ |
172
|
|
|
if (null !== $this->currentFile) { |
173
|
|
|
return $this->currentFile->read($bytes); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return false; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @param string $data |
181
|
|
|
* |
182
|
|
|
* @return int |
183
|
|
|
*/ |
184
|
1 |
|
public function stream_write(string $data) // phpcs:ignore |
185
|
|
|
{ |
186
|
1 |
|
if (null !== $this->currentFile) { |
187
|
1 |
|
return $this->currentFile->write($data); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
return 0; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @param string $path |
195
|
|
|
* |
196
|
|
|
* @throws \Exception |
197
|
|
|
*/ |
198
|
1 |
|
public function unlink(string $path) |
199
|
|
|
{ |
200
|
1 |
|
$path = $this->stripScheme($path); |
201
|
|
|
|
202
|
1 |
|
if (true === Exist::exec($this::fs(), $path)) { |
203
|
1 |
|
$file = Get::exec($this::fs(), $path); |
204
|
|
|
|
205
|
1 |
|
if (null !== $parent = $file->getParent()) { |
206
|
1 |
|
$parent->delete($file); |
207
|
|
|
} |
208
|
|
|
} |
209
|
1 |
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Returns path stripped of url scheme (http://, ftp://, test:// etc.). |
213
|
|
|
* |
214
|
|
|
* @param string $path |
215
|
|
|
* |
216
|
|
|
* @return string |
217
|
|
|
*/ |
218
|
1 |
|
protected function stripScheme(string $path): string |
219
|
|
|
{ |
220
|
1 |
|
return '/' . \ltrim(\substr($path, 9), '/'); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.