<?php namespace PortableInfobox\Parser; use Exception; use MediaWiki\Logger\LoggerFactory; use SimpleXMLElement; class XmlParser { protected static $contentTags = [ 'default', 'label', 'format', 'navigation', 'header' ]; /** * @param string $xmlString XML to parse * * @param array &$errors this array will be filled with errors if any found * * @return SimpleXMLElement * @throws XmlMarkupParseErrorException */ public static function parseXmlString( $xmlString, &$errors = [] ) { $global_libxml_setting = libxml_use_internal_errors(); libxml_use_internal_errors( true ); // support for html entities and single & char $xml = simplexml_load_string( self::prepareXml( $xmlString ) ); $errors = libxml_get_errors(); libxml_use_internal_errors( $global_libxml_setting ); if ( $xml === false ) { foreach ( $errors as $xmlerror ) { self::logXmlParseError( $xmlerror->level, $xmlerror->code, trim( $xmlerror->message ) ); } libxml_clear_errors(); throw new XmlMarkupParseErrorException( $errors ); } return $xml; } protected static function logXmlParseError( $level, $code, $message ) { LoggerFactory::getInstance( 'PortableInfobox' )->info( 'PortableInfobox XML Parser problem', [ 'level' => $level, 'code' => $code, 'message' => $message ] ); } /** * @param string $xmlString * * @return mixed */ protected static function prepareXml( $xmlString ) { foreach ( self::$contentTags as $tag ) { // wrap content in CDATA for content tags $xmlString = preg_replace( '|(<' . $tag . '.*>)(.*)(</' . $tag . '>)|sU', '$1<![CDATA[$2]]>$3', $xmlString ); } $decoded = str_replace( '&', '&', html_entity_decode( $xmlString ) ); return $decoded; } } // phpcs:ignore Generic.Files.OneObjectStructurePerFile.MultipleFound class XmlMarkupParseErrorException extends Exception { private $errors; public function __construct( $errors ) { $this->errors = $errors; parent::__construct(); } public function getErrors() { return $this->errors; } }