PortableInfobox/includes/services/Parser/Nodes/Node.php

298 lines
7.8 KiB
PHP
Raw Normal View History

<?php
2022-03-11 20:35:51 +00:00
namespace PortableInfobox\Parser\Nodes;
use PortableInfobox\Parser\ExternalParser;
use PortableInfobox\Parser\SimpleParser;
2022-03-11 20:35:51 +00:00
use Sanitizer;
use SimpleXMLElement;
class Node {
2021-09-10 02:52:19 +00:00
private const NAME_ATTR_NAME = 'name';
private const DEFAULT_TAG_NAME = 'default';
private const FORMAT_TAG_NAME = 'format';
private const EXTRACT_SOURCE_REGEX = '/{{{([^\|}]*?)\|?.*}}}/sU';
2021-09-11 18:55:27 +00:00
protected const DATA_SRC_ATTR_NAME = 'source';
protected const LABEL_TAG_NAME = 'label';
protected $xmlNode;
2021-09-10 02:52:19 +00:00
protected $infoboxData;
2015-06-11 09:34:28 +00:00
protected $children;
2021-09-10 02:52:19 +00:00
protected $cachedSources = null;
protected $data = null;
2015-06-09 13:09:01 +00:00
/**
* @var $externalParser ExternalParser
*/
protected $externalParser;
2022-03-11 20:35:51 +00:00
public function __construct( SimpleXMLElement $xmlNode, $infoboxData ) {
$this->xmlNode = $xmlNode;
$this->infoboxData = $infoboxData;
}
public function getSources() {
2021-09-10 02:52:19 +00:00
if ( $this->cachedSources === null ) {
$this->cachedSources = $this->extractSourcesFromNode( $this->xmlNode );
}
return $this->cachedSources;
}
public function getSourcesMetadata() {
2018-08-16 09:25:53 +00:00
$metadata = [];
$sources = $this->getSources();
$sourcesLength = count( $sources );
2022-03-11 20:35:51 +00:00
$baseLabel = Sanitizer::stripAllTags(
2018-10-02 07:41:19 +00:00
$this->getInnerValue( $this->xmlNode->{self::LABEL_TAG_NAME} )
);
2016-04-01 11:07:30 +00:00
foreach ( $sources as $source ) {
2018-08-16 09:25:53 +00:00
$metadata[$source] = [];
2017-01-02 09:58:02 +00:00
$metadata[$source]['label'] = ( $sourcesLength > 1 ) ?
( !empty( $baseLabel ) ? "{$baseLabel} ({$source})" : '' ) :
$baseLabel;
}
if ( $sourcesLength > 0 && $this->hasPrimarySource( $this->xmlNode ) ) {
2018-10-02 07:41:19 +00:00
// self::extractSourcesFromNode() puts the value of the `source` attribute
// as the first element of $sources
$firstSource = reset( $sources );
2017-01-02 09:58:02 +00:00
$metadata[$firstSource]['primary'] = true;
}
return $metadata;
}
2016-12-09 14:56:57 +00:00
public function getMetadata() {
2017-01-02 09:58:02 +00:00
return [
'type' => $this->getType(),
'sources' => $this->getSourcesMetadata()
];
}
2016-12-09 14:56:57 +00:00
/**
* @return ExternalParser
*/
public function getExternalParser() {
if ( !isset( $this->externalParser ) ) {
$this->setExternalParser( new SimpleParser() );
}
return $this->externalParser;
}
/**
* @param ExternalParser|null $externalParser
*
* @return $this
*/
public function setExternalParser( $externalParser ) {
// we can pass anything, and ignore it if not ExternalParser instance
2015-06-12 13:51:25 +00:00
// we use builder pattern here, for fluently passing external parser to children nodes,
// type hinting was removed to prevent catchable fatal error appearing
if ( $externalParser instanceof ExternalParser ) {
$this->externalParser = $externalParser;
}
return $this;
}
public function getType() {
2015-05-25 13:03:45 +00:00
/*
* Node type generation is based on XML tag name.
* It's worth to remember that SimpleXMLElement::getName method is
* case - sensitive ( "<Data>" != "<data>" ), so we need to sanitize Node Type
* by using mb_strtolower function
2015-05-25 13:03:45 +00:00
*/
return mb_strtolower( $this->xmlNode->getName() );
}
2015-06-11 09:34:28 +00:00
public function isType( $type ) {
return strcmp( $this->getType(), mb_strtolower( $type ) ) == 0;
2015-06-11 09:34:28 +00:00
}
public function getData() {
if ( !isset( $this->data ) ) {
$this->data = [
'value' => (string)$this->xmlNode,
2019-02-02 22:34:48 +00:00
'source' => $this->getPrimarySource(),
'item-name' => $this->getItemName()
];
}
return $this->data;
}
2015-06-11 09:34:28 +00:00
public function getRenderData() {
return [
'type' => $this->getType(),
'data' => $this->getData(),
];
}
2015-05-19 18:14:39 +00:00
/**
2018-10-02 07:41:19 +00:00
* Check if node is empty.
2015-05-19 18:14:39 +00:00
* Note that a '0' value cannot be treated like a null
*
* @return bool
2015-05-19 18:14:39 +00:00
*/
public function isEmpty() {
2017-01-02 09:58:02 +00:00
$data = $this->getData()['value'];
return ( empty( $data ) && $data != '0' );
}
protected function getChildNodes() {
2015-06-11 09:34:28 +00:00
if ( !isset( $this->children ) ) {
2018-08-16 09:25:53 +00:00
$this->children = [];
2015-06-11 09:34:28 +00:00
foreach ( $this->xmlNode as $child ) {
$this->children[] = NodeFactory::newFromSimpleXml( $child, $this->infoboxData )
2015-06-11 09:34:28 +00:00
->setExternalParser( $this->externalParser );
}
}
2015-06-11 09:34:28 +00:00
return $this->children;
}
protected function getDataForChildren() {
return array_map(
2021-09-10 02:52:19 +00:00
static function ( Node $item ) {
return [
'type' => $item->getType(),
'data' => $item->getData(),
'isEmpty' => $item->isEmpty(),
'source' => $item->getSources()
];
},
$this->getChildNodes()
);
2015-06-11 09:34:28 +00:00
}
protected function getRenderDataForChildren() {
2021-09-10 02:52:19 +00:00
$renderData = array_map( static function ( Node $item ) {
2015-06-11 09:34:28 +00:00
return $item->getRenderData();
2021-09-10 02:52:19 +00:00
}, array_filter( $this->getChildNodes(), static function ( Node $item ) {
2015-06-11 09:34:28 +00:00
return !$item->isEmpty();
} ) );
// rebase keys
return array_values( $renderData );
}
protected function getSourcesForChildren() {
/** @var Node $item */
2018-08-16 09:25:53 +00:00
$result = [];
foreach ( $this->getChildNodes() as $item ) {
$result = array_merge( $result, $item->getSources() );
}
2015-08-25 11:00:04 +00:00
$uniqueParams = array_unique( $result );
2015-08-25 11:00:04 +00:00
return array_values( $uniqueParams );
}
2017-01-02 09:58:45 +00:00
protected function getMetadataForChildren() {
2021-09-10 02:52:19 +00:00
return array_map( static function ( Node $item ) {
2017-01-02 09:58:45 +00:00
return $item->getMetadata();
}, $this->getChildNodes() );
}
2022-03-11 20:35:51 +00:00
protected function getValueWithDefault( SimpleXMLElement $xmlNode ) {
$value = $this->extractDataFromSource( $xmlNode );
2018-08-16 14:03:43 +00:00
$isEmpty = $value === null || $value === '';
if ( $isEmpty && $xmlNode->{self::DEFAULT_TAG_NAME} ) {
return $this->getInnerValue( $xmlNode->{self::DEFAULT_TAG_NAME} );
2015-06-22 15:38:47 +00:00
}
2018-08-16 14:03:43 +00:00
if ( !$isEmpty && $xmlNode->{self::FORMAT_TAG_NAME} ) {
return $this->getInnerValue( $xmlNode->{self::FORMAT_TAG_NAME} );
}
return $value;
}
2022-03-11 20:35:51 +00:00
protected function getRawValueWithDefault( SimpleXMLElement $xmlNode ) {
$value = $this->getRawInfoboxData( $this->getXmlAttribute( $xmlNode, self::DATA_SRC_ATTR_NAME ) );
2015-07-01 15:31:37 +00:00
if ( !$value && $xmlNode->{self::DEFAULT_TAG_NAME} ) {
2018-10-02 07:41:19 +00:00
$value = $this->getExternalParser()->replaceVariables(
(string)$xmlNode->{self::DEFAULT_TAG_NAME}
);
}
return $value;
}
2022-03-11 20:35:51 +00:00
protected function getValueWithData( SimpleXMLElement $xmlNode ) {
$value = $this->extractDataFromSource( $xmlNode );
2021-09-10 02:52:19 +00:00
return $value ?: $this->getInnerValue( $xmlNode );
}
2022-03-11 20:35:51 +00:00
protected function getInnerValue( SimpleXMLElement $xmlNode ) {
return $this->getExternalParser()->parseRecursive( (string)$xmlNode );
2015-06-11 12:51:54 +00:00
}
2022-03-11 20:35:51 +00:00
protected function getXmlAttribute( SimpleXMLElement $xmlNode, $attribute ) {
2017-01-02 09:58:02 +00:00
return ( isset( $xmlNode[$attribute] ) ) ? (string)$xmlNode[$attribute]
: null;
}
protected function getRawInfoboxData( $key ) {
2021-09-10 02:52:19 +00:00
return $this->infoboxData[$key] ?? null;
}
protected function getInfoboxData( $key ) {
return $this->getExternalParser()->parseRecursive( $this->getRawInfoboxData( $key ) );
}
/**
2022-03-11 20:35:51 +00:00
* @param SimpleXMLElement $xmlNode
*
* @return mixed
*/
2022-03-11 20:35:51 +00:00
protected function extractDataFromSource( SimpleXMLElement $xmlNode ) {
$source = $this->getXmlAttribute( $xmlNode, self::DATA_SRC_ATTR_NAME );
return ( !empty( $source ) || $source == '0' ) ? $this->getInfoboxData( $source )
: null;
}
/**
2022-03-11 20:35:51 +00:00
* @param SimpleXMLElement $xmlNode
*
* @return array
*/
2022-03-11 20:35:51 +00:00
protected function extractSourcesFromNode( SimpleXMLElement $xmlNode ) {
$sources = $this->hasPrimarySource( $xmlNode ) ?
2018-08-16 09:25:53 +00:00
[ $this->getXmlAttribute( $xmlNode, self::DATA_SRC_ATTR_NAME ) ] : [];
2015-06-23 11:05:25 +00:00
if ( $xmlNode->{self::FORMAT_TAG_NAME} ) {
$sources = $this->matchVariables( $xmlNode->{self::FORMAT_TAG_NAME}, $sources );
2015-06-23 11:05:25 +00:00
}
2015-07-01 15:31:37 +00:00
if ( $xmlNode->{self::DEFAULT_TAG_NAME} ) {
$sources = $this->matchVariables( $xmlNode->{self::DEFAULT_TAG_NAME}, $sources );
}
return $sources;
}
2015-06-23 12:25:37 +00:00
2022-03-11 20:35:51 +00:00
protected function hasPrimarySource( SimpleXMLElement $xmlNode ) {
return (bool)$this->getXmlAttribute( $xmlNode, self::DATA_SRC_ATTR_NAME );
}
2022-03-11 20:35:51 +00:00
protected function matchVariables( SimpleXMLElement $node, array $source ) {
2015-06-23 12:25:37 +00:00
preg_match_all( self::EXTRACT_SOURCE_REGEX, (string)$node, $sources );
2017-01-02 09:58:02 +00:00
return array_unique( array_merge( $source, $sources[1] ) );
2015-06-23 12:25:37 +00:00
}
2019-02-02 22:34:48 +00:00
protected function getPrimarySource() {
return $this->getXmlAttribute( $this->xmlNode, self::DATA_SRC_ATTR_NAME );
}
protected function getItemName() {
return $this->getXmlAttribute( $this->xmlNode, self::NAME_ATTR_NAME );
}
}