%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/vendor/wikimedia/remex-html/RemexHtml/DOM/ |
| Current File : //www/varak.net/wiki.varak.net/vendor/wikimedia/remex-html/RemexHtml/DOM/DOMBuilder.php |
<?php
namespace RemexHtml\DOM;
use RemexHtml\Tokenizer\Attributes;
use RemexHtml\TreeBuilder\Element;
use RemexHtml\TreeBuilder\TreeBuilder;
use RemexHtml\TreeBuilder\TreeHandler;
/**
* A TreeHandler which constructs a DOMDocument
*/
class DOMBuilder implements TreeHandler {
public $doctypeName;
public $public;
public $system;
public $quirks;
private $doc;
private $errorCallback;
private $isFragment;
private $coerced;
/**
* @param callable|null $errorCallback A function which is called on parse errors
*/
public function __construct( $errorCallback = null ) {
$this->errorCallback = $errorCallback;
}
/**
* Get the constructed document or document fragment. In the fragment case,
* a DOMElement is returned, and the caller is expected to extract its
* inner contents, ignoring the wrapping element. This convention is
* convenient because the wrapping element gives libxml somewhere to put
* its namespace declarations. If we copied the children into a
* DOMDocumentFragment, libxml would invent new prefixes for the orphaned
* namespaces.
*
* @return DOMNode
*/
public function getFragment() {
if ( $this->isFragment ) {
return $this->doc->documentElement;
} else {
return $this->doc;
}
}
/**
* Returns true if the document was coerced due to libxml limitations. We
* follow HTML 5.1 ยง 8.2.7 "Coercing an HTML DOM into an infoset".
*
* @return bool
*/
public function isCoerced() {
return $this->coerced;
}
public function startDocument( $fragmentNamespace, $fragmentName ) {
$impl = new \DOMImplementation;
$this->isFragment = $fragmentNamespace !== null;
$this->doc = $this->createDocument();
}
private function createDocument( $doctypeName = null, $public = null, $system = null ) {
$impl = new \DOMImplementation;
if ( $doctypeName === '' ) {
$this->coerced = true;
$doc = $impl->createDocument( null, null );
} elseif ( $doctypeName === null ) {
$doc = $impl->createDocument( null, null );
} else {
$doctype = $impl->createDocumentType( $doctypeName, $public, $system );
$doc = $impl->createDocument( null, null, $doctype );
}
$doc->encoding = 'UTF-8';
return $doc;
}
public function endDocument( $pos ) {
}
private function insertNode( $preposition, $refElement, $node ) {
if ( $preposition === TreeBuilder::ROOT ) {
$parent = $this->doc;
$refNode = null;
} elseif ( $preposition === TreeBuilder::BEFORE ) {
$parent = $refElement->userData->parentNode;
$refNode = $refElement->userData;
} else {
$parent = $refElement->userData;
$refNode = null;
}
$parent->insertBefore( $node, $refNode );
}
/**
* Replace unsupported characters with a code of the form U123456.
*
* @param string $name
* @return string
*/
private function coerceName( $name ) {
$coercedName = DOMUtils::coerceName( $name );
if ( $name !== $coercedName ) {
$this->coerced = true;
}
return $coercedName;
}
private function createNode( Element $element ) {
try {
$node = $this->doc->createElementNS(
$element->namespace,
$element->name );
} catch ( \DOMException $e ) {
// Attempt to escape the name so that it is more acceptable
$node = $this->doc->createElementNS(
$element->namespace,
$this->coerceName( $element->name ) );
}
foreach ( $element->attrs->getObjects() as $attr ) {
if ( $attr->namespaceURI === null
&& strpos( $attr->localName, ':' ) !== false
) {
// FIXME: this apparently works to create a prefixed localName
// in the null namespace, but this is probably taking advantage
// of a bug in PHP's DOM library, and screws up in various
// interesting ways. For example, attributes created in this
// way can't be discovered via hasAttribute() or hasAttributeNS().
$attrNode = $this->doc->createAttribute( $attr->localName );
$attrNode->value = $attr->value;
try {
$node->setAttributeNodeNS( $attrNode );
} catch ( \DOMException $e ) {
$node->setAttributeNS(
$attr->namespaceURI,
$this->coerceName( $attr->qualifiedName ),
$attr->value );
}
} else {
try {
$node->setAttributeNS(
$attr->namespaceURI,
$attr->qualifiedName,
$attr->value );
} catch ( \DOMException $e ) {
$node->setAttributeNS(
$attr->namespaceURI,
$this->coerceName( $attr->qualifiedName ),
$attr->value );
}
}
}
$element->userData = $node;
return $node;
}
public function characters( $preposition, $refElement, $text, $start, $length,
$sourceStart, $sourceLength
) {
$node = $this->doc->createTextNode( substr( $text, $start, $length ) );
$this->insertNode( $preposition, $refElement, $node );
}
public function insertElement( $preposition, $refElement, Element $element, $void,
$sourceStart, $sourceLength
) {
if ( $element->userData ) {
$node = $element->userData;
} else {
$node = $this->createNode( $element );
}
$this->insertNode( $preposition, $refElement, $node );
}
public function endTag( Element $element, $sourceStart, $sourceLength ) {
}
public function doctype( $name, $public, $system, $quirks, $sourceStart, $sourceLength ) {
if ( !$this->doc->firstChild ) {
$impl = $this->doc->implementation;
$this->doc = $this->createDocument( $name, $public, $system );
}
$this->doctypeName = $name;
$this->public = $public;
$this->system = $system;
$this->quirks = $quirks;
}
public function comment( $preposition, $refElement, $text, $sourceStart, $sourceLength ) {
$node = $this->doc->createComment( $text );
$this->insertNode( $preposition, $refElement, $node );
}
public function error( $text, $pos ) {
if ( $this->errorCallback ) {
call_user_func( $this->errorCallback, $text, $pos );
}
}
public function mergeAttributes( Element $element, Attributes $attrs, $sourceStart ) {
$node = $element->userData;
foreach ( $attrs->getObjects() as $name => $attr ) {
if ( $attr->namespaceURI === null
&& strpos( $attr->localName, ':' ) !== false
) {
// As noted in createNode(), we can't use hasAttribute() here.
// However, we can use the return value of setAttributeNodeNS()
// instead.
$attrNode = $this->doc->createAttribute( $attr->localName );
$attrNode->value = $attr->value;
try {
$replaced = $node->setAttributeNodeNS( $attrNode );
} catch ( \DOMException $e ) {
$attrNode = $this->doc->createAttribute(
$this->coerceName( $attr->localName ) );
$attrNode->value = $attr->value;
$replaced = $node->setAttributeNodeNS( $attrNode );
}
if ( $replaced ) {
// Put it back how it was
$node->setAttributeNodeNS( $replaced );
}
} elseif ( $attr->namespaceURI === null ) {
try {
if ( !$node->hasAttribute( $attr->localName ) ) {
$node->setAttribute( $attr->localName, $attr->value );
}
} catch ( \DOMException $e ) {
$name = $this->coerceName( $attr->localName );
if ( !$node->hasAttribute( $name ) ) {
$node->setAttribute( $name, $attr->value );
}
}
} else {
try {
if ( !$node->hasAttributeNS( $attr->namespaceURI, $attr->localName ) ) {
$node->setAttributeNS( $attr->namespaceURI,
$attr->localName, $attr->value );
}
} catch ( \DOMException $e ) {
$name = $this->coerceName( $attr->localName );
if ( !$node->hasAttributeNS( $attr->namespaceURI, $name ) ) {
$node->setAttributeNS( $attr->namespaceURI, $name, $attr->value );
}
}
}
}
}
public function removeNode( Element $element, $sourceStart ) {
$node = $element->userData;
$node->parentNode->removeChild( $node );
}
public function reparentChildren( Element $element, Element $newParent, $sourceStart ) {
$this->insertElement( TreeBuilder::UNDER, $element, $newParent, false, $sourceStart, 0 );
$node = $element->userData;
$newParentNode = $newParent->userData;
while ( $node->firstChild !== $newParentNode ) {
$newParentNode->appendChild( $node->firstChild );
}
}
}