Skip to content

Commit 42a030e

Browse files
committed
Add CfdiUtils\Cleaner\Cleaner::removeIncompleteSchemaLocations
1 parent 52d99a5 commit 42a030e

File tree

10 files changed

+93
-21
lines changed

10 files changed

+93
-21
lines changed

docs/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
- Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments.
1414

1515

16-
## Version 2.7.1 2018-12-03
16+
## Version 2.7.1 2018-12-04
1717

1818
- Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg`
1919
- Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate
@@ -27,6 +27,8 @@
2727
- Add `Config::getServiceUrl`
2828
- Add `Config::wsdlLocation` property
2929
- Add `--local-wsdl` parameter to `tests/estadosat.php` script
30+
- Add a new step on `CfdiUtils\Cleaner\Cleaner` that removes from `xsi:schemaLocations` the namespaces that are
31+
not followed by a string that ends on `.xsd`
3032

3133

3234
## Version 2.7.0 2018-10-19

docs/leer/limpieza-cfdi.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Para evitar estos errores se puede usar el objeto `CfdiUtils\Cleaner\Cleaner`.
2525
Este objeto requiere una cadena de texto con XML válido. Y limpia el XML siguiendo estos pasos:
2626

2727
1. Remueve el nodo `cfdi:Addenda`.
28+
1. Remueve dentro de las locaciones de espacios de nombre `xsi:schemaLocation` los namespaces que no tengan
29+
a continuación una uri que termine en `.xsd`.
2830
1. Remueve todos los nodos que no tengan relación con el SAT (los que no contengan `http://www.sat.gob.mx/`).
2931
1. Remueve todos los pares de espacio de nombre y archivo xsd de los `xsi:schemaLocation` que no tengan relación con el SAT.
3032
1. Remueve todos los espacios de nombres listados que no están en uso.
@@ -38,3 +40,6 @@ También se puede instanciar un objeto de la clase `CfdiUtils\Cleaner\Cleaner` y
3840
- `load(string $content)`: Carga un contenido XML "sucio"
3941
- `clean()`: Realiza la limpieza
4042
- `retrieveXml()`: Obtiene el contenido XML "limpio"
43+
44+
Si deseas implementar tu propio orden, hacer o agregar nuevos limpiadores puedes extender la clase o sobrescribir
45+
el método `clean` o bien llamar a cada uno de los pasos de limpieza por tu propia cuenta.

src/CfdiUtils/Cleaner/Cleaner.php

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public static function isNameSpaceAllowed(string $namespace): bool
8787
public function clean()
8888
{
8989
$this->removeAddenda();
90+
$this->removeIncompleteSchemaLocations();
9091
$this->removeNonSatNSNodes();
9192
$this->removeNonSatNSschemaLocations();
9293
$this->removeUnusedNamespaces();
@@ -157,6 +158,37 @@ public function removeAddenda()
157158
}
158159
}
159160

