File   F
last analyzed

Complexity

Total Complexity 120

Size/Duplication

Total Lines 781
Duplicated Lines 18.05 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
dl 141
loc 781
rs 1.819
c 0
b 0
f 0
wmc 120
lcom 1
cbo 6

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getFd() 0 4 1
B convertFlags() 0 21 8
B truncate() 19 19 7
B stat() 4 23 6
A statRefresh() 4 19 5
B statvfs() 0 24 6
A sync() 14 15 4
A datasync() 14 15 4
B write() 10 48 10
B chown() 21 21 7
A touch() 0 18 5
A clearStatCache() 0 5 1
B read() 8 28 7
D sendfile() 0 82 16
B readahead() 0 25 6
A readAllGenHandler() 14 15 3
A readAll() 12 36 5
A readAllChunkedGenHandler() 12 22 2
A readAllChunked() 9 31 4
A __toString() 0 4 1
A setChunkSize() 0 4 1
A seek() 0 9 2
A tell() 0 7 2
A close() 0 21 5
A __destruct() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like File often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use File, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace PHPDaemon\FS;
3
4
use PHPDaemon\Core\CallbackWrapper;
5
use PHPDaemon\Network\IOStream;
6
use PHPDaemon\Structures\StackCallbacks;
7
8
/**
9
 * File
10
 * @package PHPDaemon\FS
11
 * @author  Vasily Zorin <[email protected]>
12
 */
