Issues (84)

src/FileHelper/FileInfo/LineReader.php (1 issue)

1
<?php
2
/**
3
 * File containing the class {@see \AppUtils\FileHelper\FileInfo\LineReader}.
4
 *
5
 * @package AppUtils
6
 * @subpackage FileHelper
7
 * @see \AppUtils\FileHelper\FileInfo\LineReader
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils\FileHelper\FileInfo;
13
14
use AppUtils\ConvertHelper;
15
use AppUtils\FileHelper;
16
use AppUtils\FileHelper\FileInfo;
17
use AppUtils\FileHelper_Exception;
18
use SplFileObject;
19
20
/**
21
 * Utility used to read contents from a file, line by line.
22
 *
23
 * @package AppUtils
24
 * @subpackage FileHelper
25
 * @author Sebastian Mordziol <[email protected]>
26
 */
27
class LineReader
28
{
29
    /**
30
     * @var FileInfo
31
     */
32
    private FileInfo $file;
33
34
    public function __construct(FileInfo $file)
35
    {
36
        $this->file = $file;
37
    }
38
39
    /**
40
     * @param int $lineNumber
41
     * @return string|null
42
     * @throws FileHelper_Exception
43
     */
44
    public function getLine(int $lineNumber) : ?string
45
    {
46
        $this->file->requireExists();
47
48
        $file = new SplFileObject($this->file->getPath());
49
50
        if($file->eof()) {
51
            return '';
52
        }
53
54
        $targetLine = $lineNumber-1;
55
56
        $file->seek($targetLine);
57
58
        if($file->key() !== $targetLine)
59
        {
60
            return null;
61
        }
62
63
        return $file->current();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $file->current() could return the type array which is incompatible with the type-hinted return null|string. Consider adding an additional type-check to rule them out.
Loading history...
64
    }
65
66
    public function countLines() : int
67
    {
68
        $this->file->requireExists();
69
        $path = $this->file->getPath();
70
71
        $spl = new SplFileObject($path);
72
73
        // tries seeking as far as possible
74
        $spl->seek(PHP_INT_MAX);
75
76
        $number = $spl->key();
77
78
        // if seeking to the end the cursor is still at 0, there are no lines.
79
        if($number === 0)
80
        {
81
            // since it's a very small file, to get reliable results,
82
            // we read its contents and use that to determine what
83
            // kind of contents we are dealing with. Tests have shown
84
            // that this is not practical to solve with the SplFileObject.
85
            $content = file_get_contents($path);
86
87
            if(empty($content)) {
88
                return 0;
89
            }
90
        }
91
92
        // return the line number we were able to reach + 1 (key is zero-based)
93
        return $number+1;
94
    }
95
96
    /**
97
     * @param int $amount
98
     * @return string[]
99
     * @throws FileHelper_Exception
100
     */
101
    public function getLines(int $amount=0) : array
102
    {
103
        $this->file->requireExists();
104
105
        $fn = fopen($this->file->getPath(), 'rb');
106
107
        if($fn === false)
108
        {
109
            throw new FileHelper_Exception(
110
                'Could not open file for reading.',
111
                sprintf(
112
                    'Tried accessing file at [%s].',
113
                    $this->file->getPath()
114
                ),
115
                FileHelper::ERROR_CANNOT_OPEN_FILE_TO_READ_LINES
116
            );
117
        }
118
119
        $result = array();
120
        $counter = 0;
121
        $first = true;
122
123
        while(!feof($fn))
124
        {
125
            $counter++;
126
127
            $line = fgets($fn);
128
129
            // can happen with zero length files
130
            if($line === false) {
131
                continue;
132
            }
133
134
            // the first line may contain a unicode BOM marker.
135
            if($first)
136
            {
137
                $line = ConvertHelper::stripUTFBom($line);
138
                $first = false;
139
            }
140
141
            $result[] = $line;
142
143
            if($amount > 0 && $counter === $amount) {
144
                break;
145
            }
146
        }
147
148
        fclose($fn);
149
150
        return $result;
151
    }
152
}
153