Passed
Push — master ( fa11d8...f38bfb )
by Walter
02:27
created

Splitter::parseMoves()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 3
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * pgn-splitter (https://github.com/chesszebra/pgn-splitter)
4
 *
5
 * @link https://github.com/chesszebra/pgn-splitter for the canonical source repository
6
 * @copyright Copyright (c) 2017 Chess Zebra (https://chesszebra.com)
7
 * @license https://github.com/chesszebra/pgn-splitter/blob/master/LICENSE MIT
8
 */
9
10
namespace ChessZebra\Chess\Pgn;
11
12
use ChessZebra\Chess\Pgn\Exception\InvalidStreamException;
13
14
final class Splitter
15
{
16
    const SPLIT_GAMES = 0;
17
    const SPLIT_CHUNKS = 1;
18
19
    const STATE_LIMBO = 0;
20
    const STATE_TAGS = 1;
21
    const STATE_MOVES = 2;
22
23
    /**
24
     * The stream to split.
25
     *
26
     * @var resource
27
     */
28
    private $stream;
29
30
    /**
31
     * The mode to split the stream on.
32
     *
33
     * @var int
34
     */
35
    private $mode;
36
37
    /**
38
     * The current state of the splitter.
39
     *
40
     * @var int
41
     */
42
    private $state;
43
44
    /**
45
     * The parsed buffer.
46
     *
47
     * @var string
48
     */
49
    private $buffer;
50
51
    /**
52
     * Initializes a new instance of this class.
53
     *
54
     * @param resource $stream The stream to split.
55
     * @param int $mode The mode to split on.
56
     * @throws InvalidStreamException Thrown when an invalid stream is provided.
57
     */
58
    public function __construct($stream, int $mode = self::SPLIT_GAMES)
59
    {
60
        if (!is_resource($stream)) {
61
            throw new InvalidStreamException('The provided stream is not a valid resource.');
62
        }
63
64
        $this->stream = $stream;
65
        $this->mode = $mode;
66
    }
67
68
    /**
69
     * Splits the stream into loose chunks and provides them to the given callback.
70
     *
71
     * @param callable $callback The callback that is called for each found chunk.
72
     * @return int Returns the amount of chunks found in the stream.
73
     */
74
    public function split(callable $callback): int
75
    {
76
        $result = 0;
77
78
        $this->buffer = '';
79
        $this->state = self::STATE_LIMBO;
80
81
        while (!feof($this->stream)) {
82
            $line = fgets($this->stream);
83
            $line = str_replace("\r\n", "\n", $line); // Replace Windows line endings with unix
84
            $line = str_replace("\r", "\n", $line); // Replace mac line endings with unix
85
86
            if (!$line) {
87
                continue;
88
            }
89
90
            switch ($this->state) {
91
                case self::STATE_LIMBO:
92
                    $this->parseLimbo($line);
93
                    break;
94
95
                case self::STATE_TAGS:
96
                    $this->parseTags($callback, $result, $line);
97
                    break;
98
99
                case self::STATE_MOVES:
100
                    $this->parseMoves($callback, $result, $line);
101
                    break;
102
103
                default:
104
                    throw new \RuntimeException(sprintf('The state "%d" is not implemented.', $this->state));
105
            }
106
        }
107
108
        if (trim($this->buffer) !== '') {
109
            $result++;
110
            $callback($this->buffer);
111
            $this->buffer = '';
112
        }
113
114
        return $result;
115
    }
116
117
    private function parseLimbo(string $line)
118
    {
119
        if (trim($line) === '') {
120
            return;
121
        }
122
123
        if ($line[0] === '[') {
124
            $this->buffer .= $line;
125
            $this->state = self::STATE_TAGS;
126
        } else {
127
            $this->buffer .= $line;
128
            $this->state = self::STATE_MOVES;
129
        }
130
    }
131
132
    private function parseTags(callable $callback, int &$result, string $line)
133
    {
134
        if ($line[0] === '[') {
135
            $this->buffer .= $line;
136
            return;
137
        }
138
139
        if ($this->mode === self::SPLIT_CHUNKS) {
140
            $result++;
141
142
            $callback($this->buffer);
143
144
            $this->buffer = '';
145
            return;
146
        }
147
148
        $this->buffer .= $line;
149
150
        $this->state = self::STATE_MOVES;
151
    }
152
153
    private function parseMoves(callable $callback, int &$result, string $line)
154
    {
155
        if ($line === "\n") {
156
            $this->state = self::STATE_LIMBO;
157
158
            $result++;
159
160
            $callback($this->buffer);
161
162
            $this->buffer = '';
163
        }
164
165
        $this->buffer .= $line;
166
    }
167
}
168