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

StringInputStream::skip()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 0
cts 19
cp 0
rs 8.5806
c 0
b 0
f 0
cc 4
crap 20
eloc 14
nc 4
nop 1
1
<?php
2
namespace WebStream\IO;
3
4
use WebStream\Exception\Extend\InvalidArgumentException;
5
use WebStream\Exception\Extend\IOException;
6
7
/**
8
 * StringInputStream
9
 * @author Ryuichi TANAKA.
10
 * @since 2016/02/07
11
 * @version 0.7
12
 */
13
class StringInputStream extends InputStream
14
{
15
    /**
16
     * @var int 文字列長
17
     */
18
    private $length;
19
20
    /**
21
     * @var bool 終端かどうか
22
     */
23
    private $isEOF = false;
24
25
    /**
26
     * construct
27
     * @param string $str 文字列
28
     */
29
    public function __construct($str)
30
    {
31
        parent::__construct($str);
32
        $this->length = strlen($str);
33
    }
34
35
    /**
36
     * 入力ストリームを閉じる
37
     */
38
    public function close()
39
    {
40
        $this->stream = null;
41
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46
    public function read($length = null)
47
    {
48
        if ($this->stream === null) {
49
            return null;
50
        }
51
52
        if ($this->eof()) {
53
            return null;
54
        }
55
56
        // SkipでポインタをずらしただけではEOFにはならない、FileInputStream実装に合わせる
57
        // Skipでずらしたあとreadすると空文字を返し、もう一度readするとEOFを認識する
58
        // ファイルの終端より先にポインタをすすめることは「可能」=書き込みと同じ
59
        // なので、現在の終端位置より先に進めてもEOF自体にはならない。進めた位置はEOFのひとつ前
60
        // だからもう一回readするとEOFに到達する。なのでskipを使ってもEOF到達できない
61
        if ($this->cursorPosition > $this->length - 1) {
62
            $this->isEOF = true;
63
64
            return "";
65
        }
66
67
        $out = "";
68
        if ($length === null) {
69
            $length = 1;
70
            $out = substr($this->stream, $this->cursorPosition, 1);
71
            $this->cursorPosition += 1;
72
        } else {
73
            if (!is_int($length)) {
74
                throw new InvalidArgumentException("Stream read must be a numeric value.");
75
            }
76
77
            // $lengthがファイル終端を越えないようにする
78
            if (($this->cursorPosition + $length) > $this->length) {
79
                $length = $this->length - $this->cursorPosition;
80
            }
81
82
            $out = substr($this->stream, $this->cursorPosition, $length);
83
            $this->cursorPosition += $length;
84
        }
85
86
        return $out;
87
    }
88
89
    /**
90
     * 入力ストリームから行単位でデータを読み込む
91
     * 末尾に改行コードは含まない
92
     * @return string 読み込みデータ
93
     */
94
    public function readLine()
95
    {
96
        if ($this->stream === null) {
97
            return null;
98
        }
99
100
        if ($this->eof()) {
101
            return null;
102
        }
103
104
        // 処理対象の残りのバイト数
105
        $targetLength = $this->length - $this->cursorPosition;
106
107
        // 処理対象の文字列
108
        $text = substr($this->stream, $this->cursorPosition, $targetLength);
109
        $lengthEOL = strlen(PHP_EOL);
110
        $notLinePart = strstr($text, PHP_EOL);
111
112
        $notLinePartLength = 0;
113
        if ($notLinePart !== false) {
114
            $notLinePartLength = strlen($notLinePart);
115
        }
116
117
        $offset = $targetLength - $notLinePartLength;
118
        $out = substr($text, 0, $offset);
119
        $out = $out === false ? null : $out;
120
        $this->skip($offset + $lengthEOL);
121
122
        return $out;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function skip(int $pos)
129
    {
130
        if ($this->stream === null) {
131
            return -1;
132
        }
133
134
        // ファイル終端到達後、skipを実行すると後方にポインタが移動する
135
        // このときEOFだったものがEOFでなくなる
136
        $start = $this->cursorPosition;
137
138
        // 現在位置が負になった場合は-1を返して終了
139
        if ($this->cursorPosition + $pos < 0) {
140
            return -1;
141
        }
142
143
        $this->cursorPosition += $pos;
144
        $this->isEOF = false;
145
146
        // skipした実際のバイト数
147
        $skipNum = 0;
148
        if ($start > $this->cursorPosition) {
149
            // 後方へ移動
150
            $skipNum = $start - $this->cursorPosition;
151
        } else {
152
            // 前方へ移動
153
            $skipNum = $this->cursorPosition - $start;
154
        }
155
156
        return $skipNum;
157
    }
158
159
    /**
160
     * {@inheritdoc}
161
     */
162
    public function eof()
163
    {
164
        return $this->isEOF;
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170
    public function isMarkSupported()
171
    {
172
        return true;
173
    }
174
}
175