1<?php
2
3/**
4 * Hoa
5 *
6 *
7 * @license
8 *
9 * New BSD License
10 *
11 * Copyright © 2007-2017, Hoa community. All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 *     * Redistributions of source code must retain the above copyright
16 *       notice, this list of conditions and the following disclaimer.
17 *     * Redistributions in binary form must reproduce the above copyright
18 *       notice, this list of conditions and the following disclaimer in the
19 *       documentation and/or other materials provided with the distribution.
20 *     * Neither the name of the Hoa nor the names of its contributors may be
21 *       used to endorse or promote products derived from this software without
22 *       specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37namespace Hoa\File\Link;
38
39use Hoa\File;
40use Hoa\Stream;
41
42/**
43 * Class \Hoa\File\Link\ReadWrite.
44 *
45 * File handler.
46 *
47 * @copyright  Copyright © 2007-2017 Hoa community
48 * @license    New BSD License
49 */
50class          ReadWrite
51    extends    Link
52    implements Stream\IStream\In,
53               Stream\IStream\Out
54{
55    /**
56     * Open a file.
57     *
58     * @param   string  $streamName    Stream name.
59     * @param   string  $mode          Open mode, see the parent::MODE_* constants.
60     * @param   string  $context       Context ID (please, see the
61     *                                 \Hoa\Stream\Context class).
62     * @param   bool    $wait          Differ opening or not.
63     */
64    public function __construct(
65        $streamName,
66        $mode    = parent::MODE_APPEND_READ_WRITE,
67        $context = null,
68        $wait    = false
69    ) {
70        parent::__construct($streamName, $mode, $context, $wait);
71
72        return;
73    }
74
75    /**
76     * Open the stream and return the associated resource.
77     *
78     * @param   string               $streamName    Stream name (e.g. path or URL).
79     * @param   \Hoa\Stream\Context  $context       Context.
80     * @return  resource
81     * @throws  \Hoa\File\Exception\FileDoesNotExist
82     * @throws  \Hoa\File\Exception
83     */
84    protected function &_open($streamName, Stream\Context $context = null)
85    {
86        static $createModes = [
87            parent::MODE_READ_WRITE,
88            parent::MODE_TRUNCATE_READ_WRITE,
89            parent::MODE_APPEND_READ_WRITE,
90            parent::MODE_CREATE_READ_WRITE
91        ];
92
93        if (!in_array($this->getMode(), $createModes)) {
94            throw new File\Exception(
95                'Open mode are not supported; given %d. Only %s are supported.',
96                0,
97                [$this->getMode(), implode(', ', $createModes)]
98            );
99        }
100
101        preg_match('#^(\w+)://#', $streamName, $match);
102
103        if (((isset($match[1]) && $match[1] == 'file') || !isset($match[1])) &&
104            !file_exists($streamName) &&
105            parent::MODE_READ_WRITE == $this->getMode()) {
106            throw new File\Exception\FileDoesNotExist(
107                'File %s does not exist.',
108                1,
109                $streamName
110            );
111        }
112
113        $out = parent::_open($streamName, $context);
114
115        return $out;
116    }
117
118    /**
119     * Test for end-of-file.
120     *
121     * @return  bool
122     */
123    public function eof()
124    {
125        return feof($this->getStream());
126    }
127
128    /**
129     * Read n characters.
130     *
131     * @param   int     $length    Length.
132     * @return  string
133     * @throws  \Hoa\File\Exception
134     */
135    public function read($length)
136    {
137        if (0 > $length) {
138            throw new File\Exception(
139                'Length must be greater than 0, given %d.',
140                2,
141                $length
142            );
143        }
144
145        return fread($this->getStream(), $length);
146    }
147
148    /**
149     * Alias of $this->read().
150     *
151     * @param   int     $length    Length.
152     * @return  string
153     */
154    public function readString($length)
155    {
156        return $this->read($length);
157    }
158
159    /**
160     * Read a character.
161     *
162     * @return  string
163     */
164    public function readCharacter()
165    {
166        return fgetc($this->getStream());
167    }
168
169    /**
170     * Read a boolean.
171     *
172     * @return  bool
173     */
174    public function readBoolean()
175    {
176        return (bool) $this->read(1);
177    }
178
179    /**
180     * Read an integer.
181     *
182     * @param   int     $length    Length.
183     * @return  int
184     */
185    public function readInteger($length = 1)
186    {
187        return (int) $this->read($length);
188    }
189
190    /**
191     * Read a float.
192     *
193     * @param   int     $length    Length.
194     * @return  float
195     */
196    public function readFloat($length = 1)
197    {
198        return (float) $this->read($length);
199    }
200
201    /**
202     * Read an array.
203     * Alias of the $this->scanf() method.
204     *
205     * @param   string  $format    Format (see printf's formats).
206     * @return  array
207     */
208    public function readArray($format = null)
209    {
210        return $this->scanf($format);
211    }
212
213    /**
214     * Read a line.
215     *
216     * @return  string
217     */
218    public function readLine()
219    {
220        return fgets($this->getStream());
221    }
222
223    /**
224     * Read all, i.e. read as much as possible.
225     *
226     * @param   int  $offset    Offset.
227     * @return  string
228     */
229    public function readAll($offset = 0)
230    {
231        return stream_get_contents($this->getStream(), -1, $offset);
232    }
233
234    /**
235     * Parse input from a stream according to a format.
236     *
237     * @param   string  $format    Format (see printf's formats).
238     * @return  array
239     */
240    public function scanf($format)
241    {
242        return fscanf($this->getStream(), $format);
243    }
244
245    /**
246     * Write n characters.
247     *
248     * @param   string  $string    String.
249     * @param   int     $length    Length.
250     * @return  mixed
251     * @throws  \Hoa\File\Exception
252     */
253    public function write($string, $length)
254    {
255        if (0 > $length) {
256            throw new File\Exception(
257                'Length must be greater than 0, given %d.',
258                3,
259                $length
260            );
261        }
262
263        return fwrite($this->getStream(), $string, $length);
264    }
265
266    /**
267     * Write a string.
268     *
269     * @param   string  $string    String.
270     * @return  mixed
271     */
272    public function writeString($string)
273    {
274        $string = (string) $string;
275
276        return $this->write($string, strlen($string));
277    }
278
279    /**
280     * Write a character.
281     *
282     * @param   string  $char    Character.
283     * @return  mixed
284     */
285    public function writeCharacter($char)
286    {
287        return $this->write((string) $char[0], 1);
288    }
289
290    /**
291     * Write a boolean.
292     *
293     * @param   bool    $boolean    Boolean.
294     * @return  mixed
295     */
296    public function writeBoolean($boolean)
297    {
298        return $this->write((string) (bool) $boolean, 1);
299    }
300
301    /**
302     * Write an integer.
303     *
304     * @param   int     $integer    Integer.
305     * @return  mixed
306     */
307    public function writeInteger($integer)
308    {
309        $integer = (string) (int) $integer;
310
311        return $this->write($integer, strlen($integer));
312    }
313
314    /**
315     * Write a float.
316     *
317     * @param   float   $float    Float.
318     * @return  mixed
319     */
320    public function writeFloat($float)
321    {
322        $float = (string) (float) $float;
323
324        return $this->write($float, strlen($float));
325    }
326
327    /**
328     * Write an array.
329     *
330     * @param   array   $array    Array.
331     * @return  mixed
332     */
333    public function writeArray(array $array)
334    {
335        $array = var_export($array, true);
336
337        return $this->write($array, strlen($array));
338    }
339
340    /**
341     * Write a line.
342     *
343     * @param   string  $line    Line.
344     * @return  mixed
345     */
346    public function writeLine($line)
347    {
348        if (false === $n = strpos($line, "\n")) {
349            return $this->write($line . "\n", strlen($line) + 1);
350        }
351
352        ++$n;
353
354        return $this->write(substr($line, 0, $n), $n);
355    }
356
357    /**
358     * Write all, i.e. as much as possible.
359     *
360     * @param   string  $string    String.
361     * @return  mixed
362     */
363    public function writeAll($string)
364    {
365        return $this->write($string, strlen($string));
366    }
367
368    /**
369     * Truncate a file to a given length.
370     *
371     * @param   int     $size    Size.
372     * @return  bool
373     */
374    public function truncate($size)
375    {
376        return ftruncate($this->getStream(), $size);
377    }
378}
379