From 74c8c7079f17fc876e82e41d4d34633e9b289492 Mon Sep 17 00:00:00 2001 From: Joby Elliott Date: Wed, 21 Dec 2022 11:48:59 -0700 Subject: [PATCH] style/test improvements --- composer.json | 20 +++--- phpcs.xml | 19 +++++ src/AbstractParser.php | 15 ++-- src/Containers/ContainerGroup.php | 8 +-- src/Containers/Fragment.php | 4 +- src/ContentCategories/SectioningRoot.php | 2 +- src/Helpers/Attributes.php | 45 +++++++----- src/Helpers/Classes.php | 21 ++++-- src/Helpers/Styles.php | 52 ++++++++------ src/Html5/DocumentTags/HeadTag.php | 6 +- src/Html5/DocumentTags/HtmlTag.php | 8 +-- src/Html5/Html5Document.php | 4 +- src/Html5/Tags/BaseTag.php | 14 ++-- src/Html5/Tags/LinkTag.php | 77 +++++++++++++++------ src/Html5/Tags/MetaTag.php | 28 +++++--- src/Html5/Tags/ScriptTag.php | 42 +++++++---- src/Html5/Tags/StyleTag.php | 14 ++-- src/Html5/TextContentTags/BlockquoteTag.php | 7 +- src/Html5/TextContentTags/OlTag.php | 9 ++- src/Tags/AbstractContainerTag.php | 18 +++-- src/Tags/AbstractContentTag.php | 15 ++-- src/Tags/AbstractGroupedTag.php | 15 ++-- src/Tags/AbstractTag.php | 3 +- src/Tags/ContainerTagInterface.php | 2 +- src/Tags/ContentTagInterface.php | 2 +- src/Tags/TagInterface.php | 2 +- src/Traits/ContainerTrait.php | 29 +++++--- src/Traits/GroupedContainerTrait.php | 6 +- src/Traits/NodeTrait.php | 2 +- src/Traits/TagTrait.php | 17 +++-- tests/Helpers/StylesTest.php | 25 +++++++ tests/Html5/Tags/TagTestCase.php | 4 ++ tests/Tags/AbstractGroupedTagTest.php | 21 ++++++ tests/Tags/AbstractTagTest.php | 12 +++- 34 files changed, 399 insertions(+), 169 deletions(-) create mode 100644 phpcs.xml create mode 100644 tests/Tags/AbstractGroupedTagTest.php diff --git a/composer.json b/composer.json index f3e7561..f5a1fbb 100644 --- a/composer.json +++ b/composer.json @@ -3,14 +3,16 @@ "description": "Abstraction layer for constructing arbitrary HTML tags and documents in PHP", "type": "library", "require": { - "php": ">=8.1", + "php": ">=8.1", "myclabs/deep-copy": "^1" }, "license": "MIT", - "authors": [{ - "name": "Joby Elliott", - "email": "joby@byjoby.com" - }], + "authors": [ + { + "name": "Joby Elliott", + "email": "joby@byjoby.com" + } + ], "minimum-stability": "dev", "prefer-stable": true, "autoload": { @@ -25,10 +27,12 @@ }, "scripts": { "test": "phpunit", - "stan": "phpstan" + "stan": "phpstan", + "sniff": "phpcs" }, "require-dev": { "phpstan/phpstan": "^1.9", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.7" } -} +} \ No newline at end of file diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..5064e75 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,19 @@ + + + Coding Standard + + src + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/AbstractParser.php b/src/AbstractParser.php index 5db0afd..73eef23 100644 --- a/src/AbstractParser.php +++ b/src/AbstractParser.php @@ -4,7 +4,6 @@ namespace ByJoby\HTML; use ByJoby\HTML\Containers\Fragment; use ByJoby\HTML\Containers\FragmentInterface; -use ByJoby\HTML\Containers\GenericHtmlDocument; use ByJoby\HTML\Containers\HtmlDocumentInterface; use ByJoby\HTML\Nodes\CData; use ByJoby\HTML\Nodes\CDataInterface; @@ -109,8 +108,10 @@ abstract class AbstractParser { // build object $class = $this->tagClass($node->tagName); - if (!$class) return null; - $tag = new $class; + if (!$class) { + return null; + } + $tag = new $class(); // tool for settin gup content tags if ($tag instanceof ContentTagInterface) { $tag->setContent($node->textContent); @@ -124,12 +125,11 @@ abstract class AbstractParser protected function processAttributes(DOMElement $node, TagInterface $tag): void { - if (!$node->attributes) return; /** @var array */ $attributes = []; // absorb attributes /** @var DOMNode $attribute */ - foreach ($node->attributes as $attribute) { + foreach ($node->attributes ?? [] as $attribute) { if ($attribute->nodeValue) { $attributes[$attribute->nodeName] = $attribute->nodeValue; } else { @@ -148,10 +148,9 @@ abstract class AbstractParser // make an effort to set ID try { $tag->attributes()["$k"] = $v; - } - // it is correct to ignore attributes that are unsettable - catch (\Throwable $th) { // @codeCoverageIgnore + } catch (\Throwable $th) { // @codeCoverageIgnore // does nothing + // it is correct to ignore attributes that are unsettable } } } diff --git a/src/Containers/ContainerGroup.php b/src/Containers/ContainerGroup.php index 024175a..993c499 100644 --- a/src/Containers/ContainerGroup.php +++ b/src/Containers/ContainerGroup.php @@ -29,7 +29,7 @@ class ContainerGroup implements ContainerInterface, NodeInterface protected $limit = 0; /** - * @param int $limit + * @param int $limit * @return ContainerGroup */ public static function catchAll(int $limit = 0): ContainerGroup @@ -44,8 +44,8 @@ class ContainerGroup implements ContainerInterface, NodeInterface /** * @template C of T - * @param class-string $class - * @param int $limit + * @param class-string $class + * @param int $limit * @return ContainerGroup */ public static function ofClass(string $class, int $limit = 0): ContainerGroup @@ -60,7 +60,7 @@ class ContainerGroup implements ContainerInterface, NodeInterface /** * @param string $tag - * @param int $limit + * @param int $limit * @return ContainerGroup */ public static function ofTag(string $tag, int $limit = 0): ContainerGroup diff --git a/src/Containers/Fragment.php b/src/Containers/Fragment.php index 66c14c6..b9aeca2 100644 --- a/src/Containers/Fragment.php +++ b/src/Containers/Fragment.php @@ -16,7 +16,9 @@ class Fragment implements FragmentInterface */ public function __construct(null|array|Traversable $children = null) { - if (!$children) return; + if (!$children) { + return; + } foreach ($children as $child) { $this->addChild($child); } diff --git a/src/ContentCategories/SectioningRoot.php b/src/ContentCategories/SectioningRoot.php index 4552771..4657bbc 100644 --- a/src/ContentCategories/SectioningRoot.php +++ b/src/ContentCategories/SectioningRoot.php @@ -4,4 +4,4 @@ namespace ByJoby\HTML\ContentCategories; interface SectioningRoot { -} \ No newline at end of file +} diff --git a/src/Helpers/Attributes.php b/src/Helpers/Attributes.php index d014900..405fbcd 100644 --- a/src/Helpers/Attributes.php +++ b/src/Helpers/Attributes.php @@ -11,7 +11,7 @@ use Traversable; /** * Holds and validates a set of HTML attribute name/value pairs for use in tags. - * + * * @implements ArrayAccess * @implements IteratorAggregate */ @@ -25,48 +25,59 @@ class Attributes implements IteratorAggregate, ArrayAccess protected $disallowed = []; /** - * @param null|array $array + * @param null|array $array * @param array $disallowed - * @return void + * @return void */ public function __construct(null|array $array = null, $disallowed = []) { $this->disallowed = $disallowed; - if (!$array) return; + if (!$array) { + return; + } foreach ($array as $key => $value) { $this[$key] = $value; } } - function offsetExists(mixed $offset): bool + public function offsetExists(mixed $offset): bool { $offset = static::sanitizeOffset($offset); return isset($this->array[$offset]); } - function offsetGet(mixed $offset): mixed + public function offsetGet(mixed $offset): mixed { $offset = static::sanitizeOffset($offset); return @$this->array[$offset]; } - function offsetSet(mixed $offset, mixed $value): void + public function offsetSet(mixed $offset, mixed $value): void { - if (!$offset || !trim($offset)) throw new Exception('Attribute name must be specified when setting'); + if (!$offset || !trim($offset)) { + throw new Exception('Attribute name must be specified when setting'); + } $offset = static::sanitizeOffset($offset); - if (in_array($offset, $this->disallowed)) throw new Exception('Setting attribute is disallowed'); - if (!isset($this->array[$offset])) $this->sorted = false; + if (in_array($offset, $this->disallowed)) { + throw new Exception('Setting attribute is disallowed'); + } + if (!isset($this->array[$offset])) { + $this->sorted = false; + } $this->array[$offset] = $value; } public function string(string $offset): null|string { $value = $this->offsetGet($offset); - if (is_string($value)) return $value; - else return null; + if (is_string($value)) { + return $value; + } else { + return null; + } } - function offsetUnset(mixed $offset): void + public function offsetUnset(mixed $offset): void { $offset = static::sanitizeOffset($offset); unset($this->array[$offset]); @@ -75,7 +86,7 @@ class Attributes implements IteratorAggregate, ArrayAccess /** * @return array */ - function getArray(): array + public function getArray(): array { if (!$this->sorted) { ksort($this->array); @@ -84,7 +95,7 @@ class Attributes implements IteratorAggregate, ArrayAccess return $this->array; } - function getIterator(): Traversable + public function getIterator(): Traversable { return new ArrayIterator($this->getArray()); } @@ -93,7 +104,9 @@ class Attributes implements IteratorAggregate, ArrayAccess { $offset = trim($offset); $offset = strtolower($offset); - if (preg_match('/[\t\n\f \/>"\'=]/', $offset)) throw new Exception('Invalid character in attribute name'); + if (preg_match('/[\t\n\f \/>"\'=]/', $offset)) { + throw new Exception('Invalid character in attribute name'); + } return $offset; } } diff --git a/src/Helpers/Classes.php b/src/Helpers/Classes.php index 3283579..4c46a75 100644 --- a/src/Helpers/Classes.php +++ b/src/Helpers/Classes.php @@ -24,7 +24,9 @@ class Classes implements Countable */ public function __construct(null|array|Traversable $array = null, bool $no_exception = true) { - if (!$array) return; + if (!$array) { + return; + } foreach ($array as $class) { $this->add($class, $no_exception); } @@ -34,7 +36,9 @@ class Classes implements Countable { foreach (explode(' ', $class_string) as $class) { $class = trim($class); - if ($class) $this->add($class); + if ($class) { + $this->add($class); + } } } @@ -46,7 +50,7 @@ class Classes implements Countable /** * @return array */ - function getArray(): array + public function getArray(): array { if (!$this->sorted) { sort($this->classes); @@ -60,8 +64,11 @@ class Classes implements Countable try { $class = static::sanitizeClassName($class, true); } catch (\Throwable $th) { - if ($no_exception) return $this; - else throw $th; + if ($no_exception) { + return $this; + } else { + throw $th; + } } if (!in_array($class, $this->classes)) { $this->classes[] = $class; @@ -91,7 +98,9 @@ class Classes implements Countable protected static function sanitizeClassName(string $class, bool $validate = false): string { $class = trim($class); - if ($validate && !preg_match('/^[_\-a-z][_\-a-z0-9]*$/i', $class)) throw new Exception('Invalid class name'); + if ($validate && !preg_match('/^[_\-a-z][_\-a-z0-9]*$/i', $class)) { + throw new Exception('Invalid class name'); + } return $class; } } diff --git a/src/Helpers/Styles.php b/src/Helpers/Styles.php index 18affe4..5dff577 100644 --- a/src/Helpers/Styles.php +++ b/src/Helpers/Styles.php @@ -8,13 +8,13 @@ use Stringable; use Traversable; /** - * A key difference in strategy between this class and Attributes or Classes is + * A key difference in strategy between this class and Attributes or Classes is * that it does not make significant validation attempts. CSS is an evolving * language, and it would be a fool's errand to try and thoroughly validate it. - * + * * To that end, this class is very accepting of not-obviously-malformed property * names and values. - * + * * @implements ArrayAccess */ class Styles implements Countable, ArrayAccess, Stringable @@ -29,7 +29,9 @@ class Styles implements Countable, ArrayAccess, Stringable */ public function __construct(null|array|Traversable $classes = null) { - if (!$classes) return; + if (!$classes) { + return; + } foreach ($classes as $name => $value) { $this[$name] = $value; } @@ -39,7 +41,9 @@ class Styles implements Countable, ArrayAccess, Stringable { foreach (explode(';', $css_string) as $rule) { $rule = explode(':', trim($rule)); - if (count($rule) == 2) $this[$rule[0]] = $rule[1]; + if (count($rule) == 2) { + $this[$rule[0]] = $rule[1]; + } } } @@ -50,8 +54,7 @@ class Styles implements Countable, ArrayAccess, Stringable public function offsetExists(mixed $offset): bool { - if (!$offset) return false; - return isset($this->styles[$offset]); + return @isset($this->styles[$offset]); } public function offsetGet(mixed $offset): mixed @@ -61,13 +64,16 @@ class Styles implements Countable, ArrayAccess, Stringable public function offsetSet(mixed $offset, mixed $value): void { - if (!$offset) return; - if ($value) $value = trim($value); - if (!$value) unset($this->styles[$offset]); - else { - if (!static::validate($offset, $value)) return; - if (!isset($this->styles[$offset])) $this->sorted = false; - $this->styles[$offset] = $value; + if (!$value) { + unset($this->styles[$offset]); + } else { + if (!static::validate($offset, $value)) { + return; + } + if (!isset($this->styles[$offset])) { + $this->sorted = false; + } + $this->styles[$offset] = trim($value); } } @@ -97,15 +103,19 @@ class Styles implements Countable, ArrayAccess, Stringable return implode(';', $styles); } - protected static function validate(null|string $property, null|string $value): bool + protected static function validate(null|string $property, string $value): bool { - if (!$property) return false; - elseif (!preg_match('/[a-z]/', $property)) return false; + if (!$property) { + return false; + } elseif (!preg_match('/[a-z]/', $property)) { + return false; + } - if ($value) $value = trim($value); - if (!$value) return false; - elseif (str_contains($value, ';')) return false; - elseif (str_contains($value, ':')) return false; + if (str_contains($value, ';')) { + return false; + } elseif (str_contains($value, ':')) { + return false; + } return true; } diff --git a/src/Html5/DocumentTags/HeadTag.php b/src/Html5/DocumentTags/HeadTag.php index 9d4bfc6..da61119 100644 --- a/src/Html5/DocumentTags/HeadTag.php +++ b/src/Html5/DocumentTags/HeadTag.php @@ -10,10 +10,10 @@ use ByJoby\HTML\Traits\GroupedContainerTrait; class HeadTag extends AbstractGroupedTag implements HeadTagInterface { - const TAG = 'head'; - use GroupedContainerTrait; + const TAG = 'head'; + /** @var ContainerGroup */ protected $title; @@ -22,7 +22,7 @@ class HeadTag extends AbstractGroupedTag implements HeadTagInterface parent::__construct(); $this->title = ContainerGroup::ofClass(TitleTagInterface::class, 1); $this->addGroup($this->title); - $this->addChild(new TitleTag); + $this->addChild(new TitleTag()); $this->addGroup(ContainerGroup::ofTag('meta')); $this->addGroup(ContainerGroup::ofTag('base', 1)); $this->addGroup(ContainerGroup::ofTag('style')); diff --git a/src/Html5/DocumentTags/HtmlTag.php b/src/Html5/DocumentTags/HtmlTag.php index ab15c2e..7e99e9d 100644 --- a/src/Html5/DocumentTags/HtmlTag.php +++ b/src/Html5/DocumentTags/HtmlTag.php @@ -11,10 +11,10 @@ use ByJoby\HTML\Traits\GroupedContainerTrait; class HtmlTag extends AbstractGroupedTag implements HtmlTagInterface { - const TAG = 'html'; - use GroupedContainerTrait; + const TAG = 'html'; + /** @var ContainerGroup */ protected $head; /** @var ContainerGroup */ @@ -27,8 +27,8 @@ class HtmlTag extends AbstractGroupedTag implements HtmlTagInterface $this->body = ContainerGroup::ofClass(BodyTagInterface::class, 1); $this->addGroup($this->head); $this->addGroup($this->body); - $this->addChild(new HeadTag); - $this->addChild(new BodyTag); + $this->addChild(new HeadTag()); + $this->addChild(new BodyTag()); } public function head(): HeadTagInterface diff --git a/src/Html5/Html5Document.php b/src/Html5/Html5Document.php index e92fa31..795a42a 100644 --- a/src/Html5/Html5Document.php +++ b/src/Html5/Html5Document.php @@ -27,8 +27,8 @@ class Html5Document implements HtmlDocumentInterface $this->html = ContainerGroup::ofClass(HtmlTagInterface::class, 1); $this->addGroup($this->doctype); $this->addGroup($this->html); - $this->addChild(new Doctype); - $this->addChild(new HtmlTag); + $this->addChild(new Doctype()); + $this->addChild(new HtmlTag()); } public function doctype(): DoctypeInterface diff --git a/src/Html5/Tags/BaseTag.php b/src/Html5/Tags/BaseTag.php index da499d1..d55859a 100644 --- a/src/Html5/Tags/BaseTag.php +++ b/src/Html5/Tags/BaseTag.php @@ -16,8 +16,11 @@ class BaseTag extends AbstractTag implements MetadataContent public function setHref(null|string $href): static { - if (!$href) $this->attributes()['href'] = false; - else $this->attributes()['href'] = $href; + if (!$href) { + $this->attributes()['href'] = false; + } else { + $this->attributes()['href'] = $href; + } return $this; } @@ -34,8 +37,11 @@ class BaseTag extends AbstractTag implements MetadataContent public function setTarget(null|string $target): static { - if (!$target) $this->attributes()['target'] = false; - else $this->attributes()['target'] = $target; + if (!$target) { + $this->attributes()['target'] = false; + } else { + $this->attributes()['target'] = $target; + } return $this; } diff --git a/src/Html5/Tags/LinkTag.php b/src/Html5/Tags/LinkTag.php index b8cff37..99a4804 100644 --- a/src/Html5/Tags/LinkTag.php +++ b/src/Html5/Tags/LinkTag.php @@ -16,8 +16,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setRel(null|string $rel): static { - if (!$rel) $this->attributes()['rel'] = false; - else $this->attributes()['rel'] = $rel; + if (!$rel) { + $this->attributes()['rel'] = false; + } else { + $this->attributes()['rel'] = $rel; + } return $this; } @@ -34,8 +37,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setAs(null|string $as): static { - if (!$as) $this->attributes()['as'] = false; - else $this->attributes()['as'] = $as; + if (!$as) { + $this->attributes()['as'] = false; + } else { + $this->attributes()['as'] = $as; + } return $this; } @@ -52,8 +58,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setCrossorigin(null|string $crossorigin): static { - if (!$crossorigin) $this->attributes()['crossorigin'] = false; - else $this->attributes()['crossorigin'] = $crossorigin; + if (!$crossorigin) { + $this->attributes()['crossorigin'] = false; + } else { + $this->attributes()['crossorigin'] = $crossorigin; + } return $this; } @@ -70,8 +79,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setHref(null|string $href): static { - if (!$href) $this->attributes()['href'] = false; - else $this->attributes()['href'] = $href; + if (!$href) { + $this->attributes()['href'] = false; + } else { + $this->attributes()['href'] = $href; + } return $this; } @@ -88,8 +100,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setHreflang(null|string $hreflang): static { - if (!$hreflang) $this->attributes()['hreflang'] = false; - else $this->attributes()['hreflang'] = $hreflang; + if (!$hreflang) { + $this->attributes()['hreflang'] = false; + } else { + $this->attributes()['hreflang'] = $hreflang; + } return $this; } @@ -106,8 +121,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setImagesizes(null|string $imagesizes): static { - if (!$imagesizes) $this->attributes()['imagesizes'] = false; - else $this->attributes()['imagesizes'] = $imagesizes; + if (!$imagesizes) { + $this->attributes()['imagesizes'] = false; + } else { + $this->attributes()['imagesizes'] = $imagesizes; + } return $this; } @@ -124,8 +142,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setImagesrcset(null|string $imagesrcset): static { - if (!$imagesrcset) $this->attributes()['imagesrcset'] = false; - else $this->attributes()['imagesrcset'] = $imagesrcset; + if (!$imagesrcset) { + $this->attributes()['imagesrcset'] = false; + } else { + $this->attributes()['imagesrcset'] = $imagesrcset; + } return $this; } @@ -142,8 +163,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setIntegrity(null|string $integrity): static { - if (!$integrity) $this->attributes()['integrity'] = false; - else $this->attributes()['integrity'] = $integrity; + if (!$integrity) { + $this->attributes()['integrity'] = false; + } else { + $this->attributes()['integrity'] = $integrity; + } return $this; } @@ -160,8 +184,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setMedia(null|string $media): static { - if (!$media) $this->attributes()['media'] = false; - else $this->attributes()['media'] = $media; + if (!$media) { + $this->attributes()['media'] = false; + } else { + $this->attributes()['media'] = $media; + } return $this; } @@ -178,8 +205,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setReferrerpolicy(null|string $referrerpolicy): static { - if (!$referrerpolicy) $this->attributes()['referrerpolicy'] = false; - else $this->attributes()['referrerpolicy'] = $referrerpolicy; + if (!$referrerpolicy) { + $this->attributes()['referrerpolicy'] = false; + } else { + $this->attributes()['referrerpolicy'] = $referrerpolicy; + } return $this; } @@ -196,8 +226,11 @@ class LinkTag extends AbstractTag implements MetadataContent public function setType(null|string $type): static { - if (!$type) $this->attributes()['type'] = false; - else $this->attributes()['type'] = $type; + if (!$type) { + $this->attributes()['type'] = false; + } else { + $this->attributes()['type'] = $type; + } return $this; } diff --git a/src/Html5/Tags/MetaTag.php b/src/Html5/Tags/MetaTag.php index 2a9dc62..07d6709 100644 --- a/src/Html5/Tags/MetaTag.php +++ b/src/Html5/Tags/MetaTag.php @@ -16,8 +16,11 @@ class MetaTag extends AbstractTag implements MetadataContent public function setName(null|string $name): static { - if (!$name) $this->attributes()['name'] = false; - else $this->attributes()['name'] = $name; + if (!$name) { + $this->attributes()['name'] = false; + } else { + $this->attributes()['name'] = $name; + } return $this; } @@ -34,8 +37,11 @@ class MetaTag extends AbstractTag implements MetadataContent public function setContent(null|string $content): static { - if (!$content) $this->attributes()['content'] = false; - else $this->attributes()['content'] = $content; + if (!$content) { + $this->attributes()['content'] = false; + } else { + $this->attributes()['content'] = $content; + } return $this; } @@ -52,8 +58,11 @@ class MetaTag extends AbstractTag implements MetadataContent public function setHttpEquiv(null|string $http_equiv): static { - if (!$http_equiv) $this->attributes()['http-equiv'] = false; - else $this->attributes()['http-equiv'] = $http_equiv; + if (!$http_equiv) { + $this->attributes()['http-equiv'] = false; + } else { + $this->attributes()['http-equiv'] = $http_equiv; + } return $this; } @@ -70,8 +79,11 @@ class MetaTag extends AbstractTag implements MetadataContent public function setCharset(null|string $charset): static { - if (!$charset) $this->attributes()['charset'] = false; - else $this->attributes()['charset'] = $charset; + if (!$charset) { + $this->attributes()['charset'] = false; + } else { + $this->attributes()['charset'] = $charset; + } return $this; } diff --git a/src/Html5/Tags/ScriptTag.php b/src/Html5/Tags/ScriptTag.php index 1e71fd2..a60850c 100644 --- a/src/Html5/Tags/ScriptTag.php +++ b/src/Html5/Tags/ScriptTag.php @@ -40,8 +40,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setCrossorigin(null|string $crossorigin): static { - if (!$crossorigin) $this->attributes()['crossorigin'] = false; - else $this->attributes()['crossorigin'] = $crossorigin; + if (!$crossorigin) { + $this->attributes()['crossorigin'] = false; + } else { + $this->attributes()['crossorigin'] = $crossorigin; + } return $this; } @@ -58,8 +61,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setIntegrity(null|string $integrity): static { - if (!$integrity) $this->attributes()['integrity'] = false; - else $this->attributes()['integrity'] = $integrity; + if (!$integrity) { + $this->attributes()['integrity'] = false; + } else { + $this->attributes()['integrity'] = $integrity; + } return $this; } @@ -87,8 +93,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setNonce(null|string $nonce): static { - if (!$nonce) $this->attributes()['nonce'] = false; - else $this->attributes()['nonce'] = $nonce; + if (!$nonce) { + $this->attributes()['nonce'] = false; + } else { + $this->attributes()['nonce'] = $nonce; + } return $this; } @@ -105,8 +114,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setReferrerpolicy(null|string $referrerpolicy): static { - if (!$referrerpolicy) $this->attributes()['referrerpolicy'] = false; - else $this->attributes()['referrerpolicy'] = $referrerpolicy; + if (!$referrerpolicy) { + $this->attributes()['referrerpolicy'] = false; + } else { + $this->attributes()['referrerpolicy'] = $referrerpolicy; + } return $this; } @@ -123,8 +135,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setSrc(null|string $src): static { - if (!$src) $this->attributes()['src'] = false; - else $this->attributes()['src'] = $src; + if (!$src) { + $this->attributes()['src'] = false; + } else { + $this->attributes()['src'] = $src; + } return $this; } @@ -141,8 +156,11 @@ class ScriptTag extends AbstractContentTag implements MetadataContent, PhrasingC public function setType(null|string $type): static { - if (!$type) $this->attributes()['type'] = false; - else $this->attributes()['type'] = $type; + if (!$type) { + $this->attributes()['type'] = false; + } else { + $this->attributes()['type'] = $type; + } return $this; } diff --git a/src/Html5/Tags/StyleTag.php b/src/Html5/Tags/StyleTag.php index 9d2656a..3993eb4 100644 --- a/src/Html5/Tags/StyleTag.php +++ b/src/Html5/Tags/StyleTag.php @@ -16,8 +16,11 @@ class StyleTag extends AbstractContentTag implements MetadataContent public function setMedia(null|string $media): static { - if (!$media) $this->attributes()['media'] = false; - else $this->attributes()['media'] = $media; + if (!$media) { + $this->attributes()['media'] = false; + } else { + $this->attributes()['media'] = $media; + } return $this; } @@ -34,8 +37,11 @@ class StyleTag extends AbstractContentTag implements MetadataContent public function setNonce(null|string $nonce): static { - if (!$nonce) $this->attributes()['nonce'] = false; - else $this->attributes()['nonce'] = $nonce; + if (!$nonce) { + $this->attributes()['nonce'] = false; + } else { + $this->attributes()['nonce'] = $nonce; + } return $this; } diff --git a/src/Html5/TextContentTags/BlockquoteTag.php b/src/Html5/TextContentTags/BlockquoteTag.php index a3569cc..141e432 100644 --- a/src/Html5/TextContentTags/BlockquoteTag.php +++ b/src/Html5/TextContentTags/BlockquoteTag.php @@ -18,8 +18,11 @@ class BlockquoteTag extends AbstractContainerTag implements FlowContent, Section public function setCite(null|string $cite): static { - if (!$cite) $this->attributes()['cite'] = false; - else $this->attributes()['cite'] = $cite; + if (!$cite) { + $this->attributes()['cite'] = false; + } else { + $this->attributes()['cite'] = $cite; + } return $this; } diff --git a/src/Html5/TextContentTags/OlTag.php b/src/Html5/TextContentTags/OlTag.php index 3c8051e..63afc4c 100644 --- a/src/Html5/TextContentTags/OlTag.php +++ b/src/Html5/TextContentTags/OlTag.php @@ -28,7 +28,7 @@ class OlTag extends AbstractContainerTag implements FlowContent, DisplayBlock public function start(): null|int { - if (isset($this->attributes['start'])) { + if ($this->attributes()['start']) { return intval($this->attributes()->string('start')); } else { return null; @@ -37,8 +37,11 @@ class OlTag extends AbstractContainerTag implements FlowContent, DisplayBlock public function setStart(null|int $start): static { - if (!$start) $this->attributes()['start'] = false; - else $this->attributes()['start'] = strval($start); + if (!$start) { + $this->attributes()['start'] = false; + } else { + $this->attributes()['start'] = strval($start); + } return $this; } diff --git a/src/Tags/AbstractContainerTag.php b/src/Tags/AbstractContainerTag.php index 7618f89..0826d65 100644 --- a/src/Tags/AbstractContainerTag.php +++ b/src/Tags/AbstractContainerTag.php @@ -8,21 +8,25 @@ use ByJoby\HTML\Traits\NodeTrait; abstract class AbstractContainerTag extends AbstractTag implements ContainerTagInterface { - use NodeTrait, TagTrait; + use NodeTrait; + use TagTrait; use ContainerTrait; public function __toString(): string { $openingTag = sprintf('<%s>', implode(' ', $this->openingTagStrings())); $closingTag = sprintf('', $this->tag()); - if (!$this->children()) return $openingTag . $closingTag; - else return implode( - PHP_EOL, - [ + if (!$this->children()) { + return $openingTag . $closingTag; + } else { + return implode( + PHP_EOL, + [ $openingTag, implode(PHP_EOL, $this->children()), $closingTag - ] - ); + ] + ); + } } } diff --git a/src/Tags/AbstractContentTag.php b/src/Tags/AbstractContentTag.php index 93d33c5..d1734b3 100644 --- a/src/Tags/AbstractContentTag.php +++ b/src/Tags/AbstractContentTag.php @@ -25,14 +25,17 @@ abstract class AbstractContentTag extends AbstractTag implements ContentTagInter $openingTag = sprintf('<%s>', implode(' ', $this->openingTagStrings())); $closingTag = sprintf('', $this->tag()); $content = $this->content(); - if (!$content) return $openingTag . $closingTag; - else return implode( - PHP_EOL, - [ + if (!$content) { + return $openingTag . $closingTag; + } else { + return implode( + PHP_EOL, + [ $openingTag, $content, $closingTag - ] - ); + ] + ); + } } } diff --git a/src/Tags/AbstractGroupedTag.php b/src/Tags/AbstractGroupedTag.php index 4188ca3..1a89305 100644 --- a/src/Tags/AbstractGroupedTag.php +++ b/src/Tags/AbstractGroupedTag.php @@ -19,14 +19,17 @@ abstract class AbstractGroupedTag extends AbstractTag implements ContainerTagInt return !!$group->children(); } ); - if (!$groups) return $openingTag . $closingTag; - else return implode( - PHP_EOL, - [ + if (!$groups) { + return $openingTag . $closingTag; + } else { + return implode( + PHP_EOL, + [ $openingTag, implode(PHP_EOL, $groups), $closingTag - ] - ); + ] + ); + } } } diff --git a/src/Tags/AbstractTag.php b/src/Tags/AbstractTag.php index 8ae7706..aed0ed5 100644 --- a/src/Tags/AbstractTag.php +++ b/src/Tags/AbstractTag.php @@ -7,7 +7,8 @@ use ByJoby\HTML\Traits\NodeTrait; abstract class AbstractTag implements TagInterface { - use NodeTrait, TagTrait; + use NodeTrait; + use TagTrait; public function tag(): string { diff --git a/src/Tags/ContainerTagInterface.php b/src/Tags/ContainerTagInterface.php index 6d1af2f..2f7afc7 100644 --- a/src/Tags/ContainerTagInterface.php +++ b/src/Tags/ContainerTagInterface.php @@ -9,7 +9,7 @@ use ByJoby\HTML\ContainerInterface; * child tags. They can all have tags added and removed from them as well. * Container Tags always render as a full opening and closing tag, even when * they are empty. - * + * * @package ByJoby\HTML\Tags */ interface ContainerTagInterface extends TagInterface, ContainerInterface diff --git a/src/Tags/ContentTagInterface.php b/src/Tags/ContentTagInterface.php index 9347f3a..13f4469 100644 --- a/src/Tags/ContentTagInterface.php +++ b/src/Tags/ContentTagInterface.php @@ -8,7 +8,7 @@ use Stringable; * Content Tags contain a single string or Stringable, which may or may not be * valid HTML. They render as full opening/closing HTML tags which wrap the * content stored in the tag. - * + * * @package ByJoby\HTML\Tags */ interface ContentTagInterface extends TagInterface diff --git a/src/Tags/TagInterface.php b/src/Tags/TagInterface.php index ffbcf21..b0884a1 100644 --- a/src/Tags/TagInterface.php +++ b/src/Tags/TagInterface.php @@ -11,7 +11,7 @@ use Stringable; /** * Simple tags represent self-closing tags that cannot contain anything else * within them. - * + * * @package ByJoby\HTML\Tags */ interface TagInterface extends NodeInterface diff --git a/src/Traits/ContainerTrait.php b/src/Traits/ContainerTrait.php index 5d50ff9..2f25ca0 100644 --- a/src/Traits/ContainerTrait.php +++ b/src/Traits/ContainerTrait.php @@ -35,8 +35,11 @@ trait ContainerTrait bool $skip_sanitize = false ): static { $child = $this->prepareChildToAdd($child, $skip_sanitize); - if ($prepend) array_unshift($this->children, $child); - else $this->children[] = $child; + if ($prepend) { + array_unshift($this->children, $child); + } else { + $this->children[] = $child; + } return $this; } @@ -46,8 +49,11 @@ trait ContainerTrait $this->children = array_filter( $this->children, function (NodeInterface $e) use ($child) { - if (is_object($child)) $keep = $e !== $child; - else $keep = $e != $child; + if (is_object($child)) { + $keep = $e !== $child; + } else { + $keep = $e != $child; + } if (!$keep) { $e->setParent(null); } @@ -89,8 +95,11 @@ trait ContainerTrait { // turn strings into nodes if (!($child instanceof NodeInterface)) { - if ($skip_sanitize) $child = new UnsanitizedText($child); - else $child = new Text($child); + if ($skip_sanitize) { + $child = new UnsanitizedText($child); + } else { + $child = new Text($child); + } } // remove from parent, move it here, and return if ($parent = $child->parent()) { @@ -104,11 +113,15 @@ trait ContainerTrait { if ($child instanceof NodeInterface) { foreach ($this->children as $i => $v) { - if ($v === $child) return $i; + if ($v === $child) { + return $i; + } } } else { foreach ($this->children as $i => $v) { - if ($v == $child) return $i; + if ($v == $child) { + return $i; + } } } return null; diff --git a/src/Traits/GroupedContainerTrait.php b/src/Traits/GroupedContainerTrait.php index 0ec88e2..6283895 100644 --- a/src/Traits/GroupedContainerTrait.php +++ b/src/Traits/GroupedContainerTrait.php @@ -21,7 +21,7 @@ trait GroupedContainerTrait } /** - * @return array> + * @return array> */ public function groups(): array { @@ -36,7 +36,9 @@ trait GroupedContainerTrait public function willAccept(NodeInterface|Stringable|string $child): bool { foreach ($this->groups() as $group) { - if ($group->willAccept($child)) return true; + if ($group->willAccept($child)) { + return true; + } } return false; } diff --git a/src/Traits/NodeTrait.php b/src/Traits/NodeTrait.php index 35fd34d..11fc961 100644 --- a/src/Traits/NodeTrait.php +++ b/src/Traits/NodeTrait.php @@ -13,7 +13,7 @@ trait NodeTrait /** @var null|ContainerInterface */ protected $parent; - abstract function __toString(); + abstract public function __toString(); public function parent(): null|ContainerInterface { diff --git a/src/Traits/TagTrait.php b/src/Traits/TagTrait.php index a5371f7..74a4c4f 100644 --- a/src/Traits/TagTrait.php +++ b/src/Traits/TagTrait.php @@ -19,7 +19,7 @@ trait TagTrait /** @var Styles */ protected $styles; - abstract function tag(): string; + abstract public function tag(): string; public function __construct() { @@ -35,15 +35,20 @@ trait TagTrait public function setID(null|string|Stringable $id): static { - if ($id) $this->id = static::sanitizeID($id); - else $this->id = null; + if ($id) { + $this->id = static::sanitizeID($id); + } else { + $this->id = null; + } return $this; } protected static function sanitizeID(string|Stringable $id): string { $id = trim($id); - if (!preg_match('/^[_\-a-z][_\-a-z0-9]*$/i', $id)) throw new Exception('Invalid ID name'); + if (!preg_match('/^[_\-a-z][_\-a-z0-9]*$/i', $id)) { + throw new Exception('Invalid tag ID'); + } return $id; } @@ -73,7 +78,9 @@ trait TagTrait protected function openingTagStrings(): array { $strings = [$this->tag()]; - if ($this->id) $strings[] = sprintf('id="%s"', $this->id); + if ($this->id) { + $strings[] = sprintf('id="%s"', $this->id); + } if ($this->classes()->count()) { $strings[] = sprintf('class="%s"', implode(' ', $this->classes()->getArray())); } diff --git a/tests/Helpers/StylesTest.php b/tests/Helpers/StylesTest.php index 15cd6cc..d7eb6bb 100644 --- a/tests/Helpers/StylesTest.php +++ b/tests/Helpers/StylesTest.php @@ -33,4 +33,29 @@ class StylesTest extends TestCase $styles = new Styles(['a' => 'b', 'b' => 'c']); $this->assertEquals('a:b;b:c', $styles->__toString()); } + + /** + * @depends clone testConstruction + */ + public function testInvalidInputs(Styles $styles): void + { + // null assignments don't work + $styles[] = 'b'; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + // empty attribute doesn't work + $styles[''] = 'b'; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + // attributes that trim to nothing don't work + $styles[' '] = 'b'; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + // empty values don't work + $styles['quux'] = ''; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + // values containing ; don't work + $styles['quux'] = 'x;y'; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + // values containing : don't work + $styles['quux'] = 'x:y'; + $this->assertEquals(['foo' => 'bar'], $styles->getArray()); + } } diff --git a/tests/Html5/Tags/TagTestCase.php b/tests/Html5/Tags/TagTestCase.php index 201503d..77c7718 100644 --- a/tests/Html5/Tags/TagTestCase.php +++ b/tests/Html5/Tags/TagTestCase.php @@ -26,6 +26,10 @@ abstract class TagTestCase extends TestCase // test unsetting via unset call_user_func([$tag, $unsetFn]); $this->assertNull(call_user_func([$tag, $getFn])); + // test setting and unsetting via null value + call_user_func([$tag, $setFn], $test_value); + call_user_func([$tag, $setFn], null); + $this->assertNull(call_user_func([$tag, $getFn])); } protected function assertBooleanAttributeHelperMethods(string $attribute, string $class): void diff --git a/tests/Tags/AbstractGroupedTagTest.php b/tests/Tags/AbstractGroupedTagTest.php new file mode 100644 index 0000000..4c16465 --- /dev/null +++ b/tests/Tags/AbstractGroupedTagTest.php @@ -0,0 +1,21 @@ +getMockForAbstractClass(AbstractGroupedTag::class, [], '', true, true, true, ['tag']); + $tag->method('tag')->willReturn($name); + return $tag; + } + + public function testEmptyRendering(): void + { + $tag = $this->tag('div'); + $this->assertEquals('
', $tag->__toString()); + } +} diff --git a/tests/Tags/AbstractTagTest.php b/tests/Tags/AbstractTagTest.php index f799827..1b81918 100644 --- a/tests/Tags/AbstractTagTest.php +++ b/tests/Tags/AbstractTagTest.php @@ -28,7 +28,7 @@ class AbstractTagTest extends TestCase /** * @depends clone testBR */ - public function testID(AbstractTag $tag): void + public function testID(AbstractTag $tag): AbstractTag { $this->assertNull($tag->id()); $tag->setID('foo'); @@ -37,6 +37,16 @@ class AbstractTagTest extends TestCase $tag->setID(null); $this->assertNull($tag->id()); $this->assertEquals('
', $tag->__toString()); + return $tag; + } + + /** + * @depends clone testID + */ + public function testIDValidation(AbstractTag $tag): void + { + $this->expectExceptionMessage('Invalid tag ID'); + $tag->setID('0abc'); } /**