1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace CfdiUtils\Retenciones; |
4
|
|
|
|
5
|
|
|
use CfdiUtils\CadenaOrigen\DOMBuilder; |
6
|
|
|
use CfdiUtils\CadenaOrigen\XsltBuilderInterface; |
7
|
|
|
use CfdiUtils\CadenaOrigen\XsltBuilderPropertyInterface; |
8
|
|
|
use CfdiUtils\CadenaOrigen\XsltBuilderPropertyTrait; |
9
|
|
|
use CfdiUtils\Certificado\Certificado; |
10
|
|
|
use CfdiUtils\Certificado\CertificadoPropertyInterface; |
11
|
|
|
use CfdiUtils\Certificado\CertificadoPropertyTrait; |
12
|
|
|
use CfdiUtils\Elements\Retenciones10\Retenciones; |
|
|
|
|
13
|
|
|
use CfdiUtils\Nodes\XmlNodeUtils; |
14
|
|
|
use CfdiUtils\PemPrivateKey\PemPrivateKey; |
15
|
|
|
use CfdiUtils\Validate\Asserts; |
16
|
|
|
use CfdiUtils\Validate\Cfdi33\Xml\XmlFollowSchema; |
17
|
|
|
use CfdiUtils\XmlResolver\XmlResolver; |
18
|
|
|
use CfdiUtils\XmlResolver\XmlResolverPropertyInterface; |
19
|
|
|
use CfdiUtils\XmlResolver\XmlResolverPropertyTrait; |
20
|
|
|
|
21
|
|
|
class RetencionesCreator10 implements |
22
|
|
|
CertificadoPropertyInterface, |
23
|
|
|
XmlResolverPropertyInterface, |
24
|
|
|
XsltBuilderPropertyInterface |
25
|
|
|
{ |
26
|
|
|
use CertificadoPropertyTrait; |
27
|
|
|
use XmlResolverPropertyTrait; |
28
|
|
|
use XsltBuilderPropertyTrait; |
29
|
|
|
|
30
|
|
|
/** @var Retenciones */ |
31
|
|
|
private $retenciones; |
32
|
|
|
|
33
|
4 |
|
public function __construct( |
34
|
|
|
array $comprobanteAttributes = [], |
35
|
|
|
XmlResolver $xmlResolver = null, |
36
|
|
|
XsltBuilderInterface $xsltBuilder = null |
37
|
|
|
) { |
38
|
4 |
|
$this->retenciones = new Retenciones($comprobanteAttributes); |
39
|
4 |
|
$this->setXmlResolver($xmlResolver ? : new XmlResolver()); |
40
|
4 |
|
$this->setXsltBuilder($xsltBuilder ? : new DOMBuilder()); |
41
|
4 |
|
} |
42
|
|
|
|
43
|
1 |
|
public function retenciones(): Retenciones |
44
|
|
|
{ |
45
|
1 |
|
return $this->retenciones; |
46
|
|
|
} |
47
|
|
|
|
48
|
2 |
|
public function putCertificado(Certificado $certificado) |
49
|
|
|
{ |
50
|
2 |
|
$this->setCertificado($certificado); |
51
|
2 |
|
$this->retenciones['NumCert'] = $certificado->getSerial(); |
52
|
2 |
|
$pemContents = implode('', preg_grep('/^((?!-).)*$/', explode(PHP_EOL, $certificado->getPemContents()))); |
53
|
2 |
|
$this->retenciones['Cert'] = $pemContents; |
54
|
2 |
|
} |
55
|
|
|
|
56
|
1 |
|
public function buildCadenaDeOrigen(): string |
57
|
|
|
{ |
58
|
1 |
|
if (! $this->hasXmlResolver()) { |
59
|
|
|
throw new \LogicException('Cannot build the cadena de origen since there is no xml resolver'); |
60
|
|
|
} |
61
|
1 |
|
$xmlResolver = $this->getXmlResolver(); |
62
|
1 |
|
$xsltLocation = $xmlResolver->resolve( |
63
|
1 |
|
'http://www.sat.gob.mx/esquemas/retencionpago/1/retenciones.xslt', |
64
|
1 |
|
$xmlResolver::TYPE_XSLT |
65
|
|
|
); |
66
|
1 |
|
return $this->getXsltBuilder()->build($this->asXml(), $xsltLocation); |
67
|
|
|
} |
68
|
|
|
|
69
|
3 |
|
public function addSello(string $key, string $passPhrase = '') |
70
|
|
|
{ |
71
|
|
|
// create private key |
72
|
3 |
|
$privateKey = new PemPrivateKey($key); |
73
|
3 |
|
if (! $privateKey->open($passPhrase)) { |
74
|
1 |
|
throw new \RuntimeException('Cannot open the private key'); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
// check privatekey belongs to certificado |
78
|
2 |
|
if ($this->hasCertificado()) { |
79
|
2 |
|
if (! $privateKey->belongsTo($this->getCertificado()->getPemContents())) { |
80
|
1 |
|
throw new \RuntimeException('The private key does not belong to the current certificate'); |
81
|
|
|
} |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
// create sign and set into Sello attribute |
85
|
1 |
|
$this->retenciones['Sello'] = base64_encode( |
86
|
1 |
|
$privateKey->sign($this->buildCadenaDeOrigen(), OPENSSL_ALGO_SHA1) |
87
|
|
|
); |
88
|
1 |
|
} |
89
|
|
|
|
90
|
2 |
|
public function validate(): Asserts |
91
|
|
|
{ |
92
|
2 |
|
$validator = new XmlFollowSchema(); |
93
|
2 |
|
$validator->setXmlResolver($this->getXmlResolver()); |
94
|
2 |
|
$asserts = new Asserts(); |
95
|
2 |
|
$validator->validate($this->retenciones, $asserts); |
96
|
2 |
|
return $asserts; |
97
|
|
|
} |
98
|
|
|
|
99
|
1 |
|
public function asXml(): string |
100
|
|
|
{ |
101
|
1 |
|
return XmlNodeUtils::nodeToXmlString($this->retenciones, true); |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
|
Let?s assume that you have a directory layout like this:
and let?s assume the following content of
Bar.php
:If both files
OtherDir/Foo.php
andSomeDir/Foo.php
are loaded in the same runtime, you will see a PHP error such as the following:PHP Fatal error: Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php
However, as
OtherDir/Foo.php
does not necessarily have to be loaded and the error is only triggered if it is loaded beforeOtherDir/Bar.php
, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias: