From 5cc3e395c98d815c92c1b1a9fb8171f2832b7e84 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Wed, 24 Oct 2018 11:21:50 -0500 Subject: [PATCH 01/11] Replace use of escapeshellcmd to escapeshellarg --- docs/CHANGELOG.md | 5 +++++ src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 505f944c..fee437dc 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,11 @@ - Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments. +## Version 2.7.1 TO BE RELEASED + +- Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg` + + ## Version 2.7.0 2018-10-19 - Reintroduce `MontoGreaterOrEqualThanSumOfDocuments` as `PAGO30`. diff --git a/src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php b/src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php index 681bbc90..db5cd0e7 100644 --- a/src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php +++ b/src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php @@ -29,7 +29,7 @@ public function createCommand(string $xmlFile, string $xsltLocation): string // if is running on windows then use NUL instead of /dev/null $devnull = (0 === stripos(PHP_OS, 'win')) ? 'NUL' : '/dev/null'; return implode(' ', [ - escapeshellcmd($this->getExecutablePath()), + escapeshellarg($this->getExecutablePath()), escapeshellarg('-s:' . $xmlFile), escapeshellarg('-xsl:' . $xsltLocation), escapeshellarg('-warnings:silent'), // default recover From 02c4944dedff63b4959b89398fa1e59401c09d51 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Wed, 28 Nov 2018 11:44:45 -0600 Subject: [PATCH 02/11] Typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3752363..fb701020 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ By participating in this project and its community, you are expected to uphold t ## Team members * [Carlos C Soto](https://github.com/eclipxe13) - original author and maintainer -* [GitHub constributors](https://github.com/eclipxe13/CfdiUtils/graphs/contributors) +* [GitHub contributors](https://github.com/eclipxe13/CfdiUtils/graphs/contributors) ## Communication Channels From bbbae1a320156eeb988db1a70ae2cba9c108f896 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 19:29:24 -0600 Subject: [PATCH 03/11] Add clean argument to validate script --- docs/CHANGELOG.md | 1 + tests/validate.php | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index fee437dc..1b42b16f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,6 +15,7 @@ ## Version 2.7.1 TO BE RELEASED - Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg` +- Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate ## Version 2.7.0 2018-10-19 diff --git a/tests/validate.php b/tests/validate.php index b38833fc..8527ae33 100644 --- a/tests/validate.php +++ b/tests/validate.php @@ -6,14 +6,19 @@ $files = []; $askForHelp = false; $noCache = false; + $clean = false; foreach ($arguments as $argument) { if (in_array($argument, ['-h', '--help'], true)) { $askForHelp = true; break; // no need to continue with other arguments } + if (in_array($argument, ['-c', '--clean'], true)) { + $clean = true; + continue; + } if ($argument === '--no-cache') { $noCache = true; - break; + continue; } $files[] = $argument; } @@ -21,8 +26,9 @@ if ($askForHelp || ! count($files)) { echo implode(PHP_EOL, [ - basename($command) . ' [-h|--help] [--no-cache] cfdi.xml...', + basename($command) . ' [-h|--help] [-c|--clean] [--no-cache] cfdi.xml...', ' -h, --help Show this help', + ' -c, --clean Clean CFDI before validation', ' --no-cache Tell resolver to not use local cache', " cfdi.xml Files to check, as many as needed, don't allow wilcards", ' WARNING: This program can change at any time! Do not depend on this file or its results!', @@ -35,7 +41,11 @@ $validator->getXmlResolver()->setLocalPath(''); } foreach ($files as $file) { - $asserts = $validator->validateXml((string) file_get_contents($file)); + $xmlContent = strval(file_get_contents($file)); + if ($clean) { + $xmlContent = \CfdiUtils\Cleaner\Cleaner::staticClean($xmlContent); + } + $asserts = $validator->validateXml($xmlContent); print_r(array_filter([ 'file' => $file, 'asserts' => $asserts->count(), From ebf7471ef2d0438cff24e46aa8d8a26d61489dcb Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 19:30:40 -0600 Subject: [PATCH 04/11] Rename argument from complementoAttributes to comprobanteAttributes --- src/CfdiUtils/CfdiCreator33.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CfdiUtils/CfdiCreator33.php b/src/CfdiUtils/CfdiCreator33.php index 41d5a8d7..42004007 100644 --- a/src/CfdiUtils/CfdiCreator33.php +++ b/src/CfdiUtils/CfdiCreator33.php @@ -35,18 +35,18 @@ class CfdiCreator33 implements /** * CfdiCreator33 constructor. - * @param string[] $complementoAttributes + * @param string[] $comprobanteAttributes * @param Certificado|null $certificado * @param XmlResolver|null $xmlResolver * @param XsltBuilderInterface|null $xsltBuilder */ public function __construct( - array $complementoAttributes = [], + array $comprobanteAttributes = [], Certificado $certificado = null, XmlResolver $xmlResolver = null, XsltBuilderInterface $xsltBuilder = null ) { - $this->comprobante = new Comprobante($complementoAttributes); + $this->comprobante = new Comprobante($comprobanteAttributes); $this->setXmlResolver($xmlResolver ? : new XmlResolver()); if (null !== $certificado) { $this->putCertificado($certificado); From 9723bc18db4bcf00d454dd38a91cab531f04a671 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 19:35:12 -0600 Subject: [PATCH 05/11] Ensure that CfdiCreator33::newUsingNode let call putCertificado --- docs/CHANGELOG.md | 2 + src/CfdiUtils/CfdiCreator33.php | 9 ++++- .../CfdiCreatorFromExistentTest.php | 38 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/CfdiUtilsTests/CfdiCreatorFromExistentTest.php diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1b42b16f..54a52c55 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,8 @@ - Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg` - Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate +- Fix `CfdiCreator33::newUsingNode` since not all attributes where correctly imported (`xsi:schemaLocation`) +- Fix calling `CfdiCreator33::putCertificado` on imported cfdi (Emisor child is a `NodeInterface` but not `Emisor`) ## Version 2.7.0 2018-10-19 diff --git a/src/CfdiUtils/CfdiCreator33.php b/src/CfdiUtils/CfdiCreator33.php index 42004007..26de72be 100644 --- a/src/CfdiUtils/CfdiCreator33.php +++ b/src/CfdiUtils/CfdiCreator33.php @@ -59,8 +59,9 @@ public static function newUsingNode( Certificado $certificado = null, XmlResolver $xmlResolver = null ): self { - $new = new self($node->attributes()->exportArray(), $certificado, $xmlResolver); + $new = new self([], $certificado, $xmlResolver); $comprobante = $new->comprobante(); + $comprobante->addAttributes($node->attributes()->exportArray()); foreach ($node as $child) { $comprobante->addChild($child); } @@ -81,7 +82,11 @@ public function putCertificado(Certificado $certificado, bool $putEmisorRfcNombr $this->comprobante['Certificado'] = base64_encode((string) file_get_contents($cerfile)); } if ($putEmisorRfcNombre) { - $this->comprobante->addEmisor([ + $emisor = $this->comprobante->searchNode('cfdi:Emisor'); + if (null === $emisor) { + $emisor = $this->comprobante->getEmisor(); + } + $emisor->addAttributes([ 'Nombre' => $certificado->getName(), 'Rfc' => $certificado->getRfc(), ]); diff --git a/tests/CfdiUtilsTests/CfdiCreatorFromExistentTest.php b/tests/CfdiUtilsTests/CfdiCreatorFromExistentTest.php new file mode 100644 index 00000000..1e972cd5 --- /dev/null +++ b/tests/CfdiUtilsTests/CfdiCreatorFromExistentTest.php @@ -0,0 +1,38 @@ +utilAsset('cfdi33-real.xml')); + $nodeSource = XmlNodeUtils::nodeFromXmlString($xmlSource); + $creator = CfdiCreator33::newUsingNode($nodeSource); + $this->assertXmlStringEqualsXmlString($xmlSource, $creator->asXml()); + } + + public function testNewImportingNode() + { + $xmlSource = file_get_contents($this->utilAsset('cfdi33-real.xml')); + $nodeSource = XmlNodeUtils::nodeFromXmlString($xmlSource); + $creator = CfdiCreator33::newUsingNode($nodeSource); + $this->assertXmlStringEqualsXmlString($xmlSource, $creator->asXml()); + } + + public function testPutCertificadoFromCreatorUsingNode() + { + $xmlSource = file_get_contents($this->utilAsset('cfdi33-real.xml')) ?: ''; + $nodeSource = XmlNodeUtils::nodeFromXmlString($xmlSource); + $creator = CfdiCreator33::newUsingNode($nodeSource); + $creator->putCertificado(new Certificado($this->utilAsset('certs/CSD01_AAA010101AAA.cer')), true); + + $comprobante = $creator->comprobante(); + $this->assertCount(1, $comprobante->searchNodes('cfdi:Emisor')); + $this->assertSame('ACCEM SERVICIOS EMPRESARIALES SC', $comprobante->searchAttribute('cfdi:Emisor', 'Nombre')); + $this->assertSame('AAA010101AAA', $comprobante->searchAttribute('cfdi:Emisor', 'Rfc')); + } +} From 91ac4b30e9ee5ffa2aa3e41769cf31989d8e2900 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 20:31:52 -0600 Subject: [PATCH 06/11] Fix CfdiUtils\ConsultaCfdiSat\WebService to allow wsdl local copy --- docs/CHANGELOG.md | 8 ++++ docs/componentes/estado-sat.md | 19 +++++++++- src/CfdiUtils/ConsultaCfdiSat/Config.php | 38 ++++++++++++++++--- .../ConsultaCFDIServiceSAT.svc.xml | 2 + src/CfdiUtils/ConsultaCfdiSat/WebService.php | 3 +- .../ConsultaCfdiSat/ConfigTest.php | 8 ++-- tests/estadosat.php | 14 +++++-- 7 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 src/CfdiUtils/ConsultaCfdiSat/ConsultaCFDIServiceSAT.svc.xml diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 54a52c55..58d1536c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -18,6 +18,14 @@ - Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate - Fix `CfdiCreator33::newUsingNode` since not all attributes where correctly imported (`xsi:schemaLocation`) - Fix calling `CfdiCreator33::putCertificado` on imported cfdi (Emisor child is a `NodeInterface` but not `Emisor`) +- Refactor `CfdiUtils\ConsultaCfdiSat\WebService` to be able to use a local copy of WSDL file since SAT does not + allow to download it anymore. + - Add `ConsultaCFDIServiceSAT.svc.xml` to namespace folder + - Add `Config::wsdlLocation` static method to get `ConsultaCFDIServiceSAT.svc.xml` file path + - Deprecate `Config::getWsdlUrl` in favor of `Config::getServiceUrl` + - Add `Config::getServiceUrl` + - Add `Config::wsdlLocation` property +- Add `--local-wsdl` parameter to `tests/estadosat.php` script ## Version 2.7.0 2018-10-19 diff --git a/docs/componentes/estado-sat.md b/docs/componentes/estado-sat.md index 8fe2f60b..37d0806e 100644 --- a/docs/componentes/estado-sat.md +++ b/docs/componentes/estado-sat.md @@ -2,7 +2,7 @@ El SAT cuenta con un webservice para consultar el estado de un CFDI. -- Servicio: +- Servicio: - Documentación: Para poderlo consumir se han implementado varias clases dentro del espacio de nonbres `\CfdiUtils\ConsultaCfdiSat`. @@ -136,6 +136,23 @@ $response->getCode(); // S - ... $response->getCfdi(); // Cancelado ``` +## Problema con el webservice del SAT + +El SAT a partir de octubre 2018 dejó de publicar el archivo de contrato WSL del servicio web ubicado en +, sin embargo el servicio +sigue funcionando por lo que ahora es necesario especificar una la copia local del archivo WSDL. + +Si no cuenta con el archivo WSDL esta librería contiene una copia que se puede obtener llamando al método +estático `CfdiUtils\ConsultaCfdiSat\Config::getLocalWsdlLocation()`. + +```php + o diff --git a/src/CfdiUtils/ConsultaCfdiSat/Config.php b/src/CfdiUtils/ConsultaCfdiSat/Config.php index 2de6a4d6..6884b79c 100644 --- a/src/CfdiUtils/ConsultaCfdiSat/Config.php +++ b/src/CfdiUtils/ConsultaCfdiSat/Config.php @@ -12,13 +12,21 @@ class Config private $verifyPeer; /** @var string */ - private $wsdlUrl; + private $serviceUrl; - public function __construct(int $timeout = 10, bool $verifyPeer = true, string $wsdlUrl = '') - { + /** @var string */ + private $wsdlLocation; + + public function __construct( + int $timeout = 10, + bool $verifyPeer = true, + string $serviceUrl = '', + string $wsdlLocation = '' + ) { $this->timeout = $timeout; $this->verifyPeer = $verifyPeer; - $this->wsdlUrl = $wsdlUrl ? : static::DEFAULT_WSDL_URL; + $this->serviceUrl = $serviceUrl ? : static::DEFAULT_WSDL_URL; + $this->wsdlLocation = $wsdlLocation ? : $this->serviceUrl; } public function getTimeout(): int @@ -31,8 +39,28 @@ public function shouldVerifyPeer(): bool return $this->verifyPeer; } + /** + * @deprecated since Version 2.7.1 in favor of getServiceUrl + * @see getServiceUrl + * @return string + */ public function getWsdlUrl(): string { - return $this->wsdlUrl; + return $this->getServiceUrl(); + } + + public function getServiceUrl(): string + { + return $this->serviceUrl; + } + + public function getWsdlLocation(): string + { + return $this->wsdlLocation; + } + + public static function getLocalWsdlLocation(): string + { + return __DIR__ . DIRECTORY_SEPARATOR . 'ConsultaCFDIServiceSAT.svc.xml'; } } diff --git a/src/CfdiUtils/ConsultaCfdiSat/ConsultaCFDIServiceSAT.svc.xml b/src/CfdiUtils/ConsultaCfdiSat/ConsultaCFDIServiceSAT.svc.xml new file mode 100644 index 00000000..3489fc81 --- /dev/null +++ b/src/CfdiUtils/ConsultaCfdiSat/ConsultaCFDIServiceSAT.svc.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/CfdiUtils/ConsultaCfdiSat/WebService.php b/src/CfdiUtils/ConsultaCfdiSat/WebService.php index f0356e8d..6a5cf685 100644 --- a/src/CfdiUtils/ConsultaCfdiSat/WebService.php +++ b/src/CfdiUtils/ConsultaCfdiSat/WebService.php @@ -38,6 +38,7 @@ protected function createSoapClient(): SoapClient { $config = $this->getConfig(); $soapOptions = [ + 'location' => $config->getServiceUrl(), 'soap_version' => SOAP_1_1, 'cache_wsdl' => WSDL_CACHE_NONE, 'exceptions' => 1, @@ -50,7 +51,7 @@ protected function createSoapClient(): SoapClient 'trace' => false, // use this setting for development ]; - return new SoapClient($config->getWsdlUrl(), $soapOptions); + return new SoapClient($config->getWsdlLocation(), $soapOptions); } public function request(RequestParameters $requestParameters): StatusResponse diff --git a/tests/CfdiUtilsTests/ConsultaCfdiSat/ConfigTest.php b/tests/CfdiUtilsTests/ConsultaCfdiSat/ConfigTest.php index dd0804a1..7fb54466 100644 --- a/tests/CfdiUtilsTests/ConsultaCfdiSat/ConfigTest.php +++ b/tests/CfdiUtilsTests/ConsultaCfdiSat/ConfigTest.php @@ -11,14 +11,16 @@ public function testConstructorDefaultValues() $config = new Config(); $this->assertSame(10, $config->getTimeout()); $this->assertSame(true, $config->shouldVerifyPeer()); - $this->assertSame($config::DEFAULT_WSDL_URL, $config->getWsdlUrl()); + $this->assertSame($config::DEFAULT_WSDL_URL, $config->getServiceUrl()); + $this->assertSame($config::DEFAULT_WSDL_URL, $config->getWsdlLocation()); } public function testConstructorWithOtherData() { - $config = new Config(99, false, 'foo'); + $config = new Config(99, false, 'foo', 'bar'); $this->assertSame(99, $config->getTimeout()); $this->assertSame(false, $config->shouldVerifyPeer()); - $this->assertSame('foo', $config->getWsdlUrl()); + $this->assertSame('foo', $config->getServiceUrl()); + $this->assertSame('bar', $config->getWsdlLocation()); } } diff --git a/tests/estadosat.php b/tests/estadosat.php index 2d93f57c..db6150ee 100644 --- a/tests/estadosat.php +++ b/tests/estadosat.php @@ -1,5 +1,6 @@ Date: Mon, 3 Dec 2018 21:45:56 -0600 Subject: [PATCH 07/11] Improve changelog with last changes --- docs/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 58d1536c..e0884457 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,12 +7,13 @@ - `\CfdiUtils\CadenaOrigen\DefaultLocations` - `\CfdiUtils\CadenaOrigen\CadenaOrigenLocations` - Remove `\CfdiUtils\PemPrivateKey\PemPrivateKey::isOpened` to `\CfdiUtils\PemPrivateKey\PemPrivateKey::isOpen` +- Remove `CfdiUtils\ConsultaCfdiSat\Config::getWsdlUrl()` - Remove `static` methods from `\CfdiUtils\CfdiVersion`, instead create an instance of the class - Remove `static` methods from `\CfdiUtils\TimbreFiscalDigital\TfdVersion`, instead create an instance of the class - Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments. -## Version 2.7.1 TO BE RELEASED +## Version 2.7.1 2018-12-03 - Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg` - Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate From 9717ffedf7452b7ebf1907784a0b9709f989e8a5 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 21:55:30 -0600 Subject: [PATCH 08/11] Fix test consuming SAT webservice --- .../ConsultaCfdiSat/WebServiceConsumingTest.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/CfdiUtilsTests/ConsultaCfdiSat/WebServiceConsumingTest.php b/tests/CfdiUtilsTests/ConsultaCfdiSat/WebServiceConsumingTest.php index eb8dd2b8..77e087c6 100644 --- a/tests/CfdiUtilsTests/ConsultaCfdiSat/WebServiceConsumingTest.php +++ b/tests/CfdiUtilsTests/ConsultaCfdiSat/WebServiceConsumingTest.php @@ -20,9 +20,15 @@ */ class WebServiceConsumingTest extends TestCase { + private function createWebServiceObject(): WebService + { + $config = new Config(5, true, '', Config::getLocalWsdlLocation()); + return new WebService($config); + } + private function tolerantRequest(RequestParameters $request): StatusResponse { - $ws = new WebService(); + $ws = $this->createWebServiceObject(); try { return $ws->request($request); } catch (SoapFault $exception) { @@ -43,8 +49,7 @@ private function tolerantSoapClient(WebService $ws): SoapClient public function testGetSoapClient() { - $config = new Config(60, false); - $ws = new WebService($config); + $ws = $this->createWebServiceObject(); $soapClient = $this->tolerantSoapClient($ws); $this->assertSame($soapClient, $this->tolerantSoapClient($ws)); @@ -55,7 +60,7 @@ public function testGetSoapClient() public function testSoapClientHasSettings() { - $config = new Config(60, false); + $config = new Config(60, false, '', Config::getLocalWsdlLocation()); $ws = new WebService($config); $soapClient = $this->tolerantSoapClient($ws); From 102fd4d9410c9fc99495456165e2ec9c65e92dc5 Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 22:00:53 -0600 Subject: [PATCH 09/11] Add PHP 7.3 to build matrix on travis & appveyor --- .appveyor.yml | 1 + .travis.yml | 1 + docs/CHANGELOG.md | 1 + 3 files changed, 3 insertions(+) diff --git a/.appveyor.yml b/.appveyor.yml index 8d5d011f..4de364a8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,6 +17,7 @@ environment: - php: 7.0 - php: 7.1 - php: 7.2 + - php: 7.3 init: - SET PATH=C:\Program Files\OpenSSL;c:\tools\php;C:\tools\composer;%PATH% diff --git a/.travis.yml b/.travis.yml index 9ac66879..39b86aa0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ php: - 7.0 - 7.1 - 7.2 + - 7.3 before_install: - nvm install 8 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e0884457..767c1509 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -27,6 +27,7 @@ - Add `Config::getServiceUrl` - Add `Config::wsdlLocation` property - Add `--local-wsdl` parameter to `tests/estadosat.php` script +- Add PHP 7.3 to Travis and AppVeyor ## Version 2.7.0 2018-10-19 From 52d99a5a17d94a7d1177567ddf373141d3d7cf9f Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Mon, 3 Dec 2018 22:00:53 -0600 Subject: [PATCH 10/11] Revert "Add PHP 7.3 to build matrix on travis & appveyor" This reverts commit 102fd4d9410c9fc99495456165e2ec9c65e92dc5. --- .appveyor.yml | 1 - .travis.yml | 1 - docs/CHANGELOG.md | 1 - 3 files changed, 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4de364a8..8d5d011f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,7 +17,6 @@ environment: - php: 7.0 - php: 7.1 - php: 7.2 - - php: 7.3 init: - SET PATH=C:\Program Files\OpenSSL;c:\tools\php;C:\tools\composer;%PATH% diff --git a/.travis.yml b/.travis.yml index 39b86aa0..9ac66879 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,6 @@ php: - 7.0 - 7.1 - 7.2 - - 7.3 before_install: - nvm install 8 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 767c1509..e0884457 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -27,7 +27,6 @@ - Add `Config::getServiceUrl` - Add `Config::wsdlLocation` property - Add `--local-wsdl` parameter to `tests/estadosat.php` script -- Add PHP 7.3 to Travis and AppVeyor ## Version 2.7.0 2018-10-19 From 42a030e7af613eb5a101da5f7edd848f510ec13b Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Tue, 4 Dec 2018 10:54:51 -0600 Subject: [PATCH 11/11] Add CfdiUtils\Cleaner\Cleaner::removeIncompleteSchemaLocations --- docs/CHANGELOG.md | 4 +- docs/leer/limpieza-cfdi.md | 5 ++ src/CfdiUtils/Cleaner/Cleaner.php | 49 ++++++++++++++++--- tests/CfdiUtilsTests/Cleaner/CleanerTest.php | 33 ++++++++++--- tests/assets/cleaner/v32-dirty.xml | 2 +- tests/assets/cleaner/v32-no-addenda.xml | 2 +- .../v32-no-incomplete-schemalocations.xml | 11 +++++ tests/assets/cleaner/v32-no-nonsat-nodes.xml | 4 +- .../cleaner/v32-no-nonsat-schemalocations.xml | 2 +- tests/assets/cleaner/v32-no-nonsat-xmlns.xml | 2 +- 10 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 tests/assets/cleaner/v32-no-incomplete-schemalocations.xml diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index e0884457..ac9c37f9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,7 +13,7 @@ - Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments. -## Version 2.7.1 2018-12-03 +## Version 2.7.1 2018-12-04 - Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg` - Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate @@ -27,6 +27,8 @@ - Add `Config::getServiceUrl` - Add `Config::wsdlLocation` property - Add `--local-wsdl` parameter to `tests/estadosat.php` script +- Add a new step on `CfdiUtils\Cleaner\Cleaner` that removes from `xsi:schemaLocations` the namespaces that are + not followed by a string that ends on `.xsd` ## Version 2.7.0 2018-10-19 diff --git a/docs/leer/limpieza-cfdi.md b/docs/leer/limpieza-cfdi.md index 3854d87a..aef38676 100644 --- a/docs/leer/limpieza-cfdi.md +++ b/docs/leer/limpieza-cfdi.md @@ -25,6 +25,8 @@ Para evitar estos errores se puede usar el objeto `CfdiUtils\Cleaner\Cleaner`. Este objeto requiere una cadena de texto con XML válido. Y limpia el XML siguiendo estos pasos: 1. Remueve el nodo `cfdi:Addenda`. +1. Remueve dentro de las locaciones de espacios de nombre `xsi:schemaLocation` los namespaces que no tengan + a continuación una uri que termine en `.xsd`. 1. Remueve todos los nodos que no tengan relación con el SAT (los que no contengan `http://www.sat.gob.mx/`). 1. Remueve todos los pares de espacio de nombre y archivo xsd de los `xsi:schemaLocation` que no tengan relación con el SAT. 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 - `load(string $content)`: Carga un contenido XML "sucio" - `clean()`: Realiza la limpieza - `retrieveXml()`: Obtiene el contenido XML "limpio" + +Si deseas implementar tu propio orden, hacer o agregar nuevos limpiadores puedes extender la clase o sobrescribir +el método `clean` o bien llamar a cada uno de los pasos de limpieza por tu propia cuenta. diff --git a/src/CfdiUtils/Cleaner/Cleaner.php b/src/CfdiUtils/Cleaner/Cleaner.php index 05259a6e..3c8762c3 100644 --- a/src/CfdiUtils/Cleaner/Cleaner.php +++ b/src/CfdiUtils/Cleaner/Cleaner.php @@ -87,6 +87,7 @@ public static function isNameSpaceAllowed(string $namespace): bool public function clean() { $this->removeAddenda(); + $this->removeIncompleteSchemaLocations(); $this->removeNonSatNSNodes(); $this->removeNonSatNSschemaLocations(); $this->removeUnusedNamespaces(); @@ -157,6 +158,37 @@ public function removeAddenda() } } + /** + * Procedure to drop schemaLocations where second part does not ends with '.xsd' + * + * @return void + */ + public function removeIncompleteSchemaLocations() + { + $schemaLocations = $this->obtainXsiSchemaLocations(); + for ($s = 0; $s < $schemaLocations->length; $s++) { + $element = $schemaLocations->item($s); + if (null !== $element) { + $element->nodeValue = $this->removeIncompleteSchemaLocation($element->nodeValue); + } + } + } + + public function removeIncompleteSchemaLocation(string $source): string + { + $components = array_values(array_filter(array_map('trim', explode(' ', $source)))); + $length = count($components); + for ($c = 0; $c < $length; $c = $c + 1) { + $xsd = $components[$c + 1] ?? ''; + if ((0 === strcasecmp('.xsd', substr($xsd, -4, 4)))) { + $c = $c + 1; + continue; + } + $components[$c] = ''; + } + return strval(implode(' ', array_filter($components))); + } + /** * Procedure to drop schemaLocations that are not allowed * If the schemaLocation is empty then remove the attribute @@ -165,12 +197,7 @@ public function removeAddenda() */ public function removeNonSatNSschemaLocations() { - // Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi" - $xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance'); - if (! $xsi) { - return; - } - $schemaLocations = $this->xpathQuery("//@$xsi:schemaLocation"); + $schemaLocations = $this->obtainXsiSchemaLocations(); for ($s = 0; $s < $schemaLocations->length; $s++) { $element = $schemaLocations->item($s); if (null !== $element) { @@ -265,6 +292,16 @@ public function removeUnusedNamespaces() } } + private function obtainXsiSchemaLocations(): DOMNodeList + { + // Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi" + $xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance'); + if (! $xsi) { + return new DOMNodeList(); + } + return $this->xpathQuery("//@$xsi:schemaLocation"); + } + /** * Helper function to perform a XPath query using an element (or root element) * @param string $query diff --git a/tests/CfdiUtilsTests/Cleaner/CleanerTest.php b/tests/CfdiUtilsTests/Cleaner/CleanerTest.php index 8edc3f03..b4ed30e8 100644 --- a/tests/CfdiUtilsTests/Cleaner/CleanerTest.php +++ b/tests/CfdiUtilsTests/Cleaner/CleanerTest.php @@ -68,10 +68,11 @@ public function testCleanOnDetail() { $basefile = $this->utilAsset('cleaner/v32-dirty.xml'); $step1 = $this->utilAsset('cleaner/v32-no-addenda.xml'); - $step2 = $this->utilAsset('cleaner/v32-no-nonsat-nodes.xml'); - $step3 = $this->utilAsset('cleaner/v32-no-nonsat-schemalocations.xml'); - $step4 = $this->utilAsset('cleaner/v32-no-nonsat-xmlns.xml'); - foreach ([$basefile, $step1, $step2, $step3, $step4] as $filename) { + $step2 = $this->utilAsset('cleaner/v32-no-incomplete-schemalocations.xml'); + $step3 = $this->utilAsset('cleaner/v32-no-nonsat-nodes.xml'); + $step4 = $this->utilAsset('cleaner/v32-no-nonsat-schemalocations.xml'); + $step5 = $this->utilAsset('cleaner/v32-no-nonsat-xmlns.xml'); + foreach ([$basefile, $step1, $step3, $step2, $step4, $step5] as $filename) { $this->assertFileExists($basefile, "The file $filename for testing does not exists"); } $cleaner = new Cleaner(file_get_contents($basefile)); @@ -88,29 +89,36 @@ public function testCleanOnDetail() 'Compare that addenda was removed' ); - $cleaner->removeNonSatNSNodes(); + $cleaner->removeIncompleteSchemaLocations(); $this->assertXmlStringEqualsXmlFile( $step2, $cleaner->retrieveXml(), + 'Compare that incomplete schemaLocations were removed' + ); + + $cleaner->removeNonSatNSNodes(); + $this->assertXmlStringEqualsXmlFile( + $step3, + $cleaner->retrieveXml(), 'Compare that non SAT nodes were removed' ); $cleaner->removeNonSatNSschemaLocations(); $this->assertXmlStringEqualsXmlFile( - $step3, + $step4, $cleaner->retrieveXml(), 'Compare that non SAT schemaLocations were removed' ); $cleaner->removeUnusedNamespaces(); $this->assertXmlStringEqualsXmlFile( - $step4, + $step5, $cleaner->retrieveXml(), 'Compare that xmlns definitions were removed' ); $this->assertXmlStringEqualsXmlFile( - $step4, + $step5, Cleaner::staticClean(file_get_contents($basefile)), 'Check static method for cleaning is giving the same results as detailed execution' ); @@ -161,4 +169,13 @@ public function testRemoveNonSatNSschemaLocationsRemoveEmptySchemaLocation() $cleaner->removeNonSatNSschemaLocations(); $this->assertXmlStringEqualsXmlString($xmlExpectedContent, $cleaner->retrieveXml()); } + + public function testRemoveIncompleteSchemaLocation() + { + // source include spaces to ensure that is working properly + $source = ' bleh foo foo.xsd bar baz zoo zoo.xsd baa xee xee.xsd bah '; + $expected = 'foo foo.xsd zoo zoo.xsd xee xee.xsd'; + $cleaner = new Cleaner(''); + $this->assertSame($expected, $cleaner->removeIncompleteSchemaLocation($source)); + } } diff --git a/tests/assets/cleaner/v32-dirty.xml b/tests/assets/cleaner/v32-dirty.xml index c9487dec..b4cc4811 100644 --- a/tests/assets/cleaner/v32-dirty.xml +++ b/tests/assets/cleaner/v32-dirty.xml @@ -1,7 +1,7 @@ diff --git a/tests/assets/cleaner/v32-no-addenda.xml b/tests/assets/cleaner/v32-no-addenda.xml index 3046801c..cebb8d78 100644 --- a/tests/assets/cleaner/v32-no-addenda.xml +++ b/tests/assets/cleaner/v32-no-addenda.xml @@ -1,7 +1,7 @@ diff --git a/tests/assets/cleaner/v32-no-incomplete-schemalocations.xml b/tests/assets/cleaner/v32-no-incomplete-schemalocations.xml new file mode 100644 index 00000000..2ded8209 --- /dev/null +++ b/tests/assets/cleaner/v32-no-incomplete-schemalocations.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/tests/assets/cleaner/v32-no-nonsat-nodes.xml b/tests/assets/cleaner/v32-no-nonsat-nodes.xml index c275aaa6..ece97be8 100644 --- a/tests/assets/cleaner/v32-no-nonsat-nodes.xml +++ b/tests/assets/cleaner/v32-no-nonsat-nodes.xml @@ -1,11 +1,11 @@ - + diff --git a/tests/assets/cleaner/v32-no-nonsat-schemalocations.xml b/tests/assets/cleaner/v32-no-nonsat-schemalocations.xml index 543b89ff..2013222c 100644 --- a/tests/assets/cleaner/v32-no-nonsat-schemalocations.xml +++ b/tests/assets/cleaner/v32-no-nonsat-schemalocations.xml @@ -6,6 +6,6 @@ - + diff --git a/tests/assets/cleaner/v32-no-nonsat-xmlns.xml b/tests/assets/cleaner/v32-no-nonsat-xmlns.xml index 66c15fb0..2ea642e4 100644 --- a/tests/assets/cleaner/v32-no-nonsat-xmlns.xml +++ b/tests/assets/cleaner/v32-no-nonsat-xmlns.xml @@ -5,6 +5,6 @@ - +