13
class File
14
{
15
    use \PHPDaemon\Traits\ClassWatchdog;
16
    use \PHPDaemon\Traits\StaticObjectWatchdog;
17
18
    /**
19
     * @var integer Priority
20
     */
21
    public $priority = 10;
22
23
    /**
24
     * @var integer Chunk size
25
     */
26
    public $chunkSize = 4096;
27
28
    /**
29
     * @var string $stat hash Stat
30
     */
31
    protected $stat;
32
33
    /**
34
     * @var array Cache of statvfs()
35
     */
36
    protected $statvfs;
37
38
    /**
39
     * @var integer Current offset
40
     */
41
    public $offset = 0;
42
43
    /**
44
     * @var string Cache key
45
     */
46
    public $fdCacheKey;
47
48
    /**
49
     * @var boolean Append?
50
     */
51
    public $append;
52
53
    /**
54
     * @var string Path
55
     */
56
    public $path;
57
58
    /**
59
     * @var boolean Writing?
60
     */
61
    public $writing = false;
62
63
    /**
64
     * @var boolean Closed?
65
     */
66
    public $closed = false;
67
68
    /**
69
     * @var object File descriptor
70
     */
71
    protected $fd;
72
73
    /**
74
     * @var PHPDaemon\Structures\StackCallbacks Stack of callbacks called when writing is done
75
     */
76
    protected $onWriteOnce;
77
78
    /**
79
     * File constructor
80
     * @param resource $fd Descriptor
81
     * @param string $path Path
82
     */
83
    public function __construct($fd, $path)
84
    {
85
        $this->fd = $fd;
0 ignored issues
show
Documentation Bug introduced by
It seems like $fd of type resource is incompatible with the declared type object of property $fd.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
86
        $this->path = $path;
87
        $this->onWriteOnce = new StackCallbacks;
0 ignored issues
show
Documentation Bug introduced by
It seems like new \PHPDaemon\Structures\StackCallbacks() of type object<PHPDaemon\Structures\StackCallbacks> is incompatible with the declared type object<PHPDaemon\FS\PHPD...uctures\StackCallbacks> of property $onWriteOnce.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
88
    }
89
90
    /**
91
     * Get file descriptor
92
     * @return resource File descriptor
0 ignored issues
show
Documentation introduced by
Should the return type not be object?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
93
     */
94
    public function getFd()
95
    {
96
        return $this->fd;
97
    }
98
99
    /**
100
     * Converts string of flags to integer or standard text representation
101
     * @param  string $mode Mode
102
     * @param  boolean $text Text?
103
     * @return mixed
104
     */
105
    public static function convertFlags($mode, $text = false)
106
    {
107
        $plus = mb_orig_strpos($mode, '+') !== false;
108
        $sync = mb_orig_strpos($mode, 's') !== false;
109
        $type = strtr($mode, ['b' => '', '+' => '', 's' => '', '!' => '']);
110
        if ($text) {
111
            return $type;
112
        }
113
        $types = [
114
            'r' => $plus ? EIO_O_RDWR : EIO_O_RDONLY,
115
            'w' => ($plus ? EIO_O_RDWR : EIO_O_WRONLY) | EIO_O_CREAT | EIO_O_TRUNC,
116
            'a' => ($plus ? EIO_O_RDWR : EIO_O_WRONLY) | EIO_O_CREAT | EIO_O_APPEND,
117
            'x' => ($plus ? EIO_O_RDWR : EIO_O_WRONLY) | EIO_O_EXCL | EIO_O_CREAT,
118
            'c' => ($plus ? EIO_O_RDWR : EIO_O_WRONLY) | EIO_O_CREAT,
119
        ];
120
        $m = $types[$type];
121
        if ($sync) {
122
            $m |= EIO_O_FSYNC;
123
        }
124
        return $m;
125
    }
126
127
    /**
128
     * Truncates this file
129
     * @param  integer $offset Offset. Default is 0
130
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
131
     * @param  integer $pri Priority
132
     * @return resource|boolean
133
     */
134 View Code Duplication
    public function truncate($offset = 0, $cb = null, $pri = EIO_PRI_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
    {
136
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 136 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
137
        if (!$this->fd || $this->fd === -1) {
138
            if ($cb) {
139
                $cb($this, false);
140
            }
141
            return false;
142
        }
143
        if (!FileSystem::$supported) {
144
            $fp = fopen($this->path, 'r+');
145
            $r = $fp && ftruncate($fp, $offset);
146
            if ($cb) {
147
                $cb($this, $r);
148
            }
149
            return $r;
150
        }
151
        return eio_ftruncate($this->fd, $offset, $pri, $cb, $this);
152
    }
153
154
    /**
155
     * Stat()
156
     * @param  callable $cb Callback
157
     * @param  integer $pri Priority
158
     * @return resource|boolean
159
     */
160
    public function stat($cb, $pri = EIO_PRI_DEFAULT)
161
    {
162
        $cb = CallbackWrapper::forceWrap($cb);
163
        if (!$this->fd || $this->fd === -1) {
164
            if ($cb) {
165
                $cb($this, false);
166
            }
167
            return false;
168
        }
169 View Code Duplication
        if (!FileSystem::$supported) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
            $cb($this, FileSystem::statPrepare(fstat($this->fd)));
171
            return false;
172
        }
173
        if ($this->stat) {
174
            $cb($this, $this->stat);
175
            return true;
176
        }
177
        return eio_fstat($this->fd, $pri, function ($file, $stat) use ($cb) {
178
            $stat = FileSystem::statPrepare($stat);
179
            $file->stat = $stat;
180
            $cb($file, $stat);
181
        }, $this);
182
    }
183
184
    /**
185
     * Stat() non-cached
186
     * @param  callable $cb Callback
187
     * @param  integer $pri Priority
188
     * @return resource|boolean
189
     */
190
    public function statRefresh($cb, $pri = EIO_PRI_DEFAULT)
191
    {
192
        $cb = CallbackWrapper::forceWrap($cb);
193
        if (!$this->fd || $this->fd === -1) {
194
            if ($cb) {
195
                $cb($this, false);
196
            }
197
            return false;
198
        }
199 View Code Duplication
        if (!FileSystem::$supported) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
            $cb($this, FileSystem::statPrepare(fstat($this->fd)));
201
            return true;
202
        }
203
        return eio_fstat($this->fd, $pri, function ($file, $stat) use ($cb) {
204
            $stat = FileSystem::statPrepare($stat);
205
            $file->stat = $stat;
206
            $cb($file, $stat);
207
        }, $this);
208
    }
209
210
    /**
211
     * Statvfs()
212
     * @param  callable $cb Callback
213
     * @param  integer $pri Priority
214
     * @return resource|boolean
215
     */
216
    public function statvfs($cb, $pri = EIO_PRI_DEFAULT)
217
    {
218
        $cb = CallbackWrapper::forceWrap($cb);
219
        if (!$this->fd) {
220
            if ($cb) {
221
                $cb($this, false);
222
            }
223
            return false;
224
        }
225
        if (!FileSystem::$supported) {
226
            if ($cb) {
227
                $cb($this, false);
228
            }
229
            return false;
230
        }
231
        if ($this->statvfs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->statvfs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
232
            $cb($this, $this->statvfs);
233
            return true;
234
        }
235
        return eio_fstatvfs($this->fd, $pri, function ($file, $stat) use ($cb) {
236
            $file->statvfs = $stat;
237
            $cb($file, $stat);
238
        }, $this);
239
    }
