Code

< 40 %
40-60 %
> 60 %
1
<?php
2
3
/** @noinspection PhpComposerExtensionStubsInspection */
4
5
namespace CfdiUtils\ConsultaCfdiSat;
6
7
use RuntimeException;
8
use SoapClient;
9
use SoapVar;
10
use stdClass;
11
12
class WebService
13
{
14
    /** @var SoapClient|null */
15
    private $soapClient;
16
17
    /** @var Config */
18
    private $config;
19
20 11
    public function __construct(Config $config = null)
21
    {
22 11
        $this->config = $config ?: new Config();
23
    }
24
25 7
    public function getConfig(): Config
26
    {
27 7
        return $this->config;
28
    }
29
30 5
    public function getSoapClient(): SoapClient
31
    {
32 5
        if (! $this->soapClient instanceof SoapClient) {
33 5
            $this->soapClient = $this->createSoapClient();
34
        }
35 5
        return $this->soapClient;
36
    }
37
38 1
    public function destroySoapClient()
39
    {
40 1
        $this->soapClient = null;
41
    }
42
43 5
    protected function createSoapClient(): SoapClient
44
    {
45
        /*
46
         * options.location: required to build the object
47
         *
48
         * options.uri: required to build the object
49
         *
50
         * options.use: SOAP_ENCODED (default) or SOAP_LITERAL
51
         * Both works but SOAP_LITERAL is cleaner
52
         *
53
         * options.style: SOAP_RPC (default) or SOAP_DOCUMENT
54
         * SOAP_DOCUMENT removes the method name from soap body
55
         *
56
         */
57 5
        $config = $this->getConfig();
58 5
        $soapOptions = [
59 5
            'location' => $config->getServiceUrl(),
60 5
            'uri' => 'http://tempuri.org/',
61 5
            'style' => SOAP_RPC,
62 5
            'use' => SOAP_LITERAL,
63 5
            'soap_version' => SOAP_1_1,
64 5
            'exceptions' => 1,
65 5
            'stream_context' => stream_context_create([
66 5
                'ssl' => [
67 5
                    'verify_peer' => $config->shouldVerifyPeer(),
68 5
                ],
69 5
            ]),
70 5
            'connection_timeout' => $config->getTimeout(),
71 5
            'trace' => false, // use this setting for development
72 5
        ];
73
74 5
        return new SoapClient(null, $soapOptions);
75
    }
76
77 8
    public function request(RequestParameters $requestParameters): StatusResponse
78
    {
79 8
        $rawResponse = $this->doRequestConsulta($requestParameters->expression());
80
81 8
        if (! ($rawResponse instanceof stdClass)) {
82 1
            throw new RuntimeException('The consulta web service did not return any result');
83
        }
84 7
        $result = (array) $rawResponse;
85 7
        if (! isset($result['CodigoEstatus'])) {
86 1
            throw new RuntimeException('The consulta web service did not have expected ConsultaResult:CodigoEstatus');
87
        }
88 6
        if (! isset($result['Estado'])) {
89 1
            throw new RuntimeException('The consulta web service did not have expected ConsultaResult:Estado');
90
        }
91 5
        return new StatusResponse(
92 5
            $result['CodigoEstatus'],
93 5
            $result['Estado'],
94 5
            $result['EsCancelable'] ?? '',
95 5
            $result['EstatusCancelacion'] ?? '',
96 5
            $result['ValidacionEFOS'] ?? ''
97 5
        );
98
    }
99
100
    /**
101
     * This method exists to be able to mock SOAP call
102
     *
103
     * @param string $expression
104
     * @return null|stdClass
105
     *@internal
106
     */
107 4
    protected function doRequestConsulta(string $expression): ?stdClass
108
    {
109
        /** @var int $encoding Override because inspectors does not know that second argument can be NULL */
110 4
        $encoding = null;
111 4
        return $this->getSoapClient()->__soapCall(
112 4
            'Consulta',
113 4
            [new SoapVar($expression, $encoding, '', '', 'expresionImpresa', 'http://tempuri.org/')],
114 4
            ['soapaction' => 'http://tempuri.org/IConsultaCFDIService/Consulta']
115 4
        );
116
    }
117
}
118