]> git.mxchange.org Git - friendica.git/blob - src/Util/ReversedFileReader.php
add self for unfollowed contact
[friendica.git] / src / Util / ReversedFileReader.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Util;
23
24 /**
25  * An iterator which returns lines from file in reversed order
26  *
27  * original code https://stackoverflow.com/a/10494801
28  */
29 class ReversedFileReader implements \Iterator
30 {
31         const BUFFER_SIZE = 4096;
32         const SEPARATOR   = "\n";
33
34         /** @var resource */
35         private $fh = null;
36
37         /** @var int */
38         private $filesize = -1;
39
40         /** @var int */
41         private $pos = -1;
42
43         /** @var array */
44         private $buffer = null;
45
46         /** @var int */
47         private $key = -1;
48
49         /** @var string */
50         private $value = null;
51
52         /**
53          * Open $filename for read and reset iterator
54          *
55          * @param string $filename      File to open
56          * @return $this
57          */
58         public function open(string $filename): ReversedFileReader
59         {
60                 $this->fh = fopen($filename, 'r');
61                 if (!$this->fh) {
62                         // this should use a custom exception.
63                         throw new \Exception("Unable to open $filename");
64                 }
65                 $this->filesize = filesize($filename);
66                 $this->pos      = -1;
67                 $this->buffer   = null;
68                 $this->key      = -1;
69                 $this->value    = null;
70                 return $this;
71         }
72
73         /**
74          * Read $size bytes behind last position
75          *
76          * @param int $size
77          * @return string
78          */
79         private function _read(int $size): string
80         {
81                 $this->pos -= $size;
82                 fseek($this->fh, $this->pos);
83                 return fread($this->fh, $size);
84         }
85
86         /**
87          * Read next line from end of file
88          * Return null if no lines are left to read
89          *
90          * @return string|null Depending on data being buffered
91          */
92         private function _readline(): ?string
93         {
94                 $buffer = & $this->buffer;
95                 while (true) {
96                         if ($this->pos == 0) {
97                                 return array_pop($buffer);
98                         }
99                         if (is_null($buffer)) {
100                                 return null;
101                         }
102                         if (count($buffer) > 1) {
103                                 return array_pop($buffer);
104                         }
105                         $buffer = explode(self::SEPARATOR, $this->_read(self::BUFFER_SIZE) . $buffer[0]);
106                 }
107         }
108
109         /**
110          * Fetch next line from end and set it as current iterator value.
111          *
112          * @see Iterator::next()
113          * @return void
114          */
115         #[\ReturnTypeWillChange]
116         public function next()
117         {
118                 ++$this->key;
119                 $this->value = $this->_readline();
120         }
121
122         /**
123          * Rewind iterator to the first line at the end of file
124          *
125          * @see Iterator::rewind()
126          * @return void
127          */
128         #[\ReturnTypeWillChange]
129         public function rewind()
130         {
131                 if ($this->filesize > 0) {
132                         $this->pos    = $this->filesize;
133                         $this->value  = null;
134                         $this->key    = -1;
135                         $this->buffer = explode(self::SEPARATOR, $this->_read($this->filesize % self::BUFFER_SIZE ?: self::BUFFER_SIZE));
136                         $this->next();
137                 }
138         }
139
140         /**
141          * Return current line number, starting from zero at the end of file
142          *
143          * @see Iterator::key()
144          * @return int
145          */
146         public function key(): int
147         {
148                 return $this->key;
149         }
150
151         /**
152          * Return current line
153          *
154          * @see Iterator::current()
155          * @return string
156          */
157         public function current(): string
158         {
159                 return $this->value;
160         }
161
162         /**
163          * Checks if current iterator value is valid, that is, we read all lines in files
164          *
165          * @see Iterator::valid()
166          * @return bool
167          */
168         public function valid(): bool
169         {
170                 return ! is_null($this->value);
171         }
172 }