friendship ended with social-app. php is my new best friend
1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\CssSelector\Parser; 13 14use Symfony\Component\CssSelector\Exception\InternalErrorException; 15use Symfony\Component\CssSelector\Exception\SyntaxErrorException; 16 17/** 18 * CSS selector token stream. 19 * 20 * This component is a port of the Python cssselect library, 21 * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect. 22 * 23 * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> 24 * 25 * @internal 26 */ 27class TokenStream 28{ 29 /** 30 * @var Token[] 31 */ 32 private array $tokens = []; 33 34 /** 35 * @var Token[] 36 */ 37 private array $used = []; 38 39 private int $cursor = 0; 40 private ?Token $peeked; 41 private bool $peeking = false; 42 43 /** 44 * Pushes a token. 45 * 46 * @return $this 47 */ 48 public function push(Token $token): static 49 { 50 $this->tokens[] = $token; 51 52 return $this; 53 } 54 55 /** 56 * Freezes stream. 57 * 58 * @return $this 59 */ 60 public function freeze(): static 61 { 62 return $this; 63 } 64 65 /** 66 * Returns next token. 67 * 68 * @throws InternalErrorException If there is no more token 69 */ 70 public function getNext(): Token 71 { 72 if ($this->peeking) { 73 $this->peeking = false; 74 $this->used[] = $this->peeked; 75 76 return $this->peeked; 77 } 78 79 if (!isset($this->tokens[$this->cursor])) { 80 throw new InternalErrorException('Unexpected token stream end.'); 81 } 82 83 return $this->tokens[$this->cursor++]; 84 } 85 86 /** 87 * Returns peeked token. 88 */ 89 public function getPeek(): Token 90 { 91 if (!$this->peeking) { 92 $this->peeked = $this->getNext(); 93 $this->peeking = true; 94 } 95 96 return $this->peeked; 97 } 98 99 /** 100 * Returns used tokens. 101 * 102 * @return Token[] 103 */ 104 public function getUsed(): array 105 { 106 return $this->used; 107 } 108 109 /** 110 * Returns next identifier token. 111 * 112 * @throws SyntaxErrorException If next token is not an identifier 113 */ 114 public function getNextIdentifier(): string 115 { 116 $next = $this->getNext(); 117 118 if (!$next->isIdentifier()) { 119 throw SyntaxErrorException::unexpectedToken('identifier', $next); 120 } 121 122 return $next->getValue(); 123 } 124 125 /** 126 * Returns next identifier or null if star delimiter token is found. 127 * 128 * @throws SyntaxErrorException If next token is not an identifier or a star delimiter 129 */ 130 public function getNextIdentifierOrStar(): ?string 131 { 132 $next = $this->getNext(); 133 134 if ($next->isIdentifier()) { 135 return $next->getValue(); 136 } 137 138 if ($next->isDelimiter(['*'])) { 139 return null; 140 } 141 142 throw SyntaxErrorException::unexpectedToken('identifier or "*"', $next); 143 } 144 145 /** 146 * Skips next whitespace if any. 147 */ 148 public function skipWhitespace(): void 149 { 150 $peek = $this->getPeek(); 151 152 if ($peek->isWhitespace()) { 153 $this->getNext(); 154 } 155 } 156}