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