240
241
    /**
242
     * Sync()
243
     * @param  callable $cb Callback
244
     * @param  integer $pri Priority
245
     * @return resource|false
246
     */
247 View Code Duplication
    public function sync($cb, $pri = EIO_PRI_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
    {
249
        $cb = CallbackWrapper::forceWrap($cb);
250
        if (!$this->fd) {
251
            if ($cb) {
252
                $cb($this, false);
253
            }
254
            return false;
255
        }
256
        if (!FileSystem::$supported) {
257
            $cb($this, true);
258
            return false;
259
        }
260
        return eio_fsync($this->fd, $pri, $cb, $this);
261
    }
262
263
    /**
264
     * Datasync()
265
     * @param  callable $cb Callback
266
     * @param  integer $pri Priority
267
     * @return resource|false
268
     */
269 View Code Duplication
    public function datasync($cb, $pri = EIO_PRI_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
    {
271
        $cb = CallbackWrapper::forceWrap($cb);
272
        if (!$this->fd) {
273
            if ($cb) {
274
                $cb($this, false);
275
            }
276
            return false;
277
        }
278
        if (!FileSystem::$supported) {
279
            $cb($this, true);
280
            return false;
281
        }
282
        return eio_fdatasync($this->fd, $pri, $cb, $this);
283
    }
284
285
    /**
286
     * Writes data to file
287
     * @param  string $data Data
288
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
289
     * @param  integer $offset Offset
0 ignored issues
show
Documentation introduced by
Should the type for parameter $offset not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
290
     * @param  integer $pri Priority
291
     * @return resource|false
292
     */
293
    public function write($data, $cb = null, $offset = null, $pri = EIO_PRI_DEFAULT)
294
    {
295
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 295 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
296
        if (!$this->fd) {
297
            if ($cb) {
298
                $cb($this, false);
299
            }
300
            return false;
301
        }
302
        if ($data === '') {
303
            if ($cb) {
304
                $cb($this, 0);
305
            }
306
            return false;
307
        }
308 View Code Duplication
        if (!FileSystem::$supported) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309
            if ($offset !== null) {
310
                fseek($data, $offset);
311
            }
312
            $r = fwrite($this->fd, $data);
313
            if ($cb) {
314
                $cb($this, $r);
315
            }
316
            return false;
317
        }
318
        if ($cb !== null) {
319
            $this->onWriteOnce->push($cb);
320
        }
321
        $l = mb_orig_strlen($data);
322
        if ($offset === null) {
323
            $offset = $this->offset;
324
            $this->offset += $l;
325
        }
326
        $this->writing = true;
327
        $res = eio_write(
328
            $this->fd,
329
            $data,
330
            $l,
331
            $offset,
332
            $pri,
333
            function ($file, $result) {
334
                $this->writing = false;
335
                $this->onWriteOnce->executeAll($file, $result);
336
            },
337
            $this
338
        );
339
        return $res;
340
    }
341
342
    /**
343
     * Changes ownership of this file
344
     * @param  integer $uid User ID
345
     * @param  integer $gid Group ID
346
     * @param  callable $cb = null Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
347
     * @param  integer $pri = EIO_PRI_DEFAULT Priority
348
     * @return resource|false
349
     */
350 View Code Duplication
    public function chown($uid, $gid = -1, $cb = null, $pri = EIO_PRI_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
351
    {
352
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 352 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
353
        if (!$this->fd) {
354
            if ($cb) {
355
                $cb($this, false);
356
            }
357
            return false;
358
        }
359
        if (!FileSystem::$supported) {
360
            $r = chown($this->path, $uid);
361
            if ($gid !== -1) {
362
                $r = $r && chgrp($this->path, $gid);
363
            }
364
            if ($cb) {
365
                $cb($this, $r);
366
            }
367
            return false;
368
        }
369
        return eio_fchown($this->fd, $uid, $gid, $pri, $cb, $this);
370
    }
371
372
    /**
373
     * touch()
374
     * @param  integer $mtime Last modification time
375
     * @param  integer $atime Last access time
0 ignored issues
show
Documentation introduced by
Should the type for parameter $atime not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
376
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
377
     * @param  integer $pri Priority
378
     * @return resource|false
379
     */
380
    public function touch($mtime, $atime = null, $cb = null, $pri = EIO_PRI_DEFAULT)
381
    {
382
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 382 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
383
        if (!$this->fd) {
384
            if ($cb) {
385
                $cb($this, false);
386
            }
387
            return false;
388
        }
389
        if (!FileSystem::$supported) {
390
            $r = touch($this->path, $mtime, $atime);
391
            if ($cb) {
392
                $cb($this, $r);
393
            }
394
            return false;
395
        }
396
        return eio_futime($this->fd, $atime, $mtime, $pri, $cb, $this);
397
    }
398
399
    /**
400
     * Clears cache of stat() and statvfs()
401
     * @return void
402
     */
403
    public function clearStatCache()
404
    {
405
        $this->stat = null;
406
        $this->statvfs = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $statvfs.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
407
    }
408
409
    /**
410
     * Reads data from file
411
     * @param  integer $length Length
412
     * @param  integer $offset Offset
0 ignored issues
show
Documentation introduced by
Should the type for parameter $offset not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
413
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
414
     * @param  integer $pri Priority
415
     * @return boolean
0 ignored issues
show
Documentation introduced by
Should the return type not be string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
416
     */
417
    public function read($length, $offset = null, $cb = null, $pri = EIO_PRI_DEFAULT)
418
    {
419
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 419 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
420
        if (!$this->fd) {
421
            if ($cb) {
422
                $cb($this, false);
423
            }
424
            return false;
425
        }
426 View Code Duplication
        if (!FileSystem::$supported) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
427
            if ($offset !== null) {
428
                fseek($this->fd, $length);
429
            }
430
            $data = fread($this->fd, $length);
431
            $cb === null || $cb($this, $data);
432
            return $data;
433
        }
434
        $this->offset += $length;
435
        eio_read(
436
            $this->fd,
437
            $length,
438
            $offset !== null ? $offset : $this->offset,
439
            $pri,
440
            $cb,
441
            $this
442
        );
443
        return true;
444
    }
445
446
    /**
447
     * sendfile()
448
     * @param  mixed $outfd File descriptor
449
     * @param  callable $cb Callback
450
     * @param  callable $startCb Start callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $startCb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
451
     * @param  integer $offset Offset
452
     * @param  integer $length Length
0 ignored issues
show
Documentation introduced by
Should the type for parameter $length not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
453
     * @param  integer $pri Priority
454
     * @return boolean           Success
455
     */
456
    public function sendfile($outfd, $cb, $startCb = null, $offset = 0, $length = null, $pri = EIO_PRI_DEFAULT)
457
    {
458
        $cb = CallbackWrapper::forceWrap($cb);
459
        if (!$this->fd) {
460
            if ($cb) {
461
                $cb($this, false);
462
            }
463
            return false;
464
        }
465
        if (!FileSystem::$supported) {
466
            if ($cb) {
467
                $cb($this, false);
468
            }
469
            return false;
470
        }
471
        static $chunkSize = 1024;
472
        $ret = true;
473
474
        $handler = function (
475
            $file,
476
            $sent = -1
477
        ) use (
478
            &$ret,
479
            $outfd,
480
            $cb,
481
            &$handler,
482
            &$offset,
483
            &$length,
484
            $pri,
485
            $chunkSize
486
        ) {
487
            if ($outfd instanceof IOStream) {
488
                if ($outfd->isFreed()) {
489
                    $cb($file, false);
490
                    return;
491
                }
492
                $ofd = $outfd->getFd();
493
            } else {
494
                $ofd = $outfd;
495
            }
496
            if (!$ret) {
497
                $cb($file, false);
498
                return;
499
            }
500
            if ($sent === -1) {
501
                $sent = 0;
502
            }
503
            $offset += $sent;
504
            $length -= $sent;
505
            if ($length <= 0) {
506
                $cb($file, true);
507
                return;
508
            }
509
            if (!$ofd) {
510
                $cb($file, false);
511
                return;
512
            }
513
            $c = min($chunkSize, $length);
514
            $ret = eio_sendfile($ofd, $file->fd, $offset, $c, $pri, $handler, $file);
515
        };
516
        if ($length !== null) {
517
            if ($startCb !== null) {
518
                if (!$startCb($this, $length, $handler)) {
519
                    $handler($this);
520
                }
521
            } else {
522
                $handler($this);
523
            }
524
            return true;
525
        }
526
        $this->statRefresh(function ($file, $stat) use ($startCb, $handler, &$length) {
527
            $length = $stat['size'];
528
            if ($startCb !== null) {
529
                if (!$startCb($file, $length, $handler)) {
530
                    $handler($file);
531
                }
532
            } else {
533
                $handler($file);
534
            }
535
        }, $pri);
536
        return true;
537
    }
538
539
    /**
540
     * readahead()
541
     * @param  integer $length Length
542
     * @param  integer $offset Offset
0 ignored issues
show
Documentation introduced by
Should the type for parameter $offset not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
543
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
544
     * @param  integer $pri Priority
545
     * @return resource|false
546
     */
547
    public function readahead($length, $offset = null, $cb = null, $pri = EIO_PRI_DEFAULT)
548
    {
549
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 549 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
550
        if (!$this->fd) {
551
            if ($cb) {
552
                $cb($this, false);
553
            }
554
            return false;
555
        }
556
        if (!FileSystem::$supported) {
557
            if ($cb) {
558
                $cb($this, false);
559
            }
560
            return false;
561
        }
562
        $this->offset += $length;
563
        return eio_readahead(
564
            $this->fd,
565
            $length,
566
            $offset !== null ? $offset : $this->offset,
567
            $pri,
568
            $cb,
569
            $this
570
        );
571
    }
572
573
    /**
574
     * Generates closure-callback for readAll
575
     * @param  callable $cb
576
     * @param  integer $size
577
     * @param  integer &$offset
578
     * @param  integer &$pri
579
     * @param  string &$buf
580
     * @return callable
581
     */
582 View Code Duplication
    protected function readAllGenHandler($cb, $size, &$offset, &$pri, &$buf)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
583
    {
584
        return function ($file, $data) use ($cb, $size, &$offset, &$pri, &$buf) {
585
            $buf .= $data;
586
            $offset += mb_orig_strlen($data);
587
            $len = min($file->chunkSize, $size - $offset);
588
            if ($offset >= $size) {
589
                if ($cb) {
590
                    $cb($file, $buf);
591
                }
592
                return;
593
            }
594
            eio_read($file->fd, $len, $offset, $pri, $this->readAllGenHandler($cb, $size, $offset, $pri, $buf), $this);
595
        };
596
    }
597
598
    /**
599
     * Reads whole file
600
     * @param  callable $cb Callback
601
     * @param  integer $pri Priority
602
     * @return boolean       Success
603
     */
604
    public function readAll($cb, $pri = EIO_PRI_DEFAULT)
605
    {
606
        $cb = CallbackWrapper::forceWrap($cb);
607
        if (!$this->fd) {
608
            if ($cb) {
609
                $cb($this, false);
610
            }
611
            return false;
612
        }
613
        $this->statRefresh(
614 View Code Duplication
            function ($file, $stat) use ($cb, $pri) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
615
                if (!$stat) {
616
                    if ($cb) {
617
                        $cb($file, false);
618
                    }
619
                    return;
620
                }
621
622
                $offset = 0;
623
                $buf = '';
624
                $size = $stat['size'];
625
626
                eio_read(
627
                    $file->fd,
628
                    min($file->chunkSize, $size),
629
                    0,
630
                    $pri,
631
                    $this->readAllGenHandler($cb, $size, $offset, $pri, $buf),
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 606 can be null; however, PHPDaemon\FS\File::readAllGenHandler() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
632
                    $file
633
                );
634
            },
635
            $pri
636
        );
637
638
        return true;
639
    }
