reorganized html5 parsing, added some tags

This commit is contained in:
Joby 2022-12-16 11:58:23 -07:00
parent 049d847fa9
commit 67763ada8f
34 changed files with 217 additions and 159 deletions

View file

@ -20,13 +20,10 @@ use DOMElement;
use DOMNode;
use DOMText;
class Parser
abstract class AbstractParser
{
/** @var array<int,string> */
protected $tag_namespaces = [
'\\ByJoby\\HTML\\Html5\\Tags\\',
'\\ByJoby\\HTML\\Containers\\DocumentTags\\'
];
protected $tag_namespaces = [];
/** @var array<string,class-string<TagInterface>> */
protected $tag_classes = [];
@ -41,7 +38,7 @@ class Parser
protected $cdata_class = CData::class;
/** @var class-string<HtmlDocumentInterface> */
protected $document_class = GenericHtmlDocument::class;
protected $document_class;
/** @var class-string<FragmentInterface> */
protected $fragment_class = Fragment::class;

View file

@ -1,64 +0,0 @@
<?php
namespace ByJoby\HTML\Containers;
use ByJoby\HTML\Containers\DocumentTags\BodyTagInterface;
use ByJoby\HTML\Containers\DocumentTags\Doctype;
use ByJoby\HTML\Containers\DocumentTags\DoctypeInterface;
use ByJoby\HTML\Containers\DocumentTags\HeadTagInterface;
use ByJoby\HTML\Containers\DocumentTags\HtmlTag;
use ByJoby\HTML\Containers\DocumentTags\HtmlTagInterface;
use ByJoby\HTML\Traits\GroupedContainerTrait;
class GenericHtmlDocument implements HtmlDocumentInterface
{
use GroupedContainerTrait;
/** @var ContainerGroup<DoctypeInterface> */
protected $doctype;
/** @var ContainerGroup<HtmlTagInterface> */
protected $html;
public function __construct()
{
$this->doctype = ContainerGroup::ofClass(DoctypeInterface::class, 1);
$this->html = ContainerGroup::ofClass(HtmlTagInterface::class, 1);
$this->addGroup($this->doctype);
$this->addGroup($this->html);
$this->addChild(new Doctype);
$this->addChild(new HtmlTag);
}
public function doctype(): DoctypeInterface
{
return $this->doctype->children()[0];
}
public function html(): HtmlTagInterface
{
return $this->html->children()[0];
}
public function head(): HeadTagInterface
{
return $this->html()->head();
}
public function body(): BodyTagInterface
{
return $this->html()->body();
}
public function __toString(): string
{
return implode(
PHP_EOL,
array_filter(
$this->groups(),
function (ContainerGroup $group) {
return !!$group->children();
}
)
);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace ByJoby\HTML\Html5\AbstractTags;
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\HeadingContent;
use ByJoby\HTML\Tags\AbstractContainerTag;

View file

@ -0,0 +1,12 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class AddressTag extends AbstractContainerTag implements DisplayBlock, FlowContent
{
const TAG = 'address';
}

View file

@ -1,12 +1,13 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\ContentCategories\SectioningContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class ArticleTag extends AbstractContainerTag implements DisplayBlock, SectioningContent
class ArticleTag extends AbstractContainerTag implements DisplayBlock, FlowContent, SectioningContent
{
const TAG = 'article';
}

View file

@ -1,12 +1,13 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\ContentCategories\SectioningContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class AsideTag extends AbstractContainerTag implements DisplayBlock, SectioningContent
class AsideTag extends AbstractContainerTag implements DisplayBlock, FlowContent, SectioningContent
{
const TAG = 'aside';
}

View file

@ -0,0 +1,12 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class FooterTag extends AbstractContainerTag implements DisplayBlock, FlowContent
{
const TAG = 'footer';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H1Tag extends AbstractHeaderTag
{
const TAG = 'h1';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H2Tag extends AbstractHeaderTag
{
const TAG = 'h1';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H3Tag extends AbstractHeaderTag
{
const TAG = 'h3';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H4Tag extends AbstractHeaderTag
{
const TAG = 'h4';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H5Tag extends AbstractHeaderTag
{
const TAG = 'h5';
}

View file

@ -0,0 +1,8 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
class H6Tag extends AbstractHeaderTag
{
const TAG = 'h6';
}

View file

@ -0,0 +1,12 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class HeaderTag extends AbstractContainerTag implements DisplayBlock, FlowContent
{
const TAG = 'header';
}

View file

@ -0,0 +1,12 @@
<?php
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class MainTag extends AbstractContainerTag implements DisplayBlock, FlowContent
{
const TAG = 'main';
}

View file

@ -1,12 +1,13 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\ContentCategories\SectioningContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class NavTag extends AbstractContainerTag implements DisplayBlock, SectioningContent
class NavTag extends AbstractContainerTag implements DisplayBlock, FlowContent, SectioningContent
{
const TAG = 'nav';
}

View file

@ -1,12 +1,13 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
namespace ByJoby\HTML\Html5\ContentSectioningTags;
use ByJoby\HTML\ContentCategories\FlowContent;
use ByJoby\HTML\ContentCategories\SectioningContent;
use ByJoby\HTML\DisplayTypes\DisplayBlock;
use ByJoby\HTML\Tags\AbstractContainerTag;
class SectionTag extends AbstractContainerTag implements DisplayBlock, SectioningContent
class SectionTag extends AbstractContainerTag implements DisplayBlock, FlowContent, SectioningContent
{
const TAG = 'section';
}

View file

@ -1,7 +1,8 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\DocumentTags\BodyTagInterface;
use ByJoby\HTML\Tags\AbstractContainerTag;
class BodyTag extends AbstractContainerTag implements BodyTagInterface

View file

@ -1,7 +1,8 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\DocumentTags\DoctypeInterface;
use ByJoby\HTML\Traits\NodeTrait;
class Doctype implements DoctypeInterface

View file

@ -1,8 +1,10 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\ContainerGroup;
use ByJoby\HTML\Containers\DocumentTags\HeadTagInterface;
use ByJoby\HTML\Containers\DocumentTags\TitleTagInterface;
use ByJoby\HTML\Tags\AbstractGroupedTag;
use ByJoby\HTML\Traits\GroupedContainerTrait;

View file

@ -1,8 +1,11 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\ContainerGroup;
use ByJoby\HTML\Containers\DocumentTags\BodyTagInterface;
use ByJoby\HTML\Containers\DocumentTags\HeadTagInterface;
use ByJoby\HTML\Containers\DocumentTags\HtmlTagInterface;
use ByJoby\HTML\Tags\AbstractGroupedTag;
use ByJoby\HTML\Traits\GroupedContainerTrait;

View file

@ -1,7 +1,8 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\DocumentTags\TitleTagInterface;
use ByJoby\HTML\Tags\AbstractContentTag;
use Stringable;

View file

@ -2,8 +2,65 @@
namespace ByJoby\HTML\Html5;
use ByJoby\HTML\Containers\GenericHtmlDocument;
use ByJoby\HTML\Containers\ContainerGroup;
use ByJoby\HTML\Containers\DocumentTags\BodyTagInterface;
use ByJoby\HTML\Containers\DocumentTags\DoctypeInterface;
use ByJoby\HTML\Containers\DocumentTags\HeadTagInterface;
use ByJoby\HTML\Containers\DocumentTags\HtmlTagInterface;
use ByJoby\HTML\Containers\HtmlDocumentInterface;
use ByJoby\HTML\Html5\DocumentTags\Doctype;
use ByJoby\HTML\Html5\DocumentTags\HtmlTag;
use ByJoby\HTML\Traits\GroupedContainerTrait;
class Html5Document extends GenericHtmlDocument
class Html5Document implements HtmlDocumentInterface
{
use GroupedContainerTrait;
/** @var ContainerGroup<DoctypeInterface> */
protected $doctype;
/** @var ContainerGroup<HtmlTagInterface> */
protected $html;
public function __construct()
{
$this->doctype = ContainerGroup::ofClass(DoctypeInterface::class, 1);
$this->html = ContainerGroup::ofClass(HtmlTagInterface::class, 1);
$this->addGroup($this->doctype);
$this->addGroup($this->html);
$this->addChild(new Doctype);
$this->addChild(new HtmlTag);
}
public function doctype(): DoctypeInterface
{
return $this->doctype->children()[0];
}
public function html(): HtmlTagInterface
{
return $this->html->children()[0];
}
public function head(): HeadTagInterface
{
return $this->html()->head();
}
public function body(): BodyTagInterface
{
return $this->html()->body();
}
public function __toString(): string
{
return implode(
PHP_EOL,
array_filter(
$this->groups(),
function (ContainerGroup $group) {
return !!$group->children();
}
)
);
}
}

19
src/Html5/Html5Parser.php Normal file
View file

@ -0,0 +1,19 @@
<?php
namespace ByJoby\HTML\Html5;
use ByJoby\HTML\AbstractParser;
use ByJoby\HTML\Containers\HtmlDocumentInterface;
class Html5Parser extends AbstractParser
{
/** @var array<int,string> */
protected $tag_namespaces = [
'\\ByJoby\\HTML\\Html5\\Tags\\',
'\\ByJoby\\HTML\\Html5\\ContentSectioningTags\\',
'\\ByJoby\\HTML\\Html5\\DocumentTags\\',
];
/** @var class-string<HtmlDocumentInterface> */
protected $document_class = Html5Document::class;
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H1Tag extends AbstractHeaderTag
{
const TAG = 'h1';
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H2Tag extends AbstractHeaderTag
{
const TAG = 'h1';
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H3Tag extends AbstractHeaderTag
{
const TAG = 'h3';
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H4Tag extends AbstractHeaderTag
{
const TAG = 'h4';
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H5Tag extends AbstractHeaderTag
{
const TAG = 'h5';
}

View file

@ -1,10 +0,0 @@
<?php
namespace ByJoby\HTML\Html5\Tags;
use ByJoby\HTML\Html5\AbstractTags\AbstractHeaderTag;
class H6Tag extends AbstractHeaderTag
{
const TAG = 'h6';
}

View file

@ -1,7 +1,8 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use ByJoby\HTML\Containers\DocumentTags\TitleTagInterface;
use PHPUnit\Framework\TestCase;
class HeadTagTest extends TestCase

View file

@ -1,6 +1,6 @@
<?php
namespace ByJoby\HTML\Containers\DocumentTags;
namespace ByJoby\HTML\Html5\DocumentTags;
use PHPUnit\Framework\TestCase;

View file

@ -1,6 +1,6 @@
<?php
namespace ByJoby\HTML\Containers;
namespace ByJoby\HTML\Html5;
use ByJoby\HTML\Containers\DocumentTags\BodyTagInterface;
use ByJoby\HTML\Containers\DocumentTags\DoctypeInterface;
@ -8,11 +8,11 @@ use ByJoby\HTML\Containers\DocumentTags\HeadTagInterface;
use ByJoby\HTML\Containers\DocumentTags\HtmlTagInterface;
use PHPUnit\Framework\TestCase;
class GenericHtmlDocumentTest extends TestCase
class Html5DocumentTest extends TestCase
{
public function testConstruction(): void
{
$document = new GenericHtmlDocument;
$document = new Html5Document;
// all the right classes
$this->assertInstanceOf(DoctypeInterface::class, $document->doctype());
$this->assertInstanceOf(HtmlTagInterface::class, $document->html());

View file

@ -1,16 +1,16 @@
<?php
namespace ByJoby\HTML;
namespace ByJoby\HTML\Html5;
use ByJoby\HTML\Html5\Tags\DivTag;
use ByJoby\HTML\Nodes\TextInterface;
use PHPUnit\Framework\TestCase;
class ParserTest extends TestCase
class Html5ParserTest extends TestCase
{
public function testFragmentRootTextNotWrapped()
{
$parser = new Parser();
$parser = new Html5Parser();
$fragment = $parser->parseFragment('foobar');
$this->assertInstanceOf(TextInterface::class, $fragment->children()[0]);
$fragment = $parser->parseFragment('foobar<div>fizzbuzz</div>');
@ -20,7 +20,7 @@ class ParserTest extends TestCase
public function testAttributes()
{
$parser = new Parser();
$parser = new Html5Parser();
$fragment = $parser->parseFragment('<div id="foo" a="b" c="d" f></div>');
$this->assertEquals('foo', $fragment->children()[0]->id());
$this->assertEquals('b', $fragment->children()[0]->attributes()['a']);
@ -29,7 +29,7 @@ class ParserTest extends TestCase
public function testStylesAndClasses()
{
$parser = new Parser();
$parser = new Html5Parser();
$fragment = $parser->parseFragment('<div class="foo bar " style=" color:red; background-color: blue;"></div>');
$this->assertEquals(['bar', 'foo'], $fragment->children()[0]->classes()->getArray());
$this->assertEquals(['background-color' => 'blue', 'color' => 'red'], $fragment->children()[0]->styles()->getArray());
@ -37,7 +37,7 @@ class ParserTest extends TestCase
public function testNesting()
{
$parser = new Parser();
$parser = new Html5Parser();
$fragment = $parser->parseFragment('<div><p>foo<!-- comment -->bar</p><p>foo</p></div>');
$this->assertInstanceOf(DivTag::class, $fragment->children()[0]);
$this->assertCount(2, $fragment->children()[0]->children());
@ -46,14 +46,14 @@ class ParserTest extends TestCase
public function testUnknownTags()
{
$parser = new Parser();
$parser = new Html5Parser();
$fragment = $parser->parseFragment('<div></div><derp><darp>');
$this->assertCount(1, $fragment->children());
}
public function testParseDocument()
{
$parser = new Parser();
$parser = new Html5Parser();
$document = $parser->parseDocument('<html><head><title>Title</title></head><body><div>foo</div></body></html>');
$this->assertEquals('Title', $document->html()->head()->title()->content());
$this->assertEquals('<div>' . PHP_EOL . 'foo' . PHP_EOL . '</div>', $document->body()->children()[0]->__toString());