Completed
Push — master ( 2e5bdd...9e2471 )
by Ryuichi
05:26
created

FileInputStream::read()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 0
cts 23
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
crap 56
eloc 15
nc 7
nop 1
1
<?php
2
namespace WebStream\IO;
3
4
use WebStream\Exception\Extend\InvalidArgumentException;
5
use WebStream\Exception\Extend\IOException;
6
7
/**
8
 * FileInputStream
9
 * @author Ryuichi TANAKA.
10
 * @since 2016/02/05
11
 * @version 0.7
12
 */
13
class FileInputStream extends InputStream
14
{
15
    /**
16
     * @var File ファイルオブジェクト
17
     */
18
    protected $file;
19
20
    /**
21
     * constructor
22
     * @param mixed $file ファイルオブジェクトまたはファイルパス
23
     * @throws InvalidArgumentException
24
     * @throws IOException
25
     */
26
    public function __construct($file)
27
    {
28
        if ($file instanceof File) {
29
            $this->file = $file;
30
        } elseif (is_string($file)) {
31
            $this->file = new File($file);
32
        } else {
33
            throw new InvalidArgumentException("Unable to open file: " . $file);
34
        }
35
36
        // 読み込みはロックを掛けずダーティーリード
37
        $stream = fopen($this->file->getAbsoluteFilePath(), 'r');
38
        if (!is_resource($stream) || $stream === false) {
39
            throw new IOException("Unable open " . $this->file->getAbsoluteFilePath());
40
        }
41
42
        parent::__construct($stream);
43
    }
44
45
    /**
46
     * 入力ストリームを閉じる
47
     */
48
    public function close()
49
    {
50
        if ($this->stream === null) {
51
            return;
52
        }
53
54
        if (get_resource_type($this->stream) !== 'Unknown' && fclose($this->stream) === false) {
55
            throw new IOException("Cannot close input stream.");
56
        }
57
58
        $this->stream = null;
59
    }
60
61
    /**
62
     * {@inheritdoc}
63
     */
64
    public function read($length = null)
65
    {
66
        if ($this->stream === null) {
67
            return null;
68
        }
69
70
        if ($this->eof()) {
71
            return null;
72
        }
73
74
        $out = null;
75
        if ($length === null) {
76
            if (($out = @fread($this->stream, 1)) === false) {
77
                throw new IOException("Failed to read stream.");
78
            }
79
        } else {
80
            if (!is_int($length)) {
81
                throw new InvalidArgumentException("Stream read must be a numeric value.");
82
            }
83
            // ポインタ位置が負になった場合、警告が出てfalseを返す
84
            // ポインタの終端を越えた場合、読み込みを終了する
85
            // すでに終端位置の場合、空文字を返す
86
            if (($out = @fread($this->stream, $length)) === false) {
87
                throw new IOException("Failed to read stream.");
88
            }
89
        }
90
91
        return $out;
92
    }
93
94
    /**
95
     * 入力ストリームから行単位でデータを読み込む
96
     * 末尾に改行コードは含まない
97
     * @return string 読み込みデータ
98
     */
99
    public function readLine()
100
    {
101
        if ($this->stream === null) {
102
            return null;
103
        }
104
105
        if ($this->eof()) {
106
            return null;
107
        }
108
109
        $out = fgets($this->stream);
110
        if ($out === false) {
111
            return null;
112
        }
113
114
        $this->cursorPosition = ftell($this->stream);
115
116
        return trim($out);
117
    }
118
119
    /**
120
     * {@inheritdoc}
121
     */
122
    public function skip(int $pos)
123
    {
124
        if ($this->stream === null) {
125
            return -1;
126
        }
127
128
        // 現在のポインタ位置から$posだけ後方へ移動
129
        // シークに対応していないファイルシステムの場合、-1を返す
130
        if (fseek($this->stream, $pos, SEEK_CUR) === -1) {
131
            return -1;
132
        }
133
134
        $start = $this->cursorPosition;
135
        $this->cursorPosition = ftell($this->stream);
136
137
        $skipNum = 0;
138
        if ($start > $this->cursorPosition) {
139
            // 後方へ移動
140
            $skipNum = $start - $this->cursorPosition;
141
        } else {
142
            // 前方へ移動
143
            $skipNum = $this->cursorPosition - $start;
144
        }
145
146
        return $skipNum;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function reset()
153
    {
154
        if (!$this->isMarkSupported()) {
155
            throw new IOException(get_class($this) . " does not support mark and reset.");
156
        }
157
158
        if ($this->stream === null) {
159
            return;
160
        }
161
162
        // ポインタ位置をmark位置に移動
163
        fseek($this->stream, $this->markedPosition, SEEK_SET);
164
        // mark位置を初期値に戻す
165
        $this->cursorPosition = $this->markedPosition;
166
        $this->markedPosition = 0;
167
    }
168
169
    /**
170
     * {@inheritdoc}
171
     */
172
    public function eof()
173
    {
174
        return feof($this->stream);
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180
    public function isMarkSupported()
181
    {
182
        return true;
183
    }
184
}
185