Skip to content

Commit

Permalink
Merge pull request #17 from eclipxe13/improve-RemoveUnusedNamespaces
Browse files Browse the repository at this point in the history
Mejorar ejecución de `RemoveUnusedNamespaces` (Versión 1.2.3)
  • Loading branch information
blacktrue committed May 4, 2022
2 parents e278065 + f4cccdc commit 3b9ea47
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 14 deletions.
8 changes: 8 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ Utilizamos [Versionado Semántico 2.0.0](SEMVER.md).

Los cambios no liberados se integran a la rama principal, pero no requieren de la liberación de una nueva versión.

## Versión 1.2.3

La limpieza de CFDI grandes tardaba mucho tiempo en el limpiador `RemoveUnusedNamespaces`.
Se optimizó para que el resultado de la llamada al método privado `isPrefixedNamespaceOnUse` (*puro*)
fuera almacenado en *caché* y así evitar hacer consultas XPath innecesarias.
Después de la optimización, la ejecución de limpieza en un CFDI con más de 2500 conceptos pasó de
180 segundos a menos de 0.5 segundos.

## Versión 1.2.2

Se modifica el limpiador `XmlNsSchemaLocation` para que la limpieza se realice a nivel elemento XML.
Expand Down
53 changes: 39 additions & 14 deletions src/XmlDocumentCleaners/RemoveUnusedNamespaces.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,76 @@ class RemoveUnusedNamespaces implements XmlDocumentCleanerInterface
{
use XmlNamespaceMethodsTrait;

/** @var DOMXPath */
private $xpath;

/** @var array<string, bool> */
private $prefixedNamespaceOnUseCache;

private function setUp(DOMXPath $xpath): void
{
$this->xpath = $xpath;
$this->prefixedNamespaceOnUseCache = [];
}

public function clean(DOMDocument $document): void
{
$xpath = new DOMXPath($document);
$this->setUp(new DOMXPath($document));

foreach ($this->iterateNonReservedNamespaces($document) as $namespaceNode) {
$this->checkNamespaceNode($xpath, $namespaceNode);
$this->checkNamespaceNode($namespaceNode);
}
}

/**
* @param DOMXPath $xpath
* @param DOMNode&object $namespaceNode
*/
private function checkNamespaceNode(DOMXPath $xpath, $namespaceNode): void
private function checkNamespaceNode($namespaceNode): void
{
$namespace = $namespaceNode->nodeValue;
$prefix = ('' !== strval($namespaceNode->prefix)) ? $namespaceNode->prefix . ':' : '';

if (! $this->isPrefixedNamespaceOnUse($xpath, $namespace, $prefix)) {
if (! $this->isPrefixedNamespaceOnUseCached($namespace, $prefix)) {
$this->removeNamespaceNodeAttribute($namespaceNode);
}
}

private function isPrefixedNamespaceOnUse(DOMXPath $xpath, string $namespace, string $prefix): bool
/**
* Function `isPrefixedNamespaceOnUse` is costly, use cache to avoid repetetive calls.
* @see isPrefixedNamespaceOnUse
*/
private function isPrefixedNamespaceOnUseCached(string $namespace, string $prefix): bool
{
$key = sprintf('namespace=%s;prefix=%s', $namespace, $prefix);
if (! array_key_exists($key, $this->prefixedNamespaceOnUseCache)) {
$this->prefixedNamespaceOnUseCache[$key] = $this->isPrefixedNamespaceOnUse($namespace, $prefix);
}
return $this->prefixedNamespaceOnUseCache[$key];
}

private function isPrefixedNamespaceOnUse(string $namespace, string $prefix): bool
{
if ($this->hasElementsOnNamespace($xpath, $namespace, $prefix)) {
if ($this->hasElementsOnNamespace($namespace, $prefix)) {
return true;
}
if ($this->hasAttributesOnNamespace($xpath, $namespace, $prefix)) {
if ($this->hasAttributesOnNamespace($namespace, $prefix)) {
return true;
}
return false;
}

private function hasElementsOnNamespace(DOMXPath $xpath, string $namespace, string $prefix): bool
private function hasElementsOnNamespace(string $namespace, string $prefix): bool
{
$elements = $xpath->query(
sprintf('//*[namespace-uri()="%1$s" and name()=concat("%2$s", local-name())]', $namespace, $prefix),
$elements = $this->xpath->query(
sprintf('(//*[namespace-uri()="%1$s" and name()=concat("%2$s", local-name())])[1]', $namespace, $prefix),
);
return (false !== $elements && $elements->length > 0);
}

private function hasAttributesOnNamespace(DOMXPath $xpath, string $namespace, string $prefix): bool
private function hasAttributesOnNamespace(string $namespace, string $prefix): bool
{
$elements = $xpath->query(
sprintf('//@*[namespace-uri()="%1$s" and name()=concat("%2$s", local-name())]', $namespace, $prefix),
$elements = $this->xpath->query(
sprintf('(//@*[namespace-uri()="%1$s" and name()=concat("%2$s", local-name())])[1]', $namespace, $prefix),
);
return (false !== $elements && $elements->length > 0);
}
Expand Down

0 comments on commit 3b9ea47

Please sign in to comment.