640
641
    /**
642
     * Generates closure-callback for readAllChunked
643
     * @param  callable $cb
644
     * @param  callable $chunkcb
645
     * @param  integer $size
646
     * @param  integer $offset
647
     * @param  integer $pri
648
     * @return callable
649
     */
650 View Code Duplication
    protected function readAllChunkedGenHandler($cb, $chunkcb, $size, &$offset, $pri)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
651
    {
652
        return function ($file, $data) use ($cb, $chunkcb, $size, &$offset, $pri) {
653
            $chunkcb($file, $data);
654
            $offset += mb_orig_strlen($data);
655
            $len = min($file->chunkSize, $size - $offset);
656
657
            if ($offset >= $size) {
658
                $cb($file, true);
659
                return;
660
            }
661
662
            eio_read(
663
                $file->fd,
664
                $len,
665
                $offset,
666
                $pri,
667
                $this->readAllChunkedGenHandler($cb, $chunkcb, $size, $offset, $pri),
668
                $file
669
            );
670
        };
671
    }
672
673
    /**
674
     * Reads file chunk-by-chunk
675
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
676
     * @param  callable $chunkcb Callback of chunk
0 ignored issues
show
Documentation introduced by
Should the type for parameter $chunkcb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
677
     * @param  integer $pri Priority
678
     * @return resource|false
0 ignored issues
show
Documentation introduced by
Should the return type not be resource|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
679
     */
