Passed
Pull Request — master (#215)
by Dmitriy
13:45
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(StreamWrapper::class);
69
        stream_wrapper_unregister('file');
70
        stream_wrapper_register('file', self::class, STREAM_IS_URL);
71
        self::$registered = true;
72
    }
73
74
    public static function unregister(): void
75
    {
76
        if (!self::$registered) {
77
            return;
78
        }
79
        @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

79
        /** @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...
80
        self::$registered = false;
81
    }
82
83
    private function isIgnored(): bool
84
    {
85
        $backtrace = debug_backtrace();
86
        return BacktraceIgnoreMatcher::isIgnoredByClass($backtrace, self::$ignoredClasses)
87
            || BacktraceIgnoreMatcher::isIgnoredByFile($backtrace, self::$ignoredPathPatterns);
88
    }
89
90
    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...
91
    {
92
        $this->ignored = $this->isIgnored();
93
        return $this->__call(__FUNCTION__, func_get_args());
94
    }
95
96
    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...
97
    {
98
        if (!$this->ignored) {
99
            $this->operations['read'] = [
100
                '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...
101
                'args' => [],
102
            ];
103
        }
104
        return $this->__call(__FUNCTION__, func_get_args());
105
    }
106
107
    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...
108
    {
109
        return $this->__call(__FUNCTION__, func_get_args());
110
    }
111
112
    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...
113
    {
114
        return $this->__call(__FUNCTION__, func_get_args());
115
    }
116
117
    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...
118
    {
119
        return $this->__call(__FUNCTION__, func_get_args());
120
    }
121
122
    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...
123
    {
124
        return $this->__call(__FUNCTION__, func_get_args());
125
    }
126
127
    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...
128
    {
129
        return $this->__call(__FUNCTION__, func_get_args());
130
    }
131
132
    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...
133
    {
134
        return $this->__call(__FUNCTION__, func_get_args());
135
    }
136
137
    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...
138
    {
139
        return $this->__call(__FUNCTION__, func_get_args());
140
    }
141
142
    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...
143
    {
144
        return $this->__call(__FUNCTION__, func_get_args());
145
    }
146
147
    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...
148
    {
149
        if (!$this->ignored) {
150
            $this->operations['readdir'] = [
151
                '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...
152
                'args' => [],
153
            ];
154
        }
155
        return $this->__call(__FUNCTION__, func_get_args());
156
    }
157
158
    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...
159
    {
160
        return $this->__call(__FUNCTION__, func_get_args());
161
    }
162
163
    public function mkdir(string $path, int $mode, int $options): bool
164
    {
165
        if (!$this->isIgnored()) {
166
            $this->operations['mkdir'] = [
167
                'path' => $path,
168
                'args' => [
169
                    'mode' => $mode,
170
                    'options' => $options,
171
                ],
172
            ];
173
        }
174
        return $this->__call(__FUNCTION__, func_get_args());
175
    }
176
177
    public function rename(string $path_from, string $path_to): bool
178
    {
179
        if (!$this->isIgnored()) {
180
            $this->operations['rename'] = [
181
                'path' => $path_from,
182
                'args' => [
183
                    'path_to' => $path_to,
184
                ],
185
            ];
186
        }
187
        return $this->__call(__FUNCTION__, func_get_args());
188
    }
189
190
    public function rmdir(string $path, int $options): bool
191
    {
192
        if (!$this->isIgnored()) {
193
            $this->operations['rmdir'] = [
194
                'path' => $path,
195
                'args' => [
196
                    'options' => $options,
197
                ],
198
            ];
199
        }
200
        return $this->__call(__FUNCTION__, func_get_args());
201
    }
202
203
    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...
204
    {
205
        $this->__call(__FUNCTION__, func_get_args());
206
    }
207
208
    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...
209
    {
210
        return $this->__call(__FUNCTION__, func_get_args());
211
    }
212
213
    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...
214
    {
215
        return $this->__call(__FUNCTION__, func_get_args());
216
    }
217
218
    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...
219
    {
220
        return $this->__call(__FUNCTION__, func_get_args());
221
    }
222
223
    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...
224
    {
225
        return $this->__call(__FUNCTION__, func_get_args());
226
    }
227
228
    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...
229
    {
230
        if (!$this->ignored) {
231
            $this->operations['write'] = [
232
                '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...
233
                'args' => [],
234
            ];
235
        }
236
237
        return $this->__call(__FUNCTION__, func_get_args());
238
    }
239
240
    public function unlink(string $path): bool
241
    {
242
        if (!$this->isIgnored()) {
243
            $this->operations['unlink'] = [
244
                'path' => $path,
245
                'args' => [],
246
            ];
247
        }
248
        return $this->__call(__FUNCTION__, func_get_args());
249
    }
250
251
    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...
252
    {
253
        return $this->__call(__FUNCTION__, func_get_args());
254
    }
255
}
256