Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
StringDataSource
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
3 / 3
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 readCharacter
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
7
 putBackCharacter
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * @file
4 * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0
5 */
6
7namespace Wikimedia\CSS\Parser;
8
9use InvalidArgumentException;
10use UnexpectedValueException;
11
12/**
13 * Read data for the CSS parser
14 */
15class StringDataSource implements DataSource {
16
17    /** @var string */
18    protected $string;
19
20    /** @var int */
21    protected $len = 0;
22
23    /** @var int */
24    protected $pos = 0;
25
26    /** @var string[] */
27    protected $putBack = [];
28
29    /**
30     * @param string $string Input string. Must be valid UTF-8 with no BOM.
31     */
32    public function __construct( $string ) {
33        $this->string = (string)$string;
34        $this->len = strlen( $this->string );
35
36        if ( !mb_check_encoding( $this->string, 'UTF-8' ) ) {
37            throw new InvalidArgumentException( '$string is not valid UTF-8' );
38        }
39    }
40
41    /** @inheritDoc */
42    public function readCharacter() {
43        if ( $this->putBack ) {
44            return array_pop( $this->putBack );
45        }
46
47        if ( $this->pos >= $this->len ) {
48            return self::EOF;
49        }
50
51        // We already checked that the string is valid UTF-8 in the
52        // constructor, so we can do a quick binary "get next character" here.
53        $p = $this->pos;
54        $c = $this->string[$p];
55        $cc = ord( $this->string[$p] );
56        if ( $cc <= 0x7f ) {
57            $this->pos++;
58            return $c;
59        } elseif ( ( $cc & 0xe0 ) === 0xc0 ) {
60            $this->pos += 2;
61            return substr( $this->string, $p, 2 );
62        } elseif ( ( $cc & 0xf0 ) === 0xe0 ) {
63            $this->pos += 3;
64            return substr( $this->string, $p, 3 );
65        } elseif ( ( $cc & 0xf8 ) === 0xf0 ) {
66            $this->pos += 4;
67            return substr( $this->string, $p, 4 );
68        } else {
69            // WTF? Should never get here because it should have failed
70            // validation in the constructor.
71            // @codeCoverageIgnoreStart
72            throw new UnexpectedValueException(
73                sprintf( 'Unexpected byte %02X in string at position %d.', $cc, $this->pos )
74            );
75            // @codeCoverageIgnoreEnd
76        }
77    }
78
79    /** @inheritDoc */
80    public function putBackCharacter( $char ) {
81        if ( $char !== self::EOF ) {
82            $this->putBack[] = $char;
83        }
84    }
85}