680
    public function readAllChunked($cb = null, $chunkcb = null, $pri = EIO_PRI_DEFAULT)
681
    {
682
        $cb = CallbackWrapper::forceWrap($cb);
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 682 can also be of type null; however, PHPDaemon\Core\CallbackWrapper::forceWrap() does only seem to accept callable, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
683
        if (!$this->fd) {
684
            if ($cb) {
685
                $cb($this, false);
686
            }
687
            return false;
688
        }
689
        return $this->statRefresh(
690 View Code Duplication
            function ($file, $stat) use ($cb, $chunkcb, $pri) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
691
                if (!$stat) {
692
                    $cb($file, false);
693
                    return;
694
                }
695
696
                $offset = 0;
697
                $size = $stat['size'];
698
699
                eio_read(
700
                    $file->fd,
701
                    min($file->chunkSize, $size),
702
                    $offset,
703
                    $pri,
704
                    $this->readAllChunkedGenHandler($cb, $chunkcb, $size, $offset, $pri),
0 ignored issues
show
Bug introduced by
It seems like $cb defined by \PHPDaemon\Core\CallbackWrapper::forceWrap($cb) on line 682 can be null; however, PHPDaemon\FS\File::readAllChunkedGenHandler() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
Bug introduced by
It seems like $chunkcb defined by parameter $chunkcb on line 680 can also be of type null; however, PHPDaemon\FS\File::readAllChunkedGenHandler() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
705
                    $file
706
                );
707
            },
708
            $pri
709
        );
