Completed
Push — master ( 235b38...44849f )
by Benjamin
02:48
created

LineReader::readLines()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 2
1
<?php
2
3
namespace Bcremer\LineReader;
4
5
final class LineReader
6
{
7
    /**
8
     * Prevent instantiation
9
     */
10
    private function __construct()
11
    {
12
    }
13
14
    /**
15
     * @param string $filePath
16
     * @return \Generator
17
     * @throws \InvalidArgumentException if $filePath is not readable
18
     */
19 4
    public static function readLines($filePath)
20
    {
21 4
        if (!$fh = @fopen($filePath, 'r')) {
22 1
            throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath);
23
        }
24
25 3
        return self::read($fh);
26
    }
27
28
    /**
29
     * @param string $filePath
30
     * @return \Generator
31
     */
32 1
    public static function readLinesBackwards($filePath)
33
    {
34 1
        if (!$fh = @fopen($filePath, 'r')) {
35
            throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath);
36
        }
37
38 1
        $size = filesize($filePath);
39 1
        return self::readBackwards($fh, $size);
40
    }
41
42
    /**
43
     * @param resource $fh
44
     * @return \Generator
45
     */
46 3
    private static function read($fh)
47
    {
48 3
        while (false !== $line = fgets($fh)) {
49 3
            yield rtrim($line, "\n");
50
        }
51
52 2
        fclose($fh);
53 2
    }
54
55
    /**
56
     * Read a file from the end using a buffer.
57
     *
58
     * This is way more efficient than using the naive method
59
     * of reading the file backwards byte by byte looking for
60
     * a newline character.
61
     *
62
     * @see http://stackoverflow.com/a/10494801/147634
63
     * @param resource $fh
64
     * @param int $pos
65
     * @return \Generator
66
     */
67 1
    private static function readBackwards($fh, $pos)
68
    {
69 1
        $buffer = null;
70 1
        $bufferSize = 4096;
71
72 1
        while (true) {
73 1
            if (isset($buffer[1])) { // faster than count($buffer) > 1
74 1
                yield array_pop($buffer);
75 1
                continue;
76
            }
77
78 1
            if ($pos === 0) {
79
                yield array_pop($buffer);
80
                break;
81
            }
82
83 1
            if ($bufferSize > $pos) {
84
                $bufferSize = $pos;
85
                $pos = 0;
86
            } else {
87 1
                $pos -= $bufferSize;
88
            }
89 1
            fseek($fh, $pos);
90 1
            $chunk = fread($fh, $bufferSize);
91 1
            if ($buffer) {
0 ignored issues
show
Bug Best Practice introduced by Benjamin Cremer
The expression $buffer 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...
92
                $buffer = explode("\n", $chunk . $buffer[0]);
93
            } else {
94 1
                $chunk = rtrim($chunk, "\n");
95 1
                $buffer = explode("\n", $chunk);
96
            }
97
        }
98
    }
99
}
100