Completed
Pull Request — master (#8)
by Harry
28:53
created

State::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 6
ccs 5
cts 5
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 2
crap 1
1
<?php
2
/**
3
 * This file is part of graze/csv-token
4
 *
5
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license https://github.com/graze/csv-token/blob/master/LICENSE.md
11
 * @link    https://github.com/graze/csv-token
12
 */
13
14
namespace Graze\CsvToken\Tokeniser;
15
16
use Graze\CsvToken\Buffer\BufferInterface;
17
use Graze\CsvToken\Tokeniser\Token\Token;
18
use Graze\CsvToken\Tokeniser\Token\TokenStoreInterface;
19
use RuntimeException;
20
21
class State
22
{
23
    const S_ANY             = 0;
24
    const S_IN_QUOTE        = 1;
25
    const S_IN_ESCAPE       = 2;
26
    const S_IN_QUOTE_ESCAPE = 4;
27
    const S_INITIAL         = 5;
28
29
    const S_INITIAL_TOKENS         = Token::T_ANY & ~Token::T_DOUBLE_QUOTE;
30
    const S_ANY_TOKENS             = Token::T_ANY & ~Token::T_DOUBLE_QUOTE & ~Token::T_BOM;
31
    const S_IN_QUOTE_TOKENS        = Token::T_CONTENT | Token::T_QUOTE | Token::T_DOUBLE_QUOTE | Token::T_ESCAPE;
32
    const S_IN_ESCAPE_TOKENS       = Token::T_CONTENT;
33
    const S_IN_QUOTE_ESCAPE_TOKENS = Token::T_CONTENT;
34
35
    /** @var State[] */
36
    private $states;
37
    /** @var TokenStoreInterface */
38
    private $tokenStore;
39
    /** @var int */
40
    private $tokenMask;
41
    /** @var int[] */
42
    private $tokens;
43
    /** @var string[] */
44
    private $keys;
45
    /** @var int[] */
46
    private $keyLengths;
47
    /** @var int */
48
    private $maxLen;
49
50
    /**
51
     * TokenStoreInterface is passed in here, as the tokens can be modified by the store
52
     *
53
     * @param TokenStoreInterface $tokens
54
     * @param int                 $tokenMask
55
     */
56 32
    public function __construct(TokenStoreInterface $tokens, $tokenMask = Token::T_ANY)
57
    {
58 32
        $this->tokenStore = $tokens;
59 32
        $this->tokenMask = $tokenMask;
60 32
        $this->parseTokens();
61 32
    }
62
63
    /**
64
     * Parse the current set ok tokens and cache some metadata about them for speed
65
     */
66 32
    private function parseTokens()
67
    {
68 32
        $this->tokens = $this->tokenStore->getTokens($this->tokenMask);
69 32
        $this->keys = array_keys($this->tokens);
1 ignored issue
show
Documentation Bug introduced by
It seems like array_keys($this->tokens) of type array<integer,integer> is incompatible with the declared type array<integer,string> of property $keys.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
70 32
        $this->keyLengths = array_unique(array_map('strlen', $this->keys));
71 32
        arsort($this->keyLengths);
72 32
        $this->maxLen = reset($this->keyLengths);
73 32
    }
74
75
    /**
76
     * @param int $token
77
     *
78
     * @return State|null
79
     */
80 29
    public function getNextState($token)
81
    {
82 29
        foreach ($this->states as $mask => $state) {
83 29
            if ($mask & $token) {
84 29
                return $state;
85
            }
86 23
        }
87
88 1
        throw new RuntimeException("The supplied token: {$token} has no target state");
89
    }
90
91
    /**
92
     * @param int   $tokenMask
93
     * @param State $target
94
     */
95 32
    public function addStateTarget($tokenMask, State $target)
96
    {
97 32
        $this->states[$tokenMask] = $target;
98 32
    }
99
100
    /**
101
     * @param BufferInterface $buffer
102
     *
103
     * @return array
104
     */
105 28
    public function match(BufferInterface $buffer)
106
    {
107 28
        if ($this->tokenStore->hasChanged($this->tokenMask)) {
108 2
            $this->parseTokens();
109 2
        }
110
111 28
        $contents = $buffer->getContents();
112 28
        $length = $buffer->getLength();
113 28
        $position = $buffer->getPosition();
114 28
        if (count($this->tokens) > 0) {
115 28
            $totalLen = max($length - $this->maxLen, 1);
116 28
            for ($i = 0; $i < $totalLen; $i++) {
117 28
                foreach ($this->keyLengths as $len) {
118 28
                    $buf = substr($contents, $i, $len);
119 28
                    if (isset($this->tokens[$buf])) {
120 27
                        if ($i > 0) {
121
                            return [
122 26
                                [Token::T_CONTENT, substr($contents, 0, $i), $position, $i],
123 26
                                [$this->tokens[$buf], $buf, $position + $i, $len],
124 26
                            ];
125
                        } else {
126 21
                            return [[$this->tokens[$buf], $buf, $position, $len]];
127
                        }
128
                    }
129 28
                }
130 28
                if ($totalLen !== $length && $i == $totalLen - 1) {
131 19
                    $buffer->read();
132 19
                    $totalLen = $length = $buffer->getLength();
133 19
                    $contents = $buffer->getContents();
134 19
                }
135 28
            }
136 8
        }
137
138 16
        return [[Token::T_CONTENT, $contents[0], $buffer->getPosition(), 1]];
139
    }
140
}
141