DAT-2875 nodes refactoring, xmlParser will be used only for parsing

This commit is contained in:
idradm 2015-06-10 17:19:40 +02:00
parent ed16bffd63
commit 1abbaab514
11 changed files with 273 additions and 249 deletions

View file

@ -31,6 +31,7 @@ $wgInfoboxParserNodes = [
'NodeGroup',
'NodeHeader',
'NodeImage',
'NodeInfobox',
'NodeData',
'NodeTitle',
'NodeUnimplemented'

View file

@ -1,5 +1,7 @@
<?php
use Wikia\PortableInfobox\Parser\Nodes;
class PortableInfoboxParserTagController extends WikiaController {
const PARSER_TAG_NAME = 'infobox';
const INFOBOXES_PROPERTY_NAME = 'infoboxes';
@ -18,6 +20,7 @@ class PortableInfoboxParserTagController extends WikiaController {
if ( !isset( static::$instance ) ) {
static::$instance = new PortableInfoboxParserTagController();
}
return static::$instance;
}
@ -25,20 +28,25 @@ class PortableInfoboxParserTagController extends WikiaController {
* @desc Parser hook: used to register parser tag in MW
*
* @param Parser $parser
*
* @return bool
*/
public static function parserTagInit( Parser $parser ) {
$parser->setHook( self::PARSER_TAG_NAME, [ static::getInstance(), 'renderInfobox' ] );
return true;
}
/**
* Parser hook: used to replace infobox markes put on rendering
*
* @param $text
*
* @return string
*/
public static function replaceInfoboxMarkers( &$parser, &$text ) {
$text = static::getInstance()->replaceMarkers( $text );
return true;
}
@ -46,27 +54,31 @@ class PortableInfoboxParserTagController extends WikiaController {
* @param $markup
* @param Parser $parser
* @param PPFrame $frame
* @param array $params
*
* @return string
* @throws UnimplementedNodeException when node used in markup does not exists
* @throws XmlMarkupParseErrorException xml not well formatted
*/
public function render( $markup, Parser $parser, PPFrame $frame, $params = null ) {
$infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( $this->getFrameParams( $frame ) );
$infoboxParser->setExternalParser( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) );
$infoboxNode = Wikia\PortableInfobox\Parser\Nodes\NodeFactory::newFromXML(
$markup, $this->getFrameParams( $frame ) );
$infoboxNode->setExternalParser( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) );
//get params if not overridden
if ( !isset( $params ) ) {
$params = $infoboxParser->getInfoboxParams( $markup );
$params = ( $infoboxNode instanceof Nodes\NodeInfobox ) ? $infoboxNode->getParams() : [ ];
}
$data = $infoboxParser->getDataFromXmlString( $markup );
$data = $infoboxNode->getData()[ 'value' ];
$data = array_values( array_filter( $data, function ( $item ) {
return !$item[ 'isEmpty' ];
} ) );
//save for later api usage
$this->saveToParserOutput( $parser->getOutput(), $data );
//filter out empty nodes
$render = array_filter( $data, function($item) { return !$item['isEmpty']; } );
$theme = $this->getThemeWithDefault( $params, $frame );
return ( new PortableInfoboxRenderService() )->renderInfobox( $render, $theme );
return ( new PortableInfoboxRenderService() )->renderInfobox( $data, $theme );
}
/**
@ -76,6 +88,7 @@ class PortableInfoboxParserTagController extends WikiaController {
* @param Array $params
* @param Parser $parser
* @param PPFrame $frame
*
* @returns String $html
*/
public function renderInfobox( $text, $params, $parser, $frame ) {
@ -98,6 +111,7 @@ class PortableInfoboxParserTagController extends WikiaController {
$marker = $parser->uniqPrefix() . "-" . self::PARSER_TAG_NAME . "-{$this->markerNumber}\x7f-QINU";
$this->markers[ $marker ] = $renderedValue;
return [ $marker, 'markerType' => 'nowiki' ];
}
@ -115,12 +129,14 @@ class PortableInfoboxParserTagController extends WikiaController {
private function handleError( $message ) {
$renderedValue = '<strong class="error"> ' . $message . '</strong>';
return [ $renderedValue, 'markerType' => 'nowiki' ];
}
private function getThemeWithDefault( $params, PPFrame $frame ) {
$value = isset( $params[ 'theme-source' ] ) ? $frame->getArgument( $params[ 'theme-source' ] ) : false;
$themeName = $this->getThemeName( $params, $value );
//make sure no whitespaces, prevents side effects
return Sanitizer::escapeClass( self::INFOBOX_THEME_PREFIX . preg_replace( '|\s+|s', '-', $themeName ) );
}
@ -133,7 +149,9 @@ class PortableInfoboxParserTagController extends WikiaController {
/**
* Function ensures that arrays are used for merging
*
* @param PPFrame $frame
*
* @return array
*/
protected function getFrameParams( PPFrame $frame ) {

View file

@ -13,7 +13,6 @@ class Node {
const LABEL_TAG_NAME = 'label';
protected $xmlNode;
protected $parent = null;
protected $data = null;
/* @var $externalParser ExternalParser */
@ -24,24 +23,6 @@ class Node {
$this->infoboxData = $infoboxData;
}
/**
* @return mixed
*/
public function getParent() {
return $this->parent;
}
/**
* @param mixed $parent
*/
public function setParent( Node $parent ) {
$this->parent = $parent;
}
public function ignoreNodeWhenEmpty() {
return true;
}
public function getSource() {
$source = $this->getXmlAttribute( $this->xmlNode, self::DATA_SRC_ATTR_NAME );
if ( $this->xmlNode->{self::DEFAULT_TAG_NAME} ) {
@ -66,12 +47,15 @@ class Node {
}
/**
* @param mixed $externalParser
* @param ExternalParser|null $externalParser
*
* @return $this
*/
public function setExternalParser( ExternalParser $externalParser ) {
$this->externalParser = $externalParser;
public function setExternalParser( $externalParser ) {
// we can pass anything, and ignore it if not ExternalParser instance
if ( $externalParser instanceof ExternalParser ) {
$this->externalParser = $externalParser;
}
return $this;
}
@ -104,6 +88,22 @@ class Node {
return !( isset( $data ) ) || ( empty( $data ) && $data != '0' );
}
protected function getChildNodes() {
$result = [ ];
foreach ( $this->xmlNode as $child ) {
$node = NodeFactory::newFromSimpleXml( $child, $this->infoboxData )
->setExternalParser( $this->externalParser );
$result[ ] = [
'type' => $node->getType(),
'data' => $node->getData(),
'isEmpty' => $node->isEmpty(),
'source' => $node->getSource()
];
}
return $result;
}
protected function getValueWithDefault( \SimpleXMLElement $xmlNode ) {
$source = $this->getXmlAttribute( $xmlNode, self::DATA_SRC_ATTR_NAME );
$value = null;

View file

@ -1,17 +1,11 @@
<?php
namespace Wikia\PortableInfobox\Parser\Nodes;
use Wikia\PortableInfobox\Parser\XmlParser;
class NodeComparison extends Node {
public function getData() {
if ( !isset( $this->data ) ) {
$nodeFactory = new XmlParser( $this->infoboxData );
if ( $this->externalParser ) {
$nodeFactory->setExternalParser( $this->externalParser );
}
$this->data = [ 'value' => $nodeFactory->getDataFromNodes( $this->xmlNode, $this ) ];
$this->data = [ 'value' => $this->getChildNodes() ];
}
return $this->data;
@ -27,4 +21,17 @@ class NodeComparison extends Node {
return true;
}
public function getSource() {
$data = $this->getData();
$result = [ ];
foreach ( $data[ 'value' ] as $item ) {
if ( isset( $item[ 'source' ] ) ) {
$source = !is_array( $item[ 'source' ] ) ? [ $item[ 'source' ] ] : $item[ 'source' ];
$result = array_merge( $result, $source );
}
}
return array_unique( $result );
}
}

View file

@ -5,17 +5,17 @@ use Wikia\PortableInfobox\Helpers\SimpleXmlUtil;
class NodeData extends Node {
public function ignoreNodeWhenEmpty() {
$parent = $this->getParent();
if ( $parent instanceof NodeSet ) {
if ( $parent->getParent() instanceof NodeComparison ) {
// data tag inside comparison tag can not be ignored
return false;
}
}
return true;
}
// public function ignoreNodeWhenEmpty() {
// $parent = $this->getParent();
// if ( $parent instanceof NodeSet ) {
// if ( $parent->getParent() instanceof NodeComparison ) {
// // data tag inside comparison tag can not be ignored
// return false;
// }
// }
//
// return true;
// }
public function getData() {
if ( !isset( $this->data ) ) {

View file

@ -9,19 +9,37 @@
namespace Wikia\PortableInfobox\Parser\Nodes;
use Wikia\PortableInfobox\Parser\XmlParser;
class NodeFactory {
public static function newFromSimpleXml( \SimpleXMLElement $xmlNode, array $data = [] ) {
wfProfileIn(__METHOD__);
public static function newFromXML( $text, array $data = [ ] ) {
return self::getInstance( XmlParser::parseXmlString( $text ), $data );
}
public static function newFromSimpleXml( \SimpleXMLElement $xmlNode, array $data = [ ] ) {
return self::getInstance( $xmlNode, $data );
}
/**
* @param \SimpleXMLElement $xmlNode
* @param array $data
*
* @return Node|NodeUnimplemented
*/
protected static function getInstance( \SimpleXMLElement $xmlNode, array $data ) {
wfProfileIn( __METHOD__ );
$tagType = $xmlNode->getName();
$className = 'Wikia\\PortableInfobox\\Parser\\Nodes\\' . 'Node' . ucfirst( strtolower( $tagType ) );
if ( class_exists( $className ) ) {
/* @var $instance \Wikia\PortableInfobox\Parser\Nodes\Node */
$instance = new $className( $xmlNode, $data );
wfProfileOut( __METHOD__ );
return $instance;
}
wfProfileOut( __METHOD__ );
return new NodeUnimplemented( $xmlNode, $data );
}

View file

@ -1,17 +1,11 @@
<?php
namespace Wikia\PortableInfobox\Parser\Nodes;
use Wikia\PortableInfobox\Parser\XmlParser;
class NodeGroup extends Node {
public function getData() {
if ( !isset( $this->data ) ) {
$nodeFactory = new XmlParser( $this->infoboxData );
if ( $this->externalParser ) {
$nodeFactory->setExternalParser( $this->externalParser );
}
$this->data = [ 'value' => $nodeFactory->getDataFromNodes( $this->xmlNode, $this ) ];
$this->data = [ 'value' => $this->getChildNodes() ];
}
return $this->data;
@ -27,4 +21,17 @@ class NodeGroup extends Node {
return true;
}
public function getSource() {
$data = $this->getData();
$result = [ ];
foreach ( $data[ 'value' ] as $item ) {
if ( isset( $item[ 'source' ] ) ) {
$source = !is_array( $item[ 'source' ] ) ? [ $item[ 'source' ] ] : $item[ 'source' ];
$result = array_merge( $result, $source );
}
}
return array_unique( $result );
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Wikia\PortableInfobox\Parser\Nodes;
class NodeInfobox extends Node {
protected $params;
public function getData() {
if ( !isset( $this->data ) ) {
$this->data = [ 'value' => $this->getChildNodes() ];
}
return $this->data;
}
public function isEmpty() {
$data = $this->getData();
foreach ( $data[ 'value' ] as $item ) {
if ( $item[ 'isEmpty' ] == false ) {
return false;
}
}
return true;
}
public function getParams() {
if ( !isset( $this->params ) ) {
$result = [ ];
foreach ( $this->xmlNode->attributes() as $k => $v ) {
$result[ $k ] = (string)$v;
}
$this->params = $result;
}
return $this->params;
}
public function getSource() {
$data = $this->getData();
$result = [ ];
foreach ( $data[ 'value' ] as $item ) {
if ( isset( $item[ 'source' ] ) ) {
$source = !is_array( $item[ 'source' ] ) ? [ $item[ 'source' ] ] : $item[ 'source' ];
$result = array_merge( $result, $source );
}
}
return array_unique( $result );
}
}

View file

@ -1,17 +1,11 @@
<?php
namespace Wikia\PortableInfobox\Parser\Nodes;
use Wikia\PortableInfobox\Parser\XmlParser;
class NodeSet extends Node {
public function getData() {
if ( !isset( $this->data ) ) {
$nodeFactory = new XmlParser( $this->infoboxData );
if ( $this->externalParser ) {
$nodeFactory->setExternalParser( $this->externalParser );
}
$this->data = [ 'value' => $nodeFactory->getDataFromNodes( $this->xmlNode, $this ) ];
$this->data = [ 'value' => $this->getChildNodes() ];
}
return $this->data;
@ -27,4 +21,17 @@ class NodeSet extends Node {
return true;
}
public function getSource() {
$data = $this->getData();
$result = [ ];
foreach ( $data[ 'value' ] as $item ) {
if ( isset( $item[ 'source' ] ) ) {
$source = !is_array( $item[ 'source' ] ) ? [ $item[ 'source' ] ] : $item[ 'source' ];
$result = array_merge( $result, $source );
}
}
return array_unique( $result );
}
}

View file

@ -1,96 +1,18 @@
<?php
namespace Wikia\PortableInfobox\Parser;
use Wikia\Logger\WikiaLogger;
class XmlParser {
protected $infoboxData;
protected $externalParser;
public function __construct( $infoboxData ) {
$this->infoboxData = $infoboxData;
}
/**
* @return mixed
*/
public function getExternalParser() {
return $this->externalParser;
}
/**
* @param mixed $externalParser
*/
public function setExternalParser( ExternalParser $externalParser ) {
$this->externalParser = $externalParser;
}
/**
* @param \SimpleXMLElement $xmlIterable
* @return array
*/
public function getDataFromNodes( \SimpleXMLElement $xmlIterable, $parentNode = null ) {
wfProfileIn(__METHOD__);
$data = [ ];
foreach ( $xmlIterable as $node ) {
$nodeHandler = Nodes\NodeFactory::newFromSimpleXml( $node, $this->infoboxData );
if ( $this->getExternalParser() ) {
$nodeHandler->setExternalParser( $this->getExternalParser() );
}
if ( $parentNode ) {
$nodeHandler->setParent( $parentNode );
}
$nodeData = $nodeHandler->getData();
// add data if node is not empty or - when node can not be ignored when empty
if ( !$nodeHandler->isEmpty() || !$nodeHandler->ignoreNodeWhenEmpty() ) {
$data[ ] = [
'type' => $nodeHandler->getType(),
'data' => $nodeData,
'isEmpty' => $nodeHandler->isEmpty( $nodeData ),
'source' => $nodeHandler->getSource()
];
}
}
wfProfileOut(__METHOD__);
return $data;
}
/**
* @param $xmlString
* @return array
* @throws XmlMarkupParseErrorException
*/
public function getDataFromXmlString( $xmlString ) {
wfProfileIn( __METHOD__ );
$xml = $this->parseXmlString( $xmlString );
$data = $this->getDataFromNodes( $xml );
wfProfileOut( __METHOD__ );
return $data;
}
public function getInfoboxParams( $xmlString ) {
$xml = $this->parseXmlString( $xmlString );
$result = [];
foreach ( $xml->attributes() as $k => $v ) {
$result[$k] = (string) $v;
}
return $result;
}
protected function logXmlParseError( $level, $code, $message ) {
\Wikia\Logger\WikiaLogger::instance()->info( "PortableInfobox XML Parser problem", [
"level" => $level,
"code" => $code,
"message" => $message ] );
}
/**
* @param $xmlString
* @param string $xmlString XML to parse
*
* @param array $errors this array will be filled with errors if any found
*
* @return \SimpleXMLElement
* @throws XmlMarkupParseErrorException
*/
protected function parseXmlString( $xmlString ) {
public static function parseXmlString( $xmlString, &$errors = [ ] ) {
$global_libxml_setting = libxml_use_internal_errors();
libxml_use_internal_errors( true );
$xml = simplexml_load_string( $xmlString );
@ -99,14 +21,22 @@ class XmlParser {
if ( $xml === false ) {
foreach ( $errors as $xmlerror ) {
$this->logXmlParseError( $xmlerror->level, $xmlerror->code, trim( $xmlerror->message ) );
self::logXmlParseError( $xmlerror->level, $xmlerror->code, trim( $xmlerror->message ) );
}
libxml_clear_errors();
throw new XmlMarkupParseErrorException();
}
return $xml;
}
protected static function logXmlParseError( $level, $code, $message ) {
WikiaLogger::instance()->info( "PortableInfobox XML Parser problem", [
"level" => $level,
"code" => $code,
"message" => $message ] );
}
}
class XmlMarkupParseErrorException extends \Exception {

View file

@ -7,12 +7,9 @@ class XmlParserTest extends WikiaBaseTest {
parent::setUp();
}
/*
public function testIsEmpty() {
$parser = new \Wikia\PortableInfobox\Parser\XmlParser( [
'elem2' => 'ELEM2',
'lado2' => 'LALALA',
'nonempty' => '111'
] );
$parser = new \Wikia\PortableInfobox\Parser\XmlParser( );
$markup = '
<infobox>
<comparison>
@ -27,18 +24,18 @@ class XmlParserTest extends WikiaBaseTest {
</infobox>
';
$data = $parser->getDataFromXmlString( $markup );
$this->assertTrue( $data[0]['data']['value'][0]['data']['value'][0]['data']['value'] == 'Combatientes' );
\Wikia\PortableInfobox\Parser\Nodes\NodeFactory::newFromXML( $markup, [
'elem2' => 'ELEM2',
'lado2' => 'LALALA',
'nonempty' => '111'
] );
$this->assertTrue( $data[ 0 ][ 'data' ][ 'value' ][ 0 ][ 'data' ][ 'value' ][ 0 ][ 'data' ][ 'value' ] == 'Combatientes' );
// '111' should be at [1] position, becasue <data source="empty"> should be ommited
$this->assertTrue( $data[1]['data']['value'] == '111' );
$this->assertTrue( $data[ 1 ][ 'data' ][ 'value' ] == '111' );
}
public function testExternalParser() {
$parser = new \Wikia\PortableInfobox\Parser\XmlParser( [
'elem2' => 'ELEM2',
'lado2' => 'LALALA'
] );
$externalParser = new \Wikia\PortableInfobox\Parser\DummyParser();
$parser->setExternalParser( $externalParser );
$markup = '
<infobox>
<title><default>ABB</default></title>
@ -52,22 +49,28 @@ class XmlParserTest extends WikiaBaseTest {
<footer>[[aaa]]</footer>
</infobox>
';
$data = $parser->getDataFromXmlString( $markup );
$xml = \Wikia\PortableInfobox\Parser\XmlParser::parseXmlString( $markup );
$infobox = Wikia\PortableInfobox\Parser\Nodes\NodeFactory::newFromSimpleXml( $xml, [
'elem2' => 'ELEM2',
'lado2' => 'LALALA'
] );
$infobox->setExternalParser( $externalParser );
$data = $infobox->getData()['value'];
$this->assertTrue( $data[0]['data']['value'] == 'parseRecursive(ABB)' );
$this->assertTrue( $data[ 0 ][ 'data' ][ 'value' ] == 'parseRecursive(ABB)' );
// ledo1 ommited, ledo2 at [1] position
$this->assertTrue( $data[1]['data']['value'][0]['data']['value'][2]['data']['value'] == 'parseRecursive(LALALA)' );
$this->assertTrue( $data[ 1 ][ 'data' ][ 'value' ][ 0 ][ 'data' ][ 'value' ][ 2 ][ 'data' ][ 'value' ] == 'parseRecursive(LALALA)' );
}
public function testNotProvidingDataSource() {
$xmlParser = new \Wikia\PortableInfobox\Parser\XmlParser( ['a'=>'1'] );
$xmlParser = new \Wikia\PortableInfobox\Parser\XmlParser( [ 'a' => '1' ] );
$data1 = $xmlParser->getDataFromXmlString('<infobox><data source="b" /><data source="a" /></infobox>');
$this->assertTrue( $data1[0]['data']['value'] == '1', 'Data with empty source "b" should be ignored' );
$data1 = $xmlParser->getDataFromXmlString( '<infobox><data source="b" /><data source="a" /></infobox>' );
$this->assertTrue( $data1[ 0 ][ 'data' ][ 'value' ] == '1', 'Data with empty source "b" should be ignored' );
}
public function testNotProvidingDataSourceInsideComparison() {
$xmlParser = new \Wikia\PortableInfobox\Parser\XmlParser( ['a'=>'1'] );
$xmlParser = new \Wikia\PortableInfobox\Parser\XmlParser( [ 'a' => '1' ] );
$data1 = $xmlParser->getDataFromXmlString( '<infobox>
<comparison>
@ -78,54 +81,35 @@ class XmlParserTest extends WikiaBaseTest {
</comparison>
</infobox>' );
$setElement = $data1[0]['data']['value'][0]['data']['value'];
$setElement = $data1[ 0 ][ 'data' ][ 'value' ][ 0 ][ 'data' ][ 'value' ];
//Data with empty source "b" should not be ignored inside comparison:
$this->assertTrue( $setElement[0]['data']['value'] == '', 'Data with empty source "b" inside comparison' );
$this->assertTrue( $setElement[0]['isEmpty'] == true );
$this->assertTrue( $setElement[ 0 ][ 'data' ][ 'value' ] == '', 'Data with empty source "b" inside comparison' );
$this->assertTrue( $setElement[ 0 ][ 'isEmpty' ] == true );
$this->assertTrue( $setElement[1]['data']['value'] == '1', '' );
$this->assertTrue( $setElement[ 1 ][ 'data' ][ 'value' ] == '1', '' );
}
*/
/**
* @dataProvider errorHandlingDataProvider
*/
public function testErrorHandling( $markup, $expectedErrors ) {
$parser = $this->getMockBuilder( 'Wikia\PortableInfobox\Parser\XmlParser' )
->setConstructorArgs( [ [] ] )
->setMethods( [ 'logXmlParseError' ] )
->getMock();
->setMethods( [ 'logXmlParseError' ] )
->getMock();
$errorCodes = [];
$errorMsgs = [];
// Let's gather error codes and messages that comes to logXmlParseError function:
$parser->expects($this->any())->method( 'logXmlParseError' )->will( $this->returnCallback(
function( $level, $code, $msg ) use ( &$errorCodes, &$errorMsgs ) {
$errorCodes[] = $code;
$errorMsgs[] = $msg;
}
));
$throwsException = false;
// getDataFromXmlString should throw an exception, but we want to proceed in order to check parameters
$errors = [ ];
// parseXmlString should throw an exception, but we want to proceed in order to check parameters
// from logXmlParseError
try {
$data = $parser->getDataFromXmlString( $markup );
$data = $parser->parseXmlString( $markup, $errors );
} catch ( \Wikia\PortableInfobox\Parser\XmlMarkupParseErrorException $e ) {
$throwsException = true;
}
$this->assertTrue( $throwsException );
// lets compare expectedErrors with real errors
foreach ( $expectedErrors as $expectedError ) {
$this->assertTrue( in_array( $expectedError['code'], $errorCodes ) );
$this->assertTrue( in_array( $expectedError['msg'], $errorMsgs ) );
}
$this->assertEquals( $expectedErrors, array_map( function ( LibXMLError $error ) {
return [ 'level' => $error->level, 'code' => $error->code, 'msg' => trim( $error->message ) ];
}, $errors ) );
}
public function errorHandlingDataProvider() {
@ -135,56 +119,57 @@ class XmlParserTest extends WikiaBaseTest {
* http://www.xmlsoft.org/html/libxml-xmlerror.html
*/
return [
[
'<data>d</dat/a>',
[
'<data>d</dat/a>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "expected '>'"],
['level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: data line 1 and dat"],
['level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document"],
]
],
[
'<data> x </data></data>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document"],
]
],
[
'<data> > ddd < a ></data>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 68, 'msg' => "StartTag: invalid element name"],
]
],
[
'<data>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 77, 'msg' => "Premature end of data in tag data line 1"],
]
],
[
'<infobox><data source=caption></infobox>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 39, 'msg' => "AttValue: \" or ' expected"],
['level' => LIBXML_ERR_FATAL, 'code' => 65, 'msg' => "attributes construct error"],
['level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "Couldn't find end of Start Tag data line 1"],
]
],
[
'<infobox><data source="caption"></infobox>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: data line 1 and infobox"],
]
],
[
'<infobox><data source="caption></data></infobox>',
[
['level' => LIBXML_ERR_FATAL, 'code' => 38, 'msg' => "Unescaped '<' not allowed in attributes values"],
['level' => LIBXML_ERR_FATAL, 'code' => 65, 'msg' => "attributes construct error"],
['level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "Couldn't find end of Start Tag data line 1"],
['level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: infobox line 1 and data"],
['level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document"]
]
[ 'level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "expected '>'" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: data line 1 and dat" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document" ],
]
],
[
'<data> x </data></data>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document" ],
]
],
[
'<data> > ddd < a ></data>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 68, 'msg' => "StartTag: invalid element name" ],
]
],
[
'<data>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 77, 'msg' => "Premature end of data in tag data line 1" ],
]
],
[
'<infobox><data source=caption></infobox>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 39, 'msg' => "AttValue: \" or ' expected" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 65, 'msg' => "attributes construct error" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "Couldn't find end of Start Tag data line 1" ],
]
],
[
'<infobox><data source="caption"></infobox>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: data line 1 and infobox" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 77, 'msg' => "Premature end of data in tag infobox line 1" ],
]
],
[
'<infobox><data source="caption></data></infobox>',
[
[ 'level' => LIBXML_ERR_FATAL, 'code' => 38, 'msg' => "Unescaped '<' not allowed in attributes values" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 65, 'msg' => "attributes construct error" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 73, 'msg' => "Couldn't find end of Start Tag data line 1" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 76, 'msg' => "Opening and ending tag mismatch: infobox line 1 and data" ],
[ 'level' => LIBXML_ERR_FATAL, 'code' => 5, 'msg' => "Extra content at the end of the document" ]
]
]
];
}
}