710
    }
711
712
    /**
713
     * toString handler
714
     * @return string
715
     */
716
    public function __toString()
717
    {
718
        return $this->path;
719
    }
720
721
    /**
722
     * Set chunk size
723
     * @param  integer $n Chunk size
724
     * @return void
725
     */
726
    public function setChunkSize($n)
727
    {
728
        $this->chunkSize = $n;
729
    }
730
731
    /**
732
     * Move pointer to arbitrary position
733
     * @param  integer $offset Offset
734
     * @param  callable $cb Callback
735
     * @param  integer $pri Priority
736
     * @return resource|false
737
     */
738
    public function seek($offset, $cb, $pri = EIO_PRI_DEFAULT)
739
    {
740
        $cb = CallbackWrapper::forceWrap($cb);
741
        if (!\EIO::$supported) {
742
            fseek($this->fd, $offset);
743
            return false;
744
        }
745
        return eio_seek($this->fd, $offset, $pri, $cb, $this);
746
    }
747
748
    /**
749
     * Get current pointer position
750
     * @return integer
751
     */
752
    public function tell()
753
    {
754
        if (\EIO::$supported) {
755
            return $this->offset;
756
        }
757
        return ftell($this->fd);
758
    }
759
760
    /**
761
     * Close the file
762
     * @return resource|false
763
     */
764
    public function close()
765
    {
766
        if ($this->closed) {
767
            return false;
768
        }
769
        $this->closed = true;
770
        if ($this->fdCacheKey !== null) {
771
            FileSystem::$fdCache->invalidate($this->fdCacheKey);
772
        }
773
        if ($this->fd === null) {
774
            return false;
775
        }
776
777
        if (!FileSystem::$supported) {
778
            fclose($this->fd);
779
            return false;
780
        }
781
        $r = eio_close($this->fd, EIO_PRI_MAX);
782
        $this->fd = null;
783
        return $r;
784
    }
785
786
    /**
787
     * Destructor
788
     */
789
    public function __destruct()
790
    {
791
        $this->close();
792
    }
793
}
794