Fix shared strings XML Entities auto decode (#411)
When converting an XMLReader node to a SimpleXMLElement, the conversion would automatically decode the XML entities. This resulted in a double decode. For example: """ was converted to """ when imported into a SimpleXMLElement and was again converted into " (quote). This commit changes the way the XLSX Shared Strings file is processed. It also changes the unescaping logic for both XLSX and ODS. Finally, it removes any usage of the SimpleXML library (yay!).
This commit is contained in:
1 parent
1eb01a3d2a
commit
048105461c
17 files changed
+120
-405
No files matched your search
@@ -1,159 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Reader\Wrapper;
|
||||
|
||||
use Box\Spout\Reader\Exception\XMLProcessingException;
|
||||
|
||||
|
||||
/**
|
||||
* Class SimpleXMLElement
|
||||
* Wrapper around the built-in SimpleXMLElement. This class does not extend \SimpleXMLElement
|
||||
* because it its constructor is final... Instead, it is used as a passthrough.
|
||||
* @see \SimpleXMLElement
|
||||
*
|
||||
* @package Box\Spout\Reader\Wrapper
|
||||
*/
|
||||
class SimpleXMLElement
|
||||
{
|
||||
use XMLInternalErrorsHelper;
|
||||
|
||||
/** @var \SimpleXMLElement Instance of the wrapped SimpleXMLElement object */
|
||||
protected $simpleXMLElement;
|
||||
|
||||
/**
|
||||
* Creates a new SimpleXMLElement object
|
||||
* @see \SimpleXMLElement::__construct
|
||||
*
|
||||
* @param string $xmlData A well-formed XML string
|
||||
* @throws \Box\Spout\Reader\Exception\XMLProcessingException If the XML string is not well-formed
|
||||
*/
|
||||
public function __construct($xmlData)
|
||||
{
|
||||
$this->useXMLInternalErrors();
|
||||
|
||||
try {
|
||||
$this->simpleXMLElement = new \SimpleXMLElement($xmlData);
|
||||
} catch (\Exception $exception) {
|
||||
// if the data is invalid, the constructor will throw an Exception
|
||||
$this->resetXMLInternalErrorsSetting();
|
||||
throw new XMLProcessingException($this->getLastXMLErrorMessage());
|
||||
}
|
||||
|
||||
$this->resetXMLInternalErrorsSetting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute for the given name.
|
||||
*
|
||||
* @param string $name Attribute name
|
||||
* @param string|null|void $namespace An optional namespace for the retrieved attributes
|
||||
* @return string|null The attribute value or NULL if attribute not found
|
||||
*/
|
||||
public function getAttribute($name, $namespace = null)
|
||||
{
|
||||
$isPrefix = ($namespace !== null);
|
||||
$attributes = $this->simpleXMLElement->attributes($namespace, $isPrefix);
|
||||
$attributeValue = $attributes->{$name};
|
||||
|
||||
return ($attributeValue !== null) ? (string) $attributeValue : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a prefix/ns context for the next XPath query
|
||||
* @see \SimpleXMLElement::registerXPathNamespace
|
||||
*
|
||||
* @param string $prefix The namespace prefix to use in the XPath query for the namespace given in "namespace".
|
||||
* @param string $namespace The namespace to use for the XPath query. This must match a namespace in
|
||||
* use by the XML document or the XPath query using "prefix" will not return any results.
|
||||
* @return bool TRUE on success or FALSE on failure.
|
||||
*/
|
||||
public function registerXPathNamespace($prefix, $namespace)
|
||||
{
|
||||
return $this->simpleXMLElement->registerXPathNamespace($prefix, $namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs XPath query on XML data
|
||||
* @see \SimpleXMLElement::xpath
|
||||
*
|
||||
* @param string $path An XPath path
|
||||
* @return SimpleXMLElement[]|bool an array of SimpleXMLElement objects or FALSE in case of an error.
|
||||
*/
|
||||
public function xpath($path)
|
||||
{
|
||||
$elements = $this->simpleXMLElement->xpath($path);
|
||||
|
||||
if ($elements !== false) {
|
||||
$wrappedElements = [];
|
||||
foreach ($elements as $element) {
|
||||
$wrappedElement = $this->wrapSimpleXMLElement($element);
|
||||
|
||||
if ($wrappedElement !== null) {
|
||||
$wrappedElements[] = $this->wrapSimpleXMLElement($element);
|
||||
}
|
||||
}
|
||||
|
||||
$elements = $wrappedElements;
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given element into an instance of the wrapper
|
||||
*
|
||||
* @param \SimpleXMLElement $element Element to be wrapped
|
||||
* @return SimpleXMLElement|null The wrapped element or NULL if the given element is invalid
|
||||
*/
|
||||
protected function wrapSimpleXMLElement(\SimpleXMLElement $element)
|
||||
{
|
||||
$wrappedElement = null;
|
||||
$elementAsXML = $element->asXML();
|
||||
|
||||
if ($elementAsXML !== false) {
|
||||
$wrappedElement = new SimpleXMLElement($elementAsXML);
|
||||
}
|
||||
|
||||
return $wrappedElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all nodes matching the given XPath query.
|
||||
* It does not map to any \SimpleXMLElement function.
|
||||
*
|
||||
* @param string $path An XPath path
|
||||
* @return void
|
||||
*/
|
||||
public function removeNodesMatchingXPath($path)
|
||||
{
|
||||
$nodesToRemove = $this->simpleXMLElement->xpath($path);
|
||||
|
||||
foreach ($nodesToRemove as $nodeToRemove) {
|
||||
unset($nodeToRemove[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first child matching the given tag name
|
||||
*
|
||||
* @param string $tagName
|
||||
* @return SimpleXMLElement|null The first child matching the tag name or NULL if none found
|
||||
*/
|
||||
public function getFirstChildByTagName($tagName)
|
||||
{
|
||||
$doesElementExist = isset($this->simpleXMLElement->{$tagName});
|
||||
|
||||
/** @var \SimpleXMLElement $realElement */
|
||||
$realElement = $this->simpleXMLElement->{$tagName};
|
||||
|
||||
return $doesElementExist ? $this->wrapSimpleXMLElement($realElement) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->simpleXMLElement->__toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Reader\Wrapper;
|
||||
use DOMNode;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user