Dustin Wilson
3 years ago
37 changed files with 506 additions and 123 deletions
@ -0,0 +1,100 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM; |
|||
use MensBeam\Framework\MagicProperties; |
|||
|
|||
|
|||
/** Exists because PHP DOM's DOMNodeList is always live. */ |
|||
class NodeList implements \ArrayAccess, \Countable, \Iterator { |
|||
use MagicProperties; |
|||
|
|||
protected int $_length = 0; |
|||
protected int $position = 0; |
|||
protected array $storage = []; |
|||
|
|||
|
|||
protected function __get_length(): int { |
|||
# The length attribute must return the number of nodes represented by the |
|||
# collection. |
|||
return $this->_length; |
|||
} |
|||
|
|||
|
|||
public function __construct(iterable $iterable) { |
|||
// Per the specification one cannot create a NodeList via its constructor, but |
|||
// this implementation is not going to build up the framework for that. |
|||
|
|||
// Check types while also unpacking the traversable. |
|||
$array = []; |
|||
foreach ($iterable as $i) { |
|||
if (!$i instanceof Node && !$i instanceof \DOMDocumentType) { |
|||
$type = gettype($i); |
|||
if ($type === 'object') { |
|||
$type = get_class($i); |
|||
} |
|||
throw new Exception(Exception::ARGUMENT_TYPE_ERROR, 1, 'traversable', 'Node|\\DOMDocumentType', $type); |
|||
} |
|||
|
|||
$array[] = $i; |
|||
} |
|||
|
|||
$this->storage = $array; |
|||
$this->_length = count($array); |
|||
} |
|||
|
|||
public function count(): int { |
|||
return $this->_length; |
|||
} |
|||
|
|||
public function current(): Node|\DOMDocumentType|null { |
|||
return $this->item($this->position); |
|||
} |
|||
|
|||
public function item(int $index): Node|\DOMDocumentType|null { |
|||
# The item(index) method must return the indexth node in the collection. If |
|||
# there is no indexth node in the collection, then the method must return null. |
|||
if ($index >= $this->_length) { |
|||
return null; |
|||
} |
|||
|
|||
return $this->storage[$index]; |
|||
} |
|||
|
|||
public function key(): int { |
|||
return $this->position; |
|||
} |
|||
|
|||
public function next(): void { |
|||
++$this->position; |
|||
} |
|||
|
|||
public function rewind(): void { |
|||
$this->position = 0; |
|||
} |
|||
|
|||
public function offsetExists($offset): bool { |
|||
return isset($this->storage[$offset]); |
|||
} |
|||
|
|||
public function offsetGet($offset): Node|\DOMDocumentType|null { |
|||
return $this->item($offset); |
|||
} |
|||
|
|||
public function offsetSet($offset, $value): void { |
|||
// NodeLists are immutable |
|||
} |
|||
|
|||
public function offsetUnset($offset): void { |
|||
// Nodelists are immutable |
|||
} |
|||
|
|||
public function valid() { |
|||
return array_key_exists($this->position, $this->storage); |
|||
} |
|||
} |
@ -0,0 +1,103 @@ |
|||
<?php |
|||
/** |
|||
* @license MIT |
|||
* Copyright 2017 Dustin Wilson, J. King, et al. |
|||
* See LICENSE and AUTHORS files for details |
|||
*/ |
|||
|
|||
declare(strict_types=1); |
|||
namespace MensBeam\HTML\DOM\TestCase; |
|||
|
|||
use MensBeam\HTML\DOM\{ |
|||
Document, |
|||
DOMException, |
|||
Exception, |
|||
NodeList |
|||
}; |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\NodeList */ |
|||
class TestNodeList extends \PHPUnit\Framework\TestCase { |
|||
public function provideConstructorFailures(): iterable { |
|||
return [ |
|||
[ function() { |
|||
new NodeList([ 'fail' ]); |
|||
} ], |
|||
[ function() { |
|||
new NodeList([ new \DateTime() ]); |
|||
} ] |
|||
]; |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideConstructorFailures |
|||
* @covers \MensBeam\HTML\DOM\NodeList::__construct |
|||
*/ |
|||
public function testConstructorFailures(\Closure $closure): void { |
|||
$this->expectException(Exception::class); |
|||
$this->expectExceptionCode(Exception::ARGUMENT_TYPE_ERROR); |
|||
$closure(); |
|||
} |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\NodeList::count */ |
|||
public function testCount(): void { |
|||
$d = new Document(); |
|||
$list = new NodeList([ |
|||
$d->createElement('ook'), |
|||
$d->createTextNode('eek'), |
|||
$d->createComment('ack') |
|||
]); |
|||
$this->assertEquals(3, count($list)); |
|||
} |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\NodeList::item */ |
|||
public function testItem(): void { |
|||
$d = new Document(); |
|||
$list = new NodeList([ |
|||
$d->createElement('ook'), |
|||
$d->createTextNode('eek'), |
|||
$d->createComment('ack') |
|||
]); |
|||
$this->assertNull($list->item(42)); |
|||
} |
|||
|
|||
|
|||
/** |
|||
* @covers \MensBeam\HTML\DOM\NodeList::current |
|||
* @covers \MensBeam\HTML\DOM\NodeList::item |
|||
* @covers \MensBeam\HTML\DOM\NodeList::key |
|||
* @covers \MensBeam\HTML\DOM\NodeList::next |
|||
* @covers \MensBeam\HTML\DOM\NodeList::rewind |
|||
* @covers \MensBeam\HTML\DOM\NodeList::offsetExists |
|||
* @covers \MensBeam\HTML\DOM\NodeList::offsetGet |
|||
* @covers \MensBeam\HTML\DOM\NodeList::valid |
|||
*/ |
|||
public function testIteration(): void { |
|||
$d = new Document(); |
|||
$list = new NodeList([ |
|||
$d->createElement('ook'), |
|||
$d->createTextNode('eek'), |
|||
$d->createComment('ack') |
|||
]); |
|||
|
|||
foreach ($list as $key => $node) { |
|||
$this->assertSame($node, $list[$key]); |
|||
// test offsetExists |
|||
$this->assertTrue(isset($list[$key])); |
|||
} |
|||
} |
|||
|
|||
|
|||
/** @covers \MensBeam\HTML\DOM\NodeList::__get_length */ |
|||
public function testPropertyGetLength(): void { |
|||
$d = new Document(); |
|||
$list = new NodeList([ |
|||
$d->createElement('ook'), |
|||
$d->createTextNode('eek'), |
|||
$d->createComment('ack') |
|||
]); |
|||
$this->assertEquals(3, $list->length); |
|||
} |
|||
} |
Loading…
Reference in new issue