Warning: session_start(): Session cannot be started after headers have already been sent in /home/tvrreohg/public_html/manga.php on line 13
`, `+`, `~`) before being passed to this class. */ class CompoundSelector implements Component { use ShortClassNameProvider; private const PARSER_STOP_CHARACTERS = [ '{', '}', '\'', '"', '(', ')', '[', ']', ',', ' ', "\t", "\n", "\r", '>', '+', '~', ParserState::EOF, '', // `ParserState::peek()` returns empty string rather than `ParserState::EOF` when end of string is reached ]; private const SELECTOR_VALIDATION_RX = '/ ^ # not starting with whitespace (?!\\s) (?: (?: # any sequence of valid unescaped characters, except quotes [a-zA-Z0-9\\x{00A0}-\\x{FFFF}_^$|*=~\\[\\]()\\-\\s\\.:#+>,]++ | # one or more escaped characters (?:\\\\.)++ | # quoted text, like in `[id="example"]` (?: # opening quote ([\'"]) (?: # sequence of characters except closing quote or backslash (?:(?!\\g{-1}|\\\\).)++ | # one or more escaped characters (?:\\\\.)++ )*+ # zero or more times # closing quote or end (unmatched quote is currently allowed) (?:\\g{-1}|$) ) )++ # one or more times | # keyframe animation progress percentage (e.g. 50%) (?:\\d++%) ) # not ending with whitespace (?setValue($value); } /** * @param list $comments * * @throws UnexpectedTokenException * * @internal */ public static function parse(ParserState $parserState, array &$comments = []): self { $selectorParts = []; $stringWrapperCharacter = null; $functionNestingLevel = 0; $isWithinAttribute = false; while (true) { $selectorParts[] = $parserState->consumeUntil(self::PARSER_STOP_CHARACTERS, false, false, $comments); $nextCharacter = $parserState->peek(); switch ($nextCharacter) { case '': // EOF break 2; case '\'': // The fallthrough is intentional. case '"': $lastPart = \end($selectorParts); $backslashCount = \strspn(\strrev($lastPart), '\\'); $quoteIsEscaped = ($backslashCount % 2 === 1); if (!$quoteIsEscaped) { if (!\is_string($stringWrapperCharacter)) { $stringWrapperCharacter = $nextCharacter; } elseif ($stringWrapperCharacter === $nextCharacter) { $stringWrapperCharacter = null; } } break; case '(': if (!\is_string($stringWrapperCharacter)) { ++$functionNestingLevel; } break; case ')': if (!\is_string($stringWrapperCharacter)) { if ($functionNestingLevel <= 0) { throw new UnexpectedTokenException( 'anything but', ')', 'literal', $parserState->currentLine() ); } --$functionNestingLevel; } break; case '[': if (!\is_string($stringWrapperCharacter)) { if ($isWithinAttribute) { throw new UnexpectedTokenException( 'anything but', '[', 'literal', $parserState->currentLine() ); } $isWithinAttribute = true; } break; case ']': if (!\is_string($stringWrapperCharacter)) { if (!$isWithinAttribute) { throw new UnexpectedTokenException( 'anything but', ']', 'literal', $parserState->currentLine() ); } $isWithinAttribute = false; } break; case '{': // The fallthrough is intentional. case '}': if (!\is_string($stringWrapperCharacter)) { break 2; } break; case ',': // The fallthrough is intentional. case ' ': // The fallthrough is intentional. case "\t": // The fallthrough is intentional. case "\n": // The fallthrough is intentional. case "\r": // The fallthrough is intentional. case '>': // The fallthrough is intentional. case '+': // The fallthrough is intentional. case '~': if (!\is_string($stringWrapperCharacter) && $functionNestingLevel === 0 && !$isWithinAttribute) { break 2; } break; } $selectorParts[] = $parserState->consume(1); } if ($functionNestingLevel !== 0) { throw new UnexpectedTokenException(')', $nextCharacter, 'literal', $parserState->currentLine()); } if (\is_string($stringWrapperCharacter)) { throw new UnexpectedTokenException( $stringWrapperCharacter, $nextCharacter, 'literal', $parserState->currentLine() ); } $value = \implode('', $selectorParts); if ($value === '') { throw new UnexpectedTokenException('selector', $nextCharacter, 'literal', $parserState->currentLine()); } if (!self::isValid($value)) { throw new UnexpectedTokenException( 'Selector component is not valid:', '`' . $value . '`', 'custom', $parserState->currentLine() ); } return new self($value); } /** * @return non-empty-string */ public function getValue(): string { return $this->value; } /** * @param non-empty-string $value * * @throws \UnexpectedValueException if `$value` contains invalid characters or has surrounding whitespce */ public function setValue(string $value): void { if (!self::isValid($value)) { throw new \UnexpectedValueException('`' . $value . '` is not a valid compound selector.'); } $this->value = $value; } /** * @return int<0, max> */ public function getSpecificity(): int { return SpecificityCalculator::calculate($this->value); } public function render(OutputFormat $outputFormat): string { return $this->getValue(); } /** * @return array|null> * * @internal */ public function getArrayRepresentation(): array { return [ 'class' => $this->getShortClassName(), 'value' => $this->value, ]; } private static function isValid(string $value): bool { $numberOfMatches = preg_match(self::SELECTOR_VALIDATION_RX, $value); return $numberOfMatches === 1; } }