Passed
Push — master ( bace86...bb4cad )
by Dmitriy
05:30 queued 02:57
created

FilesystemStreamProxy::isIgnored()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Debug\Collector\Stream;
6
7
use Yiisoft\Yii\Debug\Helper\BacktraceIgnoreMatcher;
8
use Yiisoft\Yii\Debug\Helper\StreamWrapper\StreamWrapper;
9
use Yiisoft\Yii\Debug\Helper\StreamWrapper\StreamWrapperInterface;
10
11
use const SEEK_SET;
12
13
class FilesystemStreamProxy implements StreamWrapperInterface
14
{
15
    public static bool $registered = false;
16
    /**
17
     * @var resource|null
18
     */
19
    public $context;
20
    public StreamWrapperInterface $decorated;
21
    public bool $ignored = false;
22
23
    public static ?FilesystemStreamCollector $collector = null;
24
    public static array $ignoredPathPatterns = [];
25
    public static array $ignoredClasses = [];
26
    public array $operations = [];
27
28
    public function __construct()
29
    {
30
        $this->decorated = new StreamWrapper();
31
        $this->decorated->context = $this->context;
0 ignored issues
show
Bug introduced by
Accessing context on the interface Yiisoft\Yii\Debug\Helper...\StreamWrapperInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
32
    }
33
34
    public function __call(string $name, array $arguments)
35
    {
36
        try {
37
            self::unregister();
38
            return $this->decorated->{$name}(...$arguments);
39
        } finally {
40
            self::register();
41
        }
42
    }
43
44
    public function __destruct()
45
    {
46
        foreach ($this->operations as $name => $operation) {
47
            self::$collector->collect(
0 ignored issues
show
Bug introduced by
The method collect() does not exist on null. ( Ignorable by Annotation )

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

47
            self::$collector->/** @scrutinizer ignore-call */ 
48
                              collect(

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...
48
                operation: $name,
49
                path: $operation['path'],
50
                args: $operation['args'],
51
            );
52
        }
53
    }
54
55
    public function __get(string $name)
56
    {
57
        return $this->decorated->{$name};
58
    }
59
60
    public static function register(): void
61
    {
62
        if (self::$registered) {
63
            return;
64
        }
65
        /**
66
         * It's important to trigger autoloader before unregistering the file stream handler
67
         */
68
        class_exists(BacktraceIgnoreMatcher::class);
69
        class_exists(StreamWrapper::class);
70
        stream_wrapper_unregister('file');
71
        stream_wrapper_register('file', self::class, STREAM_IS_URL);
72
        self::$registered = true;
73
    }
74
75
    public static function unregister(): void
76
    {
77
        if (!self::$registered) {
78
            return;
79
        }
80
        @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

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