Issues (158)

src/Collector/Stream/FilesystemStreamProxy.php (20 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Collector\Stream;
6
7
use Yiisoft\Strings\CombinedRegexp;
8
use Yiisoft\Yii\Debug\Helper\BacktraceIgnoreMatcher;
9
use Yiisoft\Yii\Debug\Helper\StreamWrapper\StreamWrapper;
10
use Yiisoft\Yii\Debug\Helper\StreamWrapper\StreamWrapperInterface;
11
12
use const SEEK_SET;
13
14
final class FilesystemStreamProxy implements StreamWrapperInterface
15
{
16
    public static bool $registered = false;
17
    /**
18
     * @var resource|null
19
     */
20
    public $context;
21
    public StreamWrapper $decorated;
22
    public bool $ignored = false;
23
24
    public static ?FilesystemStreamCollector $collector = null;
25
    public static array $ignoredPathPatterns = [];
26
    public static array $ignoredClasses = [];
27
    public array $operations = [];
28
29
    public function __construct()
30
    {
31
        $this->decorated = new StreamWrapper();
32
        $this->decorated->context = $this->context;
33
    }
34
35
    public function __call(string $name, array $arguments)
36
    {
37
        try {
38
            self::unregister();
39
            return $this->decorated->{$name}(...$arguments);
40
        } finally {
41
            self::register();
42
        }
43
    }
44
45
    public function __destruct()
46
    {
47
        if (self::$collector === null) {
48
            return;
49
        }
50
        foreach ($this->operations as $name => $operation) {
51
            self::$collector->collect(
52
                operation: $name,
53
                path: $operation['path'],
54
                args: $operation['args'],
55
            );
56
        }
57
        self::unregister();
58
    }
59
60
    public function __get(string $name)
61
    {
62
        return $this->decorated->{$name};
63
    }
64
65
    public static function register(): void
66
    {
67
        if (self::$registered) {
68
            return;
69
        }
70
        /**
71
         * It's important to trigger autoloader before unregistering the file stream handler
72
         */
73
        class_exists(BacktraceIgnoreMatcher::class);
74
        class_exists(StreamWrapper::class);
75
        class_exists(CombinedRegexp::class);
76
        stream_wrapper_unregister('file');
77
        stream_wrapper_register('file', self::class, STREAM_IS_URL);
78
        self::$registered = true;
79
    }
80
81
    public static function unregister(): void
82
    {
83
        if (!self::$registered) {
84
            return;
85
        }
86
        @stream_wrapper_restore('file');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for stream_wrapper_restore(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

86
        /** @scrutinizer ignore-unhandled */ @stream_wrapper_restore('file');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
87
        self::$registered = false;
88
    }
89
90
    private function isIgnored(): bool
91
    {
92
        $backtrace = debug_backtrace();
93
        return BacktraceIgnoreMatcher::isIgnoredByClass($backtrace, self::$ignoredClasses)
94
            || BacktraceIgnoreMatcher::isIgnoredByFile($backtrace, self::$ignoredPathPatterns);
95
    }
96
97
    public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_open" is not in camel caps format
Loading history...
98
    {
99
        $this->ignored = $this->isIgnored();
100
        return $this->__call(__FUNCTION__, func_get_args());
101
    }
102
103
    public function stream_read(int $count): string|false
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_read" is not in camel caps format
Loading history...
104
    {
105
        if (!$this->ignored) {
106
            $this->operations['read'] = [
107
                'path' => $this->decorated->filename,
108
                'args' => [],
109
            ];
110
        }
111
        return $this->__call(__FUNCTION__, func_get_args());
112
    }
113
114
    public function stream_set_option(int $option, int $arg1, ?int $arg2): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_set_option" is not in camel caps format
Loading history...
115
    {
116
        return $this->__call(__FUNCTION__, func_get_args());
117
    }
118
119
    public function stream_tell(): int
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_tell" is not in camel caps format
Loading history...
120
    {
121
        return $this->__call(__FUNCTION__, func_get_args());
122
    }
123
124
    public function stream_eof(): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_eof" is not in camel caps format
Loading history...
125
    {
126
        return $this->__call(__FUNCTION__, func_get_args());
127
    }
128
129
    public function stream_seek(int $offset, int $whence = SEEK_SET): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_seek" is not in camel caps format
Loading history...
130
    {
131
        return $this->__call(__FUNCTION__, func_get_args());
132
    }
133
134
    public function stream_cast(int $castAs)
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_cast" is not in camel caps format
Loading history...
135
    {
136
        return $this->__call(__FUNCTION__, func_get_args());
137
    }
138
139
    public function stream_stat(): array|false
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_stat" is not in camel caps format
Loading history...
140
    {
141
        return $this->__call(__FUNCTION__, func_get_args());
142
    }
143
144
    public function dir_closedir(): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::dir_closedir" is not in camel caps format
Loading history...
145
    {
146
        return $this->__call(__FUNCTION__, func_get_args());
147
    }
148
149
    public function dir_opendir(string $path, int $options): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::dir_opendir" is not in camel caps format
Loading history...
150
    {
151
        return $this->__call(__FUNCTION__, func_get_args());
152
    }
153
154
    public function dir_readdir(): false|string
0 ignored issues
show
Method name "FilesystemStreamProxy::dir_readdir" is not in camel caps format
Loading history...
155
    {
156
        if (!$this->ignored) {
157
            $this->operations['readdir'] = [
158
                'path' => $this->decorated->filename,
159
                'args' => [],
160
            ];
161
        }
162
        return $this->__call(__FUNCTION__, func_get_args());
163
    }
164
165
    public function dir_rewinddir(): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::dir_rewinddir" is not in camel caps format
Loading history...
166
    {
167
        return $this->__call(__FUNCTION__, func_get_args());
168
    }
169
170
    public function mkdir(string $path, int $mode, int $options): bool
171
    {
172
        if (!$this->isIgnored()) {
173
            $this->operations['mkdir'] = [
174
                'path' => $path,
175
                'args' => [
176
                    'mode' => $mode,
177
                    'options' => $options,
178
                ],
179
            ];
180
        }
181
        return $this->__call(__FUNCTION__, func_get_args());
182
    }
183
184
    public function rename(string $path_from, string $path_to): bool
185
    {
186
        if (!$this->isIgnored()) {
187
            $this->operations['rename'] = [
188
                'path' => $path_from,
189
                'args' => [
190
                    'path_to' => $path_to,
191
                ],
192
            ];
193
        }
194
        return $this->__call(__FUNCTION__, func_get_args());
195
    }
196
197
    public function rmdir(string $path, int $options): bool
198
    {
199
        if (!$this->isIgnored()) {
200
            $this->operations['rmdir'] = [
201
                'path' => $path,
202
                'args' => [
203
                    'options' => $options,
204
                ],
205
            ];
206
        }
207
        return $this->__call(__FUNCTION__, func_get_args());
208
    }
209
210
    public function stream_close(): void
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_close" is not in camel caps format
Loading history...
211
    {
212
        $this->__call(__FUNCTION__, func_get_args());
213
    }
214
215
    public function stream_flush(): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_flush" is not in camel caps format
Loading history...
216
    {
217
        return $this->__call(__FUNCTION__, func_get_args());
218
    }
219
220
    public function stream_lock(int $operation): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_lock" is not in camel caps format
Loading history...
221
    {
222
        return $this->__call(__FUNCTION__, func_get_args());
223
    }
224
225
    public function stream_metadata(string $path, int $option, mixed $value): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_metadata" is not in camel caps format
Loading history...
226
    {
227
        return $this->__call(__FUNCTION__, func_get_args());
228
    }
229
230
    public function stream_truncate(int $new_size): bool
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_truncate" is not in camel caps format
Loading history...
231
    {
232
        return $this->__call(__FUNCTION__, func_get_args());
233
    }
234
235
    public function stream_write(string $data): int
0 ignored issues
show
Method name "FilesystemStreamProxy::stream_write" is not in camel caps format
Loading history...
236
    {
237
        if (!$this->ignored) {
238
            $this->operations['write'] = [
239
                'path' => $this->decorated->filename,
240
                'args' => [],
241
            ];
242
        }
243
244
        return $this->__call(__FUNCTION__, func_get_args());
245
    }
246
247
    public function unlink(string $path): bool
248
    {
249
        if (!$this->isIgnored()) {
250
            $this->operations['unlink'] = [
251
                'path' => $path,
252
                'args' => [],
253
            ];
254
        }
255
        return $this->__call(__FUNCTION__, func_get_args());
256
    }
257
258
    public function url_stat(string $path, int $flags): array|false
0 ignored issues
show
Method name "FilesystemStreamProxy::url_stat" is not in camel caps format
Loading history...
259
    {
260
        return $this->__call(__FUNCTION__, func_get_args());
261
    }
262
}
263