diff --git a/src/Main/Markup/Html/Cdata.php b/src/Main/Markup/Html/Cdata.php index eca4c79e43..f83d5a18ca 100644 --- a/src/Main/Markup/Html/Cdata.php +++ b/src/Main/Markup/Html/Cdata.php @@ -11,64 +11,71 @@ namespace OnPHP\Main\Markup\Html; -use OnPHP\Core\Base\Assert; - /** * @ingroup Html * @ingroup Module **/ final class Cdata extends SgmlToken { - private $data = null; - - private $strict = false; + const CDATA_STRICT_START = ''; /** - * @return Cdata - **/ - public static function create() - { - return new self; - } + * @var string|null + */ + private ?string $data = null; + /** + * @var bool + */ + private bool $strict = false; /** - * @return Cdata - **/ - public function setData($data) + * @param string $data + * @return static + */ + public function setData(string $data): Cdata { $this->data = $data; return $this; } - public function getData() + /** + * @return string|null + */ + public function getData(): ?string { - if ($this->strict) - return 'data.']]>'; - else + if ($this->strict) { + return self::CDATA_STRICT_START . $this->data . self::CDATA_STRICT_END; + } else { return $this->data; + } } - public function getRawData() + /** + * @return string|null + */ + public function getRawData(): ?string { return $this->data; } /** - * @return Cdata - **/ - public function setStrict($isStrict) + * @param bool $isStrict + * @return static + */ + public function setStrict(bool $isStrict): Cdata { - Assert::isBoolean($isStrict); - $this->strict = $isStrict; return $this; } - public function isStrict() + /** + * @return bool + */ + public function isStrict(): bool { return $this->strict; } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/src/Main/Markup/Html/HtmlAssembler.php b/src/Main/Markup/Html/HtmlAssembler.php index a6a0c45217..aeb5e3d5af 100644 --- a/src/Main/Markup/Html/HtmlAssembler.php +++ b/src/Main/Markup/Html/HtmlAssembler.php @@ -11,6 +11,9 @@ namespace OnPHP\Main\Markup\Html; +use DOMCharacterData; +use DOMElement; +use DOMNode; use OnPHP\Core\Base\Assert; use OnPHP\Core\Exception\WrongArgumentException; use OnPHP\Core\Exception\UnimplementedFeatureException; @@ -20,90 +23,104 @@ **/ final class HtmlAssembler { - private $tags = null; - - public function __construct($tags) + /** + * @var SgmlToken[] + */ + private array $tags = []; + + /** + * @param SgmlToken[] $tags + * @throws WrongArgumentException + */ + public function __construct(array $tags) { - Assert::isTrue(current($tags) instanceof SgmlToken); + array_map(function ($item) { + Assert::isInstance($item, SgmlToken::class); + }, $tags ?? []); $this->tags = $tags; } + /** + * @param SgmlToken $tag + * @return string|null + * @throws WrongArgumentException + */ public static function makeTag(SgmlToken $tag) { - if ($tag instanceof Cdata) - $result = $tag->getData(); - elseif ($tag instanceof SgmlIgnoredTag) { + if ($tag instanceof Cdata) { + return $tag->getData(); + } elseif ($tag instanceof SgmlIgnoredTag) { Assert::isNotNull($tag->getId()); - $result = '<'.$tag->getId() - .$tag->getCdata()->getData() + return '<'.$tag->getId() + .($tag->getCdata() instanceof Cdata ? $tag->getCdata()->getData() : '') .$tag->getEndMark().'>'; - } elseif ($tag instanceof SgmlOpenTag) { Assert::isNotNull($tag->getId()); $attributes = self::getAttributes($tag); - $result = '<'.$tag->getId() - .($attributes ? ' '.$attributes : null) - .($tag->isEmpty() ? '/' : null).'>'; - + return '<'.$tag->getId() + .($attributes ? ' '.$attributes : '') + .($tag->isEmpty() ? '/' : '').'>'; } elseif ($tag instanceof SgmlEndTag) { - $result = 'getId().'>'; + Assert::isNotNull($tag->getId()); - } else - throw new WrongArgumentException( - "don't know how to assemble tag class '" - .get_class($tag)."'" - ); + return 'getId().'>'; + } - return $result; + throw new WrongArgumentException( + "don't know how to assemble tag class '" + .get_class($tag)."'" + ); } - public static function makeDomNode(\DOMNode $node) + /** + * @param DOMNode $node + * @return string|null + * @throws UnimplementedFeatureException + */ + public static function makeDomNode(DOMNode $node): ?string { - $result = null; - - if ($node instanceof \DOMElement) { - - $result = '<'.$node->nodeName; + if ($node instanceof DOMElement) { + $result = '<' . $node->nodeName; + $attributes = self::getDomAttributes($node); - $attributes = self::getDomAttributes($node); - - if ($attributes) - $result .= ' '.$attributes; - - if (!$node->firstChild) { - $result .= ' />'; - } else { - $result .= '>'; - } - - $childNode = $node->firstChild; - - while ($childNode) { - $result .= self::makeDomNode($childNode); - $childNode = $childNode->nextSibling; - } - - if ($node->firstChild) - $result .= 'nodeName.'>'; - - } elseif ($node instanceof \DOMCharacterData) { - - $result = $node->data; + if ($attributes) { + $result .= ' ' . $attributes; + } + if (null === $node->firstChild) { + $result .= ' />'; } else { - throw new UnimplementedFeatureException( - 'assembling of '.get_class($node).' is not implemented yet' - ); + $result .= '>'; } + $childNode = $node->firstChild; + while ($childNode) { + $result .= self::makeDomNode($childNode); + $childNode = $childNode->nextSibling; + } + + if ($node->firstChild) { + $result .= 'nodeName . '>'; + } return $result; + } elseif ($node instanceof DOMCharacterData) { + return $node->data; + } + + throw new UnimplementedFeatureException( + 'assembling of '.get_class($node).' is not implemented yet' + ); } - public function getHtml() + /** + * @return string|null + * @throws WrongArgumentException + */ + public function getHtml(): ?string { $result = null; @@ -114,43 +131,50 @@ public function getHtml() return $result; } - private static function getAttributes(SgmlOpenTag $tag) + /** + * @param SgmlOpenTag $tag + * @return string + */ + private static function getAttributes(SgmlOpenTag $tag): string { $attributes = array(); foreach ($tag->getAttributesList() as $name => $value) { - if ($value === null) - $quotedValue = null; - else - // FIXME: is multibyte safe? - $quotedValue = '="'.preg_replace('/\"/u', '"', $value).'"'; - - $attributes[] = $name.$quotedValue; + $attributes[] = + $name + . ( + $value === null + ? '' + : '="' . preg_replace('/\"/u', '"', $value) . '"' + ); } return implode(' ', $attributes); } - private static function getDomAttributes(\DOMNode $node) + /** + * @param DOMNode $node + * @return string + */ + private static function getDomAttributes(DOMNode $node): string { - $result = null; + if ($node->attributes->length === 0) { + return ''; + } $attributes = array(); - - if ($node->attributes) { - $i = 0; - - while ($item = $node->attributes->item($i)) { - $attributes[] = $item->name.'="'.$item->value.'"'; - - ++$i; - } + $i = 0; + + while ($item = $node->attributes->item($i++)) { + $attributes[] = + $item->name + . ( + $item->value + ? '="' . preg_replace('/\"/u', '"', $item->value) . '"' + : '' + ); } - if ($attributes) - $result = implode(' ', $attributes); - - return $result; + return implode(' ', $attributes); } -} -?> \ No newline at end of file +} \ No newline at end of file diff --git a/src/Main/Markup/Html/HtmlTokenizer.php b/src/Main/Markup/Html/HtmlTokenizer.php index fb308fb2d5..fce062a1d0 100644 --- a/src/Main/Markup/Html/HtmlTokenizer.php +++ b/src/Main/Markup/Html/HtmlTokenizer.php @@ -11,6 +11,8 @@ namespace OnPHP\Main\Markup\Html; +use OnPHP\Core\Exception\IOException; +use OnPHP\Core\Exception\WrongArgumentException; use OnPHP\Main\Util\IO\InputStream; use OnPHP\Core\Base\Assert; use OnPHP\Core\Exception\WrongStateException; @@ -40,16 +42,21 @@ final class HtmlTokenizer const ID_FIRST_CHAR_MASK = '[A-Za-z]'; const ID_CHAR_MASK = '[-_:.A-Za-z0-9]'; - private $inlineTags = array('style', 'script', 'textarea'); - - private $stream = null; + /** + * @var string[] + */ + private array $inlineTags = array('style', 'script', 'textarea'); + /** + * @var InputStream|null + */ + private InputStream $stream; - private $char = null; + private ?string $char = null; // for logging - private $line = 1; - private $linePosition = 1; - private $previousChar = null; + private int $line = 1; + private int $linePosition = 1; + private ?string $previousChar = null; private $mark = null; @@ -76,6 +83,9 @@ final class HtmlTokenizer private $lowercaseAttributes = false; private $lowercaseTags = false; + /** + * @param InputStream $stream + */ public function __construct(InputStream $stream) { $this->stream = $stream; @@ -84,101 +94,126 @@ public function __construct(InputStream $stream) } /** - * @return HtmlTokenizer - **/ - public static function create(InputStream $stream) + * @param InputStream $stream + * @return static + */ + public static function create(InputStream $stream): HtmlTokenizer { return new self($stream); } /** - * @return HtmlTokenizer - **/ - public function suppressWhitespaces($isSuppressWhitespaces) + * @param bool $isSuppressWhitespaces + * @return static + */ + public function suppressWhitespaces(bool $isSuppressWhitespaces): HtmlTokenizer { - Assert::isBoolean($isSuppressWhitespaces); - $this->suppressWhitespaces = $isSuppressWhitespaces; return $this; } /** - * @return HtmlTokenizer - **/ - public function lowercaseAttributes($isLowercaseAttributes) + * @param bool $isLowercaseAttributes + * @return static + */ + public function lowercaseAttributes(bool $isLowercaseAttributes): HtmlTokenizer { - Assert::isBoolean($isLowercaseAttributes); - $this->lowercaseAttributes = $isLowercaseAttributes; return $this; } /** - * @return HtmlTokenizer - **/ - public function lowercaseTags($isLowercaseTags) + * @param bool $isLowercaseTags + * @return static + */ + public function lowercaseTags(bool $isLowercaseTags): HtmlTokenizer { - Assert::isBoolean($isLowercaseTags); - $this->lowercaseTags = $isLowercaseTags; return $this; } /** - * @return SgmlToken - **/ - public function nextToken() + * @return SgmlToken|null + * @throws IOException + * @throws WrongArgumentException + * @throws WrongStateException + */ + public function nextToken(): ?SgmlToken { - if ($this->state == self::FINAL_STATE) + if ($this->state == self::FINAL_STATE) { return null; + } $this->completeTag = null; - while ($this->state != self::FINAL_STATE && !$this->completeTag) + while ($this->state != self::FINAL_STATE && !$this->completeTag) { $this->state = $this->handleState(); + } - if ($this->state == self::FINAL_STATE && $this->char !== null) + if ($this->state == self::FINAL_STATE && $this->char !== null) { throw new WrongStateException('state machine is broken'); - + } $this->previousTag = $this->completeTag; return $this->completeTag; } - public function getErrors() + /** + * @return array + */ + public function getErrors(): array { return $this->errors; } - public static function isIdFirstChar($char) + /** + * @param $char + * @return bool + */ + public static function isIdFirstChar($char): bool { - return (preg_match('/'.self::ID_FIRST_CHAR_MASK.'/', $char) > 0); + return preg_match('/'.self::ID_FIRST_CHAR_MASK.'/', $char) > 0; } - public static function isIdChar($char) + /** + * @param string $char + * @return bool + */ + public static function isIdChar(string $char): bool { - return (preg_match('/'.self::ID_CHAR_MASK.'/', $char) > 0); + return preg_match('/'.self::ID_CHAR_MASK.'/', $char) > 0; } - public static function isValidId($id) + /** + * @param string $id + * @return bool + */ + public static function isValidId(string $id): bool { - $matches = preg_match( - '/^'.self::ID_FIRST_CHAR_MASK.self::ID_CHAR_MASK.'*$/', - $id - ); - - return ($matches > 0); + return + preg_match( + '/^'.self::ID_FIRST_CHAR_MASK.self::ID_CHAR_MASK.'*$/', + $id + ) > 0; } - public static function isSpacerChar($char) + /** + * @param string $char + * @return bool + */ + public static function isSpacerChar(string $char): bool { - return (preg_match('/'.self::SPACER_MASK.'/', $char) > 0); + return preg_match('/'.self::SPACER_MASK.'/', $char) > 0; } - public static function removeWhitespaces(Cdata $cdata) + /** + * @param Cdata $cdata + * @return Cdata|null + */ + public static function removeWhitespaces(Cdata $cdata): ?Cdata { $string = $cdata->getData(); @@ -187,40 +222,50 @@ public static function removeWhitespaces(Cdata $cdata) ' ', $string ); - $string = preg_replace( '/'.self::SPACER_MASK.'+$/', ' ', $string ); - if ($string === '' || $string === null) - return null; - - $cdata->setData($string); - - return $cdata; + return + empty($string) + ? null + : $cdata->setData($string); } - public function isInlineTag($id) + /** + * @param string $id + * @return bool + */ + public function isInlineTag(string $id): bool { return in_array($id, $this->inlineTags); } - private static function optionalLowercase($string, $ignoreCase) + /** + * @param string $string + * @param bool $ignoreCase + * @return string + */ + private static function optionalLowercase(string $string, bool $ignoreCase): string { - if (!$ignoreCase) - return $string; - else - return strtolower($string); + return + $ignoreCase + ? mb_strtolower($string) + : $string; } - private function getNextChar() + /** + * @return string|null + */ + private function getNextChar(): ?string { $this->char = $this->stream->read(1); - if ($this->char === null) + if ($this->char === null) { return null; + } if ( $this->char == "\n" && $this->previousChar != "\r" @@ -231,13 +276,16 @@ private function getNextChar() } else { ++$this->linePosition; } - $this->previousChar = $this->char; return $this->char; } - private function getChars($count) + /** + * @param int $count + * @return string|null + */ + private function getChars(int $count): ?string { $result = null; @@ -253,30 +301,35 @@ private function getChars($count) } /** - * @return HtmlTokenizer - **/ - private function mark() + * @return static + */ + private function mark(): HtmlTokenizer { $this->mark = array( - $this->char, $this->previousChar, - $this->line, $this->linePosition + $this->char, + $this->previousChar, + $this->line, + $this->linePosition ); - $this->stream->mark(); return $this; } /** - * @return HtmlTokenizer - **/ - private function reset() + * @return static + * @throws IOException + * @throws WrongArgumentException + */ + private function reset(): HtmlTokenizer { Assert::isNotNull($this->mark); list ( - $this->char, $this->previousChar, - $this->line, $this->linePosition + $this->char, + $this->previousChar, + $this->line, + $this->linePosition ) = $this->mark; $this->stream->reset(); @@ -285,28 +338,40 @@ private function reset() } /** - * @return HtmlTokenizer - **/ - private function skip($count) + * @param int $count + * @return static + */ + private function skip(int $count): HtmlTokenizer { - for ($i = 0; $i < $count; ++$i) + for ($i = 0; $i < $count; ++$i) { $this->getNextChar(); + } return $this; } - private function lookAhead($count) + /** + * @param int $count + * @return string + * @throws IOException + */ + private function lookAhead(int $count): string { $this->stream->mark(); - $result = $this->stream->read($count); - $this->stream->reset(); return $result; } - private function skipString($string, $skipSpaces = false) + /** + * @param string $string + * @param bool $skipSpaces + * @return bool + * @throws IOException + * @throws WrongArgumentException + */ + private function skipString(string $string, bool $skipSpaces = false): bool { $this->mark(); @@ -314,14 +379,16 @@ private function skipString($string, $skipSpaces = false) while ( $this->char !== null && self::isSpacerChar($this->char) - ) + ) { $this->getNextChar(); + } } - $length = strlen($string); + $length = mb_strlen($string); - if ($this->getChars($length) === $string) + if ($this->getChars($length) === $string) { return true; + } $this->reset(); @@ -330,22 +397,22 @@ private function skipString($string, $skipSpaces = false) /** * @return HtmlTokenizer - **/ - private function makeTag() + * @throws WrongArgumentException + */ + private function makeTag(): HtmlTokenizer { Assert::isNotNull($this->tag); - Assert::isNull($this->attrName); Assert::isNull($this->attrValue); - Assert::isNull($this->insideQuote); if ( !$this->suppressWhitespaces || !$this->tag instanceof Cdata || (self::removeWhitespaces($this->tag) !== null) - ) + ) { $this->tags[] = $this->completeTag = $this->tag; + } $this->tagId = $this->tag = null; @@ -353,21 +420,28 @@ private function makeTag() } /** + * @param SgmlTag $tag * @return SgmlTag - **/ - private function setupTag(SgmlTag $tag) + * @throws WrongArgumentException + */ + private function setupTag(SgmlTag $tag): SgmlTag { Assert::isNull($this->tag); Assert::isNotNull($this->tagId); $this->tag = $tag->setId($this->tagId); - $this->tagId = null; return $this->tag; } - private function handleState() + /** + * @return int + * @throws IOException + * @throws WrongArgumentException + * @throws WrongStateException + */ + private function handleState(): int { switch ($this->state) { case self::INITIAL_STATE: @@ -375,10 +449,11 @@ private function handleState() if ( $this->previousTag instanceof SgmlOpenTag && $this->isInlineTag($this->previousTag->getId()) - ) + ) { return $this->inlineTagState(); - else - return $this->outsideTagState(); + } + + return $this->outsideTagState(); case self::START_TAG_STATE: return $this->startTagState(); @@ -415,25 +490,30 @@ private function handleState() } /** - * @return HtmlTokenizer - **/ - private function dumpBuffer() + * @return static + * @throws WrongArgumentException + */ + private function dumpBuffer(): HtmlTokenizer { if ($this->buffer !== null) { $this->tag = Cdata::create()->setData($this->buffer); - $this->buffer = null; - $this->makeTag(); } return $this; } - private function checkSpecialTagState() + /** + * @return bool|null + * @throws IOException + * @throws WrongArgumentException + */ + private function checkSpecialTagState(): ?bool { - if ($this->char != '!') + if ($this->char != '!') { return null; + } $specialStartTags = array( '![CDATA[' => self::CDATA_STATE, @@ -441,34 +521,34 @@ private function checkSpecialTagState() ); foreach ($specialStartTags as $tag => $state) { - - if ($this->skipString($tag)) + if ($this->skipString($tag)) { return $state; + } } return null; } - // INITIAL_STATE - private function outsideTagState() + /** + * INITIAL_STATE + * @return int + * @throws IOException + * @throws WrongArgumentException + */ + private function outsideTagState(): int { Assert::isNull($this->tag); Assert::isNull($this->tagId); - Assert::isNull($this->attrName); Assert::isNull($this->attrValue); - Assert::isNull($this->insideQuote); while ($this->char !== null) { if ($this->char != '<') { - $this->buffer .= $this->char; $this->getNextChar(); - } else { - $this->getNextChar(); if ( @@ -486,29 +566,22 @@ private function outsideTagState() } $this->tagId = $this->char; - $this->getNextChar(); return self::START_TAG_STATE; - } elseif ($this->char == '/') { // dumpBuffer(); - - $this->getNextChar(); + $this + ->dumpBuffer() + ->getNextChar(); return self::END_TAG_STATE; - } else { // <2, <ф, <[space], <>, <[eof] - $this->warning( - 'incorrect start-tag, treating it as cdata' - ); - + $this->warning('incorrect start-tag, treating it as cdata'); $this->buffer .= '<'.$this->char; - $this->getNextChar(); continue; @@ -525,41 +598,42 @@ private function outsideTagState() /** * @return SgmlOpenTag - **/ - private function createOpenTag() + * @throws WrongArgumentException + */ + private function createOpenTag(): SgmlOpenTag { - if (!self::isValidId($this->tagId)) + if (!self::isValidId($this->tagId)) { $this->error("tag id '{$this->tagId}' is invalid"); - elseif ($this->lowercaseTags) + } elseif ($this->lowercaseTags) { $this->tagId = strtolower($this->tagId); + } return $this->setupTag(SgmlOpenTag::create()); } - // START_TAG_STATE - private function startTagState() + /** + * START_TAG_STATE + * @return int + * @throws WrongArgumentException + */ + private function startTagState(): int { Assert::isNull($this->tag); - Assert::isNotNull($this->tagId); // strlen(tagId) == 1 - + Assert::isNotNull($this->tagId); // mb_strlen(tagId) == 1 Assert::isNull($this->attrName); Assert::isNull($this->attrValue); - Assert::isNull($this->insideQuote); while ($this->char !== null) { - if ($this->char == '>') { // , $this->createOpenTag(); - - $this->makeTag(); - - $this->getNextChar(); + $this + ->makeTag() + ->getNextChar(); return self::INITIAL_STATE; - } elseif (self::isSpacerChar($this->char)) { // tagId[0] == '?') && ($this->tagId != '?xml'); - $doctypeTag = (strtoupper($this->tagId) == '!DOCTYPE'); if ($externalTag) { @@ -577,19 +650,20 @@ private function startTagState() ); } elseif ($doctypeTag) { $this->setupTag(SgmlIgnoredTag::create()); - } else + } else { $this->createOpenTag(); + } - if ($externalTag) + if ($externalTag) { return self::EXTERNAL_TAG_STATE; - elseif ($doctypeTag) + } elseif ($doctypeTag) { return self::DOCTYPE_TAG_STATE; - else { - // don't eating spacer for external and doctype tags - $this->getNextChar(); - - return self::INSIDE_TAG_STATE; } + + // don't eating spacer for external and doctype tags + $this->getNextChar(); + + return self::INSIDE_TAG_STATE; } else { $char = $this->char; @@ -599,10 +673,9 @@ private function startTagState() //
$this->createOpenTag()->setEmpty(true); - - $this->makeTag(); - - $this->getNextChar(); + $this + ->makeTag() + ->getNextChar(); return self::INITIAL_STATE; } @@ -614,77 +687,70 @@ private function startTagState() // ... error('unexpected end of file, tag id is incomplete'); - $this->createOpenTag(); - $this->makeTag(); return self::FINAL_STATE; } /** - * @return HtmlTokenizer - **/ - private function dumpEndTag() + * @return static + * @throws WrongArgumentException + */ + private function dumpEndTag(): HtmlTokenizer { if (!$this->tagId) { // $this->warning('empty end-tag, storing with empty id'); - } elseif (!self::isValidId($this->tagId)) { - $this->error("end-tag id '{$this->tagId}' is invalid"); } - $this->tag = SgmlEndTag::create()-> - setId( - self::optionalLowercase($this->tagId, $this->lowercaseTags) - ); - + $this->tag = + SgmlEndTag::create()-> + setId( + self::optionalLowercase($this->tagId, $this->lowercaseTags) + ); $this->makeTag(); return $this; } - // END_TAG_STATE - private function endTagState() + /** + * END_TAG_STATE + * @return int + * @throws WrongArgumentException + */ + private function endTagState(): int { Assert::isNull($this->tag); - Assert::isTrue( $this->tagId === null || $this->char == '>' || self::isSpacerChar($this->char) ); - Assert::isNull($this->attrName); Assert::isNull($this->attrValue); - Assert::isNull($this->insideQuote); $eatingGarbage = false; while ($this->char !== null) { - if ($this->char == '>') { - $this->dumpEndTag(); - - $this->getNextChar(); + $this + ->dumpEndTag() + ->getNextChar(); return self::INITIAL_STATE; - } elseif ($eatingGarbage) { - $this->getNextChar(); continue; - } elseif (self::isSpacerChar($this->char)) { // most browsers parse end-tag until next '>' char $eatingGarbage = true; - $this->getNextChar(); continue; @@ -698,66 +764,59 @@ private function endTagState() // ... error("unexpected end of file, end-tag is incomplete"); - - $this->dumpEndTag(); + $this + ->error("unexpected end of file, end-tag is incomplete") + ->dumpEndTag(); return self::FINAL_STATE; } - // INSIDE_TAG_STATE - private function insideTagState() + /** + * INSIDE_TAG_STATE + * @return int + * @throws WrongArgumentException + */ + private function insideTagState(): int { Assert::isNull($this->tagId); - Assert::isNull($this->attrName); Assert::isNull($this->attrValue); - Assert::isNotNull($this->tag); - Assert::isTrue($this->tag instanceof SgmlOpenTag); - + Assert::isInstance($this->tag, SgmlOpenTag::class); Assert::isNull($this->insideQuote); while ($this->char !== null) { - if (self::isSpacerChar($this->char)) { - $this->getNextChar(); + $this->getNextChar(); } elseif ($this->char == '>') { // - $this->makeTag(); - - $this->getNextChar(); + $this + ->makeTag() + ->getNextChar(); return self::INITIAL_STATE; - } elseif ($this->char == '=') { // most browsers' behaviour - $this->error( - 'unexpected equal sign, attr name considered empty' - ); - - $this->getNextChar(); + $this + ->error('unexpected equal sign, attr name considered empty') + ->getNextChar(); // call? return self::ATTR_VALUE_STATE; - } else { - $char = $this->char; - $this->getNextChar(); if ($char == '/' && $this->char == '>') { // , $this->tag->setEmpty(true); - - $this->makeTag(); - - $this->getNextChar(); + $this + ->makeTag() + ->getNextChar(); return self::INITIAL_STATE; } @@ -771,50 +830,49 @@ private function insideTagState() // error('unexpected end of file, incomplete tag stored'); - - $this->makeTag(); + $this + ->error('unexpected end of file, incomplete tag stored') + ->makeTag(); return self::FINAL_STATE; } /** - * @return SgmlOpenTag - **/ - private function dumpAttribute() + * @return static + */ + private function dumpAttribute(): HtmlTokenizer { if ($this->attrName) { - if (!self::isValidId($this->attrName)) $this->error("attribute name '{$this->attrName}' is invalid"); else $this->attrName = strtolower($this->attrName); - } - if ($this->attrValue === null || $this->attrValue === '') + if ($this->attrValue === null || $this->attrValue === '') { $this->warning("empty value for attr == '{$this->attrName}'"); + } $this->tag->setAttribute($this->attrName, $this->attrValue); - $this->attrName = $this->attrValue = null; return $this; } - // ATTR_NAME_STATE - private function attrNameState() + /** + * ATTR_NAME_STATE + * @return int + * @throws WrongArgumentException + */ + private function attrNameState(): int { Assert::isNotNull($this->tag); - Assert::isTrue($this->tag instanceof SgmlOpenTag); - + Assert::isInstance($this->tag,SgmlOpenTag::class); Assert::isNotNull($this->attrName); // length == 1 Assert::isNull($this->attrValue); - Assert::isNull($this->insideQuote); while ($this->char !== null) { - if (self::isSpacerChar($this->char)) { // char == '>') { // - $this->dumpAttribute(); - - $this->makeTag(); - - $this->getNextChar(); + $this + ->dumpAttribute() + ->makeTag() + ->getNextChar(); return self::INITIAL_STATE; - } elseif ($this->char == '=') { // char; $this->getNextChar(); @@ -855,12 +908,10 @@ private function attrNameState() //