161+
/**
162+
* Procedure to drop schemaLocations where second part does not ends with '.xsd'
163+
*
164+
* @return void
165+
*/
166+
public function removeIncompleteSchemaLocations()
167+
{
168+
$schemaLocations = $this->obtainXsiSchemaLocations();
169+
for ($s = 0; $s < $schemaLocations->length; $s++) {
170+
$element = $schemaLocations->item($s);
171+
if (null !== $element) {
172+
$element->nodeValue = $this->removeIncompleteSchemaLocation($element->nodeValue);
173+
}
174+
}
175+
}
176+
177+
public function removeIncompleteSchemaLocation(string $source): string
178+
{
179+
$components = array_values(array_filter(array_map('trim', explode(' ', $source))));
180+
$length = count($components);
181+
for ($c = 0; $c < $length; $c = $c + 1) {
182+
$xsd = $components[$c + 1] ?? '';
183+
if ((0 === strcasecmp('.xsd', substr($xsd, -4, 4)))) {
184+
$c = $c + 1;
185+
continue;
186+
}
187+
$components[$c] = '';
188+
}
189+
return strval(implode(' ', array_filter($components)));
190+
}
191+
160192
/**
161193
* Procedure to drop schemaLocations that are not allowed
162194
* If the schemaLocation is empty then remove the attribute
@@ -165,12 +197,7 @@ public function removeAddenda()
165197
*/
166198
public function removeNonSatNSschemaLocations()
167199
{
168-
// Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi"
169-
$xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance');
170-
if (! $xsi) {
171-
return;
172-
}
173-
$schemaLocations = $this->xpathQuery("//@$xsi:schemaLocation");
200+
$schemaLocations = $this->obtainXsiSchemaLocations();
174201
for ($s = 0; $s < $schemaLocations->length; $s++) {
175202
$element = $schemaLocations->item($s);
176203
if (null !== $element) {
@@ -265,6 +292,16 @@ public function removeUnusedNamespaces()
265292
}
266293
}
267294

295+
private function obtainXsiSchemaLocations(): DOMNodeList
296+
{
297+
// Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi"
298+
$xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance');
299+
if (! $xsi) {
300+
return new DOMNodeList();
301+
}
302+
return $this->xpathQuery("//@$xsi:schemaLocation");
303+
}
304+
268305
/**
269306
* Helper function to perform a XPath query using an element (or root element)
270307
* @param string $query

tests/CfdiUtilsTests/Cleaner/CleanerTest.php

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,11 @@ public function testCleanOnDetail()
6868
{
6969
$basefile = $this->utilAsset('cleaner/v32-dirty.xml');
7070
$step1 = $this->utilAsset('cleaner/v32-no-addenda.xml');
71-
$step2 = $this->utilAsset('cleaner/v32-no-nonsat-nodes.xml');
72-
$step3 = $this->utilAsset('cleaner/v32-no-nonsat-schemalocations.xml');
73-
$step4 = $this->utilAsset('cleaner/v32-no-nonsat-xmlns.xml');
74-
foreach ([$basefile, $step1, $step2, $step3, $step4] as $filename) {
71+
$step2 = $this->utilAsset('cleaner/v32-no-incomplete-schemalocations.xml');
72+
$step3 = $this->utilAsset('cleaner/v32-no-nonsat-nodes.xml');
73+
$step4 = $this->utilAsset('cleaner/v32-no-nonsat-schemalocations.xml');
74+
$step5 = $this->utilAsset('cleaner/v32-no-nonsat-xmlns.xml');
75+
foreach ([$basefile, $step1, $step3, $step2, $step4, $step5] as $filename) {
7576
$this->assertFileExists($basefile, "The file $filename for testing does not exists");
7677
}
7778
$cleaner = new Cleaner(file_get_contents($basefile));
@@ -88,29 +89,36 @@ public function testCleanOnDetail()
8889
'Compare that addenda was removed'
8990
);
9091

91-
$cleaner->removeNonSatNSNodes();
92+
$cleaner->removeIncompleteSchemaLocations();
9293
$this->assertXmlStringEqualsXmlFile(
9394
$step2,
9495
$cleaner->retrieveXml(),
96+
'Compare that incomplete schemaLocations were removed'
97+
);
98+
99+
$cleaner->removeNonSatNSNodes();
100+
$this->assertXmlStringEqualsXmlFile(
101+
$step3,
102+
$cleaner->retrieveXml(),
95103
'Compare that non SAT nodes were removed'
96104
);
97105

98106
$cleaner->removeNonSatNSschemaLocations();
99107
$this->assertXmlStringEqualsXmlFile(
100-
$step3,
108+
$step4,
101109
$cleaner->retrieveXml(),
102110
'Compare that non SAT schemaLocations were removed'
103111
);
104112

105113
$cleaner->removeUnusedNamespaces();
106114
$this->assertXmlStringEqualsXmlFile(
107-
$step4,
115+
$step5,
108116
$cleaner->retrieveXml(),
109117
'Compare that xmlns definitions were removed'
110118
);
111119

112120
$this->assertXmlStringEqualsXmlFile(
113-
$step4,
121+
$step5,
114122
Cleaner::staticClean(file_get_contents($basefile)),
115123
'Check static method for cleaning is giving the same results as detailed execution'
116124
);
@@ -161,4 +169,13 @@ public function testRemoveNonSatNSschemaLocationsRemoveEmptySchemaLocation()
161169
$cleaner->removeNonSatNSschemaLocations();
162170
$this->assertXmlStringEqualsXmlString($xmlExpectedContent, $cleaner->retrieveXml());
163171
}
172+
173+
public function testRemoveIncompleteSchemaLocation()
174+
{
175+
// source include spaces to ensure that is working properly
176+
$source = ' bleh foo foo.xsd bar baz zoo zoo.xsd baa xee xee.xsd bah ';
177+
$expected = 'foo foo.xsd zoo zoo.xsd xee xee.xsd';
178+
$cleaner = new Cleaner('');
179+
$this->assertSame($expected, $cleaner->removeIncompleteSchemaLocation($source));
180+
}
164181
}

tests/assets/cleaner/v32-dirty.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xmlns:add="http://example.org/add" xmlns:xyz="http://example.org/xyz"
4-
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://example.org/xyz http://example.org/xsd/xyz.xsd"
4+
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://www.sat.gob.mx/orphan1 http://www.sat.gob.mx/orphan2 http://example.org/xyz http://example.org/xsd/xyz.xsd http://www.sat.gob.mx/orphan3"
55
version="3.2">
66
<cfdi:Addenda>
77
<ext:ElementosExtra xmlns:ext="http://example.com/xsd/ext">

tests/assets/cleaner/v32-no-addenda.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xmlns:add="http://example.org/add" xmlns:xyz="http://example.org/xyz"
4-
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://example.org/xyz http://example.org/xsd/xyz.xsd"
4+
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://www.sat.gob.mx/orphan1 http://www.sat.gob.mx/orphan2 http://example.org/xyz http://example.org/xsd/xyz.xsd http://www.sat.gob.mx/orphan3"
55
version="3.2">
66

77
<cfdi:Complemento>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xmlns:add="http://example.org/add" xmlns:xyz="http://example.org/xyz"
4+
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://example.org/xyz http://example.org/xsd/xyz.xsd"
5+
version="3.2">
6+
7+
<cfdi:Complemento>
8+
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigital.xsd" version="1.0"/>
9+
<add:point x="1" y="2" add:removed="Check if this attributte is removed"/>
10+
</cfdi:Complemento>
11+
</cfdi:Comprobante>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
33
xmlns:add="http://example.org/add" xmlns:xyz="http://example.org/xyz"
4-
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://example.org/xyz http://example.org/xsd/xyz.xsd"
4+
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd http://example.org/add http://example.org/xsd/add.xsd http://example.org/xyz http://example.org/xsd/xyz.xsd"
55
version="3.2">
66

77
<cfdi:Complemento>
88
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigital.xsd" version="1.0"/>
9-
9+
1010
</cfdi:Complemento>
1111
</cfdi:Comprobante>

tests/assets/cleaner/v32-no-nonsat-schemalocations.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
<cfdi:Complemento>
88
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigital.xsd" version="1.0"/>
9-
9+
1010
</cfdi:Complemento>
1111
</cfdi:Comprobante>

tests/assets/cleaner/v32-no-nonsat-xmlns.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
<cfdi:Complemento>
77
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigital.xsd" version="1.0"/>
8-
8+
99
</cfdi:Complemento>
1010
</cfdi:Comprobante>

0 commit comments

Comments
 (0)