Passed
Push — master ( 668c77...c11634 )
by Gilles
02:19
created

Content::skip()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4.0961

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 18
ccs 9
cts 11
cp 0.8182
crap 4.0961
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PHPHtmlParser;
6
7
use PHPHtmlParser\Enum\StringToken;
8
use PHPHtmlParser\Exceptions\ContentLengthException;
9
use PHPHtmlParser\Exceptions\LogicalException;
10
11
/**
12
 * Class Content.
13
 */
14
class Content
15
{
16
    /**
17
     * The content string.
18
     *
19
     * @var string
20
     */
21
    protected $content;
22
23
    /**
24
     * The size of the content.
25
     *
26
     * @var int
27
     */
28
    protected $size;
29
30
    /**
31
     * The current position we are in the content.
32
     *
33
     * @var int
34
     */
35
    protected $pos;
36
37
    /**
38
     * The following 4 strings are tags that are important to us.
39
     *
40
     * @var string
41
     */
42
    protected $blank = " \t\r\n";
43
    protected $equal = ' =/>';
44
    protected $slash = " />\r\n\t";
45
    protected $attr = ' >';
46
47
    /**
48
     * Content constructor.
49
     */
50 327
    public function __construct(string $content = '')
51
    {
52 327
        $this->content = $content;
53 327
        $this->size = \strlen($content);
54 327
        $this->pos = 0;
55 327
    }
56
57
    /**
58
     * Returns the current position of the content.
59
     */
60 75
    public function getPosition(): int
61
    {
62 75
        return $this->pos;
63
    }
64
65
    /**
66
     * Gets the current character we are at.
67
     *
68
     * @param ?int $char
69
     */
70 312
    public function char(?int $char = null): string
71
    {
72 312
        return $this->content[$char ?? $this->pos] ?? '';
73
    }
74
75
    /**
76
     * Moves the current position forward.
77
     *
78
     * @throws ContentLengthException
79
     */
80 297
    public function fastForward(int $count): Content
81
    {
82 297
        if (!$this->canFastForward($count)) {
83
            // trying to go over the content length, throw exception
84 3
            throw new ContentLengthException('Attempt to fastForward pass the length of the content.');
85
        }
86 297
        $this->pos += $count;
87
88 297
        return $this;
89
    }
90
91
    /**
92
     * Checks if we can move the position forward.
93
     */
94 297
    public function canFastForward(int $count): bool
95
    {
96 297
        return \strlen($this->content) >= $this->pos + $count;
97
    }
98
99
    /**
100
     * Moves the current position backward.
101
     */
102 27
    public function rewind(int $count): Content
103
    {
104 27
        $this->pos -= $count;
105 27
        if ($this->pos < 0) {
106 3
            $this->pos = 0;
107
        }
108
109 27
        return $this;
110
    }
111
112
    /**
113
     * Copy the content until we find the given string.
114
     */
115 303
    public function copyUntil(string $string, bool $char = false, bool $escape = false): string
116
    {
117 303
        if ($this->pos >= $this->size) {
118
            // nothing left
119 282
            return '';
120
        }
121
122 303
        if ($escape) {
123 258
            $position = $this->pos;
124 258
            $found = false;
125 258
            while (!$found) {
126 258
                $position = \strpos($this->content, $string, $position);
127 258
                if ($position === false) {
128
                    // reached the end
129 237
                    break;
130
                }
131
132 234
                if ($this->char($position - 1) == '\\') {
133
                    // this character is escaped
134 3
                    ++$position;
135 3
                    continue;
136
                }
137
138 234
                $found = true;
139
            }
140 300
        } elseif ($char) {
141 288
            $position = \strcspn($this->content, $string, $this->pos);
142 288
            $position += $this->pos;
143
        } else {
144 294
            $position = \strpos($this->content, $string, $this->pos);
145
        }
146
147 303
        if ($position === false) {
0 ignored issues
show
introduced by
The condition $position === false is always false.
Loading history...
148
            // could not find character, just return the remaining of the content
149 246
            $return = \substr($this->content, $this->pos, $this->size - $this->pos);
150 246
            if ($return === false) {
151
                throw new LogicalException('Substr returned false with position ' . $this->pos . '.');
152
            }
153 246
            $this->pos = $this->size;
154
155 246
            return $return;
156
        }
157
158 294
        if ($position == $this->pos) {
159
            // we are at the right place
160 282
            return '';
161
        }
162
163 294
        $return = \substr($this->content, $this->pos, $position - $this->pos);
164 294
        if ($return === false) {
165
            throw new LogicalException('Substr returned false with position ' . $this->pos . '.');
166
        }
167
        // set the new position
168 294
        $this->pos = $position;
169
170 294
        return $return;
171
    }
172
173
    /**
174
     * Copies the content until the string is found and return it
175
     * unless the 'unless' is found in the substring.
176
     */
177 255
    public function copyUntilUnless(string $string, string $unless): string
178
    {
179 255
        $lastPos = $this->pos;
180 255
        $this->fastForward(1);
181 255
        $foundString = $this->copyUntil($string, true, true);
182
183 255
        $position = \strcspn($foundString, $unless);
184 255
        if ($position == \strlen($foundString)) {
185 69
            return $string . $foundString;
186
        }
187
        // rewind changes and return nothing
188 255
        $this->pos = $lastPos;
189
190 255
        return '';
191
    }
192
193
    /**
194
     * Copies the content until it reaches the token string.,.
195
     *
196
     * @uses $this->copyUntil()
197
     */
198 285
    public function copyByToken(StringToken $stringToken, bool $char = false, bool $escape = false): string
199
    {
200 285
        $string = $stringToken->getValue();
201
202 285
        return $this->copyUntil($string, $char, $escape);
203
    }
204
205
    /**
206
     * Skip a given set of characters.
207
     *
208
     * @throws LogicalException
209
     */
210 291
    public function skip(string $string, bool $copy = false): string
211
    {
212 291
        $len = \strspn($this->content, $string, $this->pos);
213 291
        if ($len === false) {
214
            throw new LogicalException('Strspn returned false with position ' . $this->pos . '.');
215
        }
216 291
        $return = '';
217 291
        if ($copy) {
218 267
            $return = \substr($this->content, $this->pos, $len);
219 267
            if ($return === false) {
220
                throw new LogicalException('Substr returned false with position ' . $this->pos . '.');
221
            }
222
        }
223
224
        // update the position
225 291
        $this->pos += $len;
226
227 291
        return $return;
228
    }
229
230
    /**
231
     * Skip a given token of pre-defined characters.
232
     *
233
     * @uses $this->skip()
234
     */
235 285
    public function skipByToken(StringToken $skipToken, bool $copy = false): string
236
    {
237 285
        $string = $skipToken->getValue();
238
239 285
        return $this->skip($string, $copy);
240
    }
241
}
242