Completed
Pull Request — master (#44)
by
unknown
03:06 queued 43s
created

Sii::request()   C

Complexity

Conditions 16
Paths 192

Size

Total Lines 58
Code Lines 44

Duplication

Lines 25
Ratio 43.1 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 25
loc 58
rs 5.8371
cc 16
eloc 44
nc 192
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * LibreDTE
5
 * Copyright (C) SASCO SpA (https://sasco.cl)
6
 *
7
 * Este programa es software libre: usted puede redistribuirlo y/o
8
 * modificarlo bajo los términos de la Licencia Pública General Affero de GNU
9
 * publicada por la Fundación para el Software Libre, ya sea la versión
10
 * 3 de la Licencia, o (a su elección) cualquier versión posterior de la
11
 * misma.
12
 *
13
 * Este programa se distribuye con la esperanza de que sea útil, pero
14
 * SIN GARANTÍA ALGUNA; ni siquiera la garantía implícita
15
 * MERCANTIL o de APTITUD PARA UN PROPÓSITO DETERMINADO.
16
 * Consulte los detalles de la Licencia Pública General Affero de GNU para
17
 * obtener una información más detallada.
18
 *
19
 * Debería haber recibido una copia de la Licencia Pública General Affero de GNU
20
 * junto a este programa.
21
 * En caso contrario, consulte <http://www.gnu.org/licenses/agpl.html>.
22
 */
23
24
namespace sasco\LibreDTE;
25
26
/**
27
 * Clase para acciones genéricas asociadas al SII de Chile
28
 * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
29
 * @version 2017-09-11
30
 */
31
class Sii
32
{
33
34
    private static $config = [
35
        'wsdl' => [
36
            '*' => 'https://{servidor}.sii.cl/DTEWS/{servicio}.jws?WSDL',
37
            'QueryEstDteAv' => 'https://{servidor}.sii.cl/DTEWS/services/{servicio}?WSDL',
38
            'wsDTECorreo' => 'https://{servidor}.sii.cl/DTEWS/services/{servicio}?WSDL',
39
        ],
40
        'servidor' => ['palena', 'maullin'], ///< servidores 0: producción, 1: certificación
41
        'certs' => [300, 100], ///< certificados 0: producción, 1: certificación
42
    ];
43
44
    const PRODUCCION = 0; ///< Constante para indicar ambiente de producción
45
    const CERTIFICACION = 1; ///< Constante para indicar ambiente de desarrollo
46
47
    const IVA = 19; ///< Tasa de IVA
48
49
    private static $retry = 10; ///< Veces que se reintentará conectar a SII al usar el servicio web
50
    private static $verificar_ssl = true; ///< Indica si se deberá verificar o no el certificado SSL del SII
51
    private static $ambiente = self::PRODUCCION; ///< Ambiente que se utilizará
52
53
    private static $direcciones_regionales = [
54
        'CHILLÁN VIEJO' => 'CHILLÁN',
55
        'HUECHURABA' => 'SANTIAGO NORTE',
56
        'LA CISTERNA' => 'SANTIAGO SUR',
57
        'LAS CONDES' => 'SANTIAGO ORIENTE',
58
        'LO ESPEJO' => 'SANTIAGO SUR',
59
        'PEÑALOLÉN' => 'ÑUÑOA',
60
        'PUDAHUEL' => 'SANTIAGO PONIENTE',
61
        'RECOLETA' => 'SANTIAGO NORTE',
62
        'SANTIAGO' => 'SANTIAGO CENTRO',
63
        'SAN MIGUEL' => 'SANTIAGO SUR',
64
        'SAN VICENTE' => 'SAN VICENTE TAGUA TAGUA',
65
        'TALTAL' => 'ANTOFAGASTA',
66
        'VITACURA' => 'SANTIAGO ORIENTE',
67
        'VICHUQUÉN' => 'CURICÓ',
68
    ]; /// Direcciones regionales del SII según la comuna
69
70
    /**
71
     * Método que permite asignar el nombre del servidor del SII que se
72
     * usará para las consultas al SII
73
     * @param servidor Servidor que se usará, si es https://maullin2.sii.cl, entonces se debe pasar como valor maullin2
74
     * @param certificacion Permite definir si se está cambiando el servidor de certificación o el de producción
75
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
76
     * @version 2015-09-14
77
     */
78
    public static function setServidor($servidor = 'maullin', $certificacion = Sii::CERTIFICACION)
79
    {
80
        self::$config['servidor'][$certificacion] = $servidor;
81
    }
82
83
    /**
84
     * Método que entrega el nombre del servidor a usar según el ambiente
85
     * @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará
86
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
87
     * @version 2016-08-01
88
     */
89
    public static function getServidor($ambiente = null)
90
    {
91
        return self::$config['servidor'][self::getAmbiente($ambiente)];
92
    }
93
94
    /**
95
     * Método que entrega la URL de un recurso en el SII según el ambiente que se esté usando
96
     * @param recurso Recurso del sitio del SII que se desea obtener la URL
97
     * @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará
98
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
99
     * @version 2017-09-11
100
     */
101
    public static function getURL($recurso, $ambiente = null)
102
    {
103
        return 'https://'.self::getServidor($ambiente).'.sii.cl'.$recurso;
104
    }
105
106
    /**
107
     * Método para obtener el WSDL
108
     *
109
     * \code{.php}
110
     *   $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed'); // WSDL para pedir semilla
111
     * \endcode
112
     *
113
     * Para forzar el uso del WSDL de certificación hay dos maneras, una es
114
     * pasando un segundo parámetro al método get con valor Sii::CERTIFICACION:
115
     *
116
     * \code{.php}
117
     *   $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed', \sasco\LibreDTE\Sii::CERTIFICACION);
118
     * \endcode
119
     *
120
     * La otra manera, para evitar este segundo parámetro, es asignar el valor a
121
     * través de la configuración:
122
     *
123
     * \code{.php}
124
     *   \sasco\LibreDTE\Sii::setAmbiente(\sasco\LibreDTE\Sii::CERTIFICACION);
125
     * \endcode
126
     *
127
     * @param servicio Servicio por el cual se está solicitando su WSDL
128
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática)
129
     * @return URL del WSDL del servicio según ambiente solicitado
130
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
131
     * @version 2016-06-11
132
     */
133
    public static function wsdl($servicio, $ambiente = null)
134
    {
135
        // determinar ambiente que se debe usar
136
        $ambiente = self::getAmbiente($ambiente);
137
        // entregar WSDL local (modificados para ambiente de certificación)
138
        if ($ambiente==self::CERTIFICACION) {
139
            $wsdl = dirname(dirname(__FILE__)).'/wsdl/'.self::$config['servidor'][$ambiente].'/'.$servicio.'.jws';
140
            if (is_readable($wsdl))
141
                return $wsdl;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $wsdl; (string) is incompatible with the return type documented by sasco\LibreDTE\Sii::wsdl of type sasco\LibreDTE\URL.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
142
        }
143
        // entregar WSDL oficial desde SII
144
        $location = isset(self::$config['wsdl'][$servicio]) ? self::$config['wsdl'][$servicio] : self::$config['wsdl']['*'];
145
        $wsdl = str_replace(
146
            ['{servidor}', '{servicio}'],
147
            [self::$config['servidor'][$ambiente], $servicio],
148
            $location
149
        );
150
        // entregar wsdl
151
        return $wsdl;
152
    }
153
154
    /**
155
     * Método para realizar una solicitud al servicio web del SII
156
     * @param wsdl Nombre del WSDL que se usará
157
     * @param request Nombre de la función que se ejecutará en el servicio web
158
     * @param args Argumentos que se pasarán al servicio web
159
     * @param retry Intentos que se realizarán como máximo para obtener respuesta
160
     * @return Objeto SimpleXMLElement con la espuesta del servicio web consultado
161
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
162
     * @version 2016-08-28
163
     */
164
    public static function request($wsdl, $request, $args = null, $retry = null)
165
    {
166
        if (is_numeric($args)) {
167
            $retry = (int)$args;
168
            $args = null;
169
        }
170
        if (!$retry)
171
            $retry = self::$retry;
172
        if ($args and !is_array($args))
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
173
            $args = [$args];
174 View Code Duplication
        if (!self::$verificar_ssl) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175
            if (self::getAmbiente()==self::PRODUCCION) {
176
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
177
                \sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING);
0 ignored issues
show
Documentation introduced by
$msg is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
LOG_WARNING is of type integer, but the function expects a object<sasco\LibreDTE\Gravedad>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
178
            }
179
            $options = ['stream_context' => stream_context_create([
180
                'ssl' => [
181
                    'verify_peer' => false,
182
                    'verify_peer_name' => false,
183
                    'allow_self_signed' => true
184
                ]
185
            ])];
186
        } else {
187
            $options = [];
188
        }
189
        try {
190
            $soap = new \SoapClient(self::wsdl($wsdl), $options);
191
        } catch (\Exception $e) {
192
            $msg = $e->getMessage();
193 View Code Duplication
            if (isset($e->getTrace()[0]['args'][1]) and is_string($e->getTrace()[0]['args'][1])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
194
                $msg .= ': '.$e->getTrace()[0]['args'][1];
195
            }
196
            \sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_SOAP, Estado::get(Estado::REQUEST_ERROR_SOAP, $msg));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...QUEST_ERROR_SOAP, $msg) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
197
            return false;
198
        }
199
        for ($i=0; $i<$retry; $i++) {
200
            try {
201
                if ($args) {
202
                    $body = call_user_func_array([$soap, $request], $args);
203
                } else {
204
                    $body = $soap->$request();
205
                }
206
                break;
207
            } catch (\Exception $e) {
208
                $msg = $e->getMessage();
209 View Code Duplication
                if (isset($e->getTrace()[0]['args'][1]) and is_string($e->getTrace()[0]['args'][1])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
210
                    $msg .= ': '.$e->getTrace()[0]['args'][1];
211
                }
212
                \sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_SOAP, Estado::get(Estado::REQUEST_ERROR_SOAP, $msg));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...QUEST_ERROR_SOAP, $msg) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
213
                $body = null;
214
            }
215
        }
216 View Code Duplication
        if ($body===null) {
0 ignored issues
show
Bug introduced by
The variable $body does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
            \sasco\LibreDTE\Log::write(Estado::REQUEST_ERROR_BODY, Estado::get(Estado::REQUEST_ERROR_BODY, $wsdl, $retry));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...OR_BODY, $wsdl, $retry) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
218
            return false;
219
        }
220
        return new \SimpleXMLElement($body, LIBXML_COMPACT);
221
    }
222
223
    /**
224
     * Método que permite indicar si se debe o no verificar el certificado SSL
225
     * del SII
226
     * @param verificar =true si se quiere verificar certificado, =false en caso que no (por defecto se verifica)
227
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
228
     * @version 2015-09-16
229
     */
230
    public static function setVerificarSSL($verificar = true)
231
    {
232
        self::$verificar_ssl = $verificar;
233
    }
234
235
    /**
236
     * Método que indica si se está o no verificando el SSL en las conexiones al SII
237
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
238
     * @version 2017-05-11
239
     */
240
    public static function getVerificarSSL()
241
    {
242
        return self::$verificar_ssl;
243
    }
244
245
    /**
246
     * Método que realiza el envío de un DTE al SII
247
     * Referencia: http://www.sii.cl/factura_electronica/factura_mercado/envio.pdf
248
     * @param usuario RUN del usuario que envía el DTE
249
     * @param empresa RUT de la empresa emisora del DTE
250
     * @param dte Documento XML con el DTE que se desea enviar a SII
251
     * @param token Token de autenticación automática ante el SII
252
     * @param retry Intentos que se realizarán como máximo para obtener respuesta
253
     * @return Respuesta XML desde SII o bien null si no se pudo obtener respuesta
254
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
255
     * @version 2016-08-06
256
     */
257
    public static function enviar($usuario, $empresa, $dte, $token, $retry = null)
258
    {
259
        // definir datos que se usarán en el envío
260
        list($rutSender, $dvSender) = explode('-', str_replace('.', '', $usuario));
261
        list($rutCompany, $dvCompany) = explode('-', str_replace('.', '', $empresa));
262
        if (strpos($dte, '<?xml')===false) {
263
            $dte = '<?xml version="1.0" encoding="ISO-8859-1"?>'."\n".$dte;
264
        }
265 View Code Duplication
        do {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
266
            $file = sys_get_temp_dir().'/dte_'.md5(microtime().$token.$dte).'.xml';
267
        } while (file_exists($file));
268
        file_put_contents($file, $dte);
269
        $data = [
270
            'rutSender' => $rutSender,
271
            'dvSender' => $dvSender,
272
            'rutCompany' => $rutCompany,
273
            'dvCompany' => $dvCompany,
274
            'archivo' => curl_file_create(
275
                $file,
276
                'application/xml',
277
                basename($file)
278
            ),
279
        ];
280
        // definir reintentos si no se pasaron
281
        if (!$retry)
282
            $retry = self::$retry;
283
        // crear sesión curl con sus opciones
284
        $curl = curl_init();
285
        $header = [
286
            'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)',
287
            'Referer: https://libredte.cl',
288
            'Cookie: TOKEN='.$token,
289
        ];
290
        $url = 'https://'.self::$config['servidor'][self::getAmbiente()].'.sii.cl/cgi_dte/UPL/DTEUpload';
291
        curl_setopt($curl, CURLOPT_POST, true);
292
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
293
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
294
        curl_setopt($curl, CURLOPT_URL, $url);
295
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
296
        // si no se debe verificar el SSL se asigna opción a curl, además si
297
        // se está en el ambiente de producción y no se verifica SSL se
298
        // generará una entrada en el log
299 View Code Duplication
        if (!self::$verificar_ssl) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
            if (self::getAmbiente()==self::PRODUCCION) {
301
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
302
                \sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING);
0 ignored issues
show
Documentation introduced by
$msg is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
LOG_WARNING is of type integer, but the function expects a object<sasco\LibreDTE\Gravedad>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
303
            }
304
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
305
        }
306
        // enviar XML al SII
307 View Code Duplication
        for ($i=0; $i<$retry; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
308
            $response = curl_exec($curl);
309
            if ($response and $response!='Error 500')
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
310
                break;
311
        }
312
        unlink($file);
313
        // verificar respuesta del envío y entregar error en caso que haya uno
314
        if (!$response or $response=='Error 500') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or instead of || is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
315
            if (!$response)
0 ignored issues
show
Bug introduced by
The variable $response does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
316
                \sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_CURL, Estado::get(Estado::ENVIO_ERROR_CURL, curl_error($curl)));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...URL, curl_error($curl)) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
317
            if ($response=='Error 500')
318
                \sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_500, Estado::get(Estado::ENVIO_ERROR_500));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...stado::ENVIO_ERROR_500) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
319
            return false;
320
        }
321
        // cerrar sesión curl
322
        curl_close($curl);
323
        // crear XML con la respuesta y retornar
324
        try {
325
            $xml = new \SimpleXMLElement($response, LIBXML_COMPACT);
326
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class sasco\LibreDTE\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
327
            \sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_XML, Estado::get(Estado::ENVIO_ERROR_XML, $e->getMessage()));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::..._XML, $e->getMessage()) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
328
            return false;
329
        }
330
        if ($xml->STATUS!=0) {
331
            \sasco\LibreDTE\Log::write(
332
                $xml->STATUS,
333
                Estado::get($xml->STATUS).(isset($xml->DETAIL)?'. '.implode("\n", (array)$xml->DETAIL->ERROR):'')
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...l->DETAIL->ERROR) : '') is of type string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
334
            );
335
        }
336
        return $xml;
337
    }
338
339
    /**
340
     * Método para obtener la clave pública (certificado X.509) del SII
341
     *
342
     * \code{.php}
343
     *   $pub_key = \sasco\LibreDTE\Sii::cert(100); // Certificado IDK 100 (certificación)
344
     * \endcode
345
     *
346
     * @param idk IDK de la clave pública del SII. Si no se indica se tratará de determinar con el ambiente que se esté usando
347
     * @return Contenido del certificado
348
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
349
     * @version 2015-09-16
350
     */
351
    public static function cert($idk = null)
352
    {
353
        // si se pasó un idk y existe el archivo asociado se entrega
354
        if ($idk) {
355
            $cert = dirname(dirname(__FILE__)).'/certs/'.$idk.'.cer';
356
            if (is_readable($cert))
357
                return file_get_contents($cert);
358
        }
359
        // buscar certificado y entregar si existe o =false si no
360
        $ambiente = self::getAmbiente();
361
        $cert = dirname(dirname(__FILE__)).'/certs/'.self::$config['certs'][$ambiente].'.cer';
362
        if (!is_readable($cert)) {
363
            \sasco\LibreDTE\Log::write(Estado::SII_ERROR_CERTIFICADO, Estado::get(Estado::SII_ERROR_CERTIFICADO, self::$config['certs'][$ambiente]));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...ig['certs'][$ambiente]) is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
364
            return false;
365
        }
366
        return file_get_contents($cert);
367
    }
368
369
    /**
370
     * Método que asigna el ambiente que se usará por defecto (si no está
371
     * asignado con la constante _LibreDTE_CERTIFICACION_)
372
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION
373
     * @warning No se está verificando SSL en ambiente de certificación
374
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
375
     * @version 2016-08-28
376
     */
377
    public static function setAmbiente($ambiente = self::PRODUCCION)
378
    {
379
        $ambiente = $ambiente ? self::CERTIFICACION : self::PRODUCCION;
380
        if ($ambiente==self::CERTIFICACION) {
381
            self::setVerificarSSL(false);
382
        }
383
        self::$ambiente = $ambiente;
384
    }
385
386
    /**
387
     * Método que determina el ambiente que se debe utilizar: producción o
388
     * certificación
389
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática)
390
     * @return Ambiente que se debe utilizar
391
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
392
     * @version 2015-09-07
393
     */
394
    public static function getAmbiente($ambiente = null)
395
    {
396
        if ($ambiente===null) {
397
            if (defined('_LibreDTE_CERTIFICACION_'))
398
                $ambiente = (int)_LibreDTE_CERTIFICACION_;
0 ignored issues
show
Bug Compatibility introduced by
The expression (int) _LibreDTE_CERTIFICACION_; of type integer adds the type integer to the return on line 402 which is incompatible with the return type documented by sasco\LibreDTE\Sii::getAmbiente of type sasco\LibreDTE\Ambiente.
Loading history...
399
            else
400
                $ambiente = self::$ambiente;
0 ignored issues
show
Bug Compatibility introduced by
The expression self::$ambiente; of type integer adds the type integer to the return on line 402 which is incompatible with the return type documented by sasco\LibreDTE\Sii::getAmbiente of type sasco\LibreDTE\Ambiente.
Loading history...
401
        }
402
        return $ambiente;
403
    }
404
405
    /**
406
     * Método que entrega la tasa de IVA vigente
407
     * @return Tasa de IVA vigente
408
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
409
     * @version 2015-09-03
410
     */
411
    public static function getIVA()
412
    {
413
        return self::IVA;
414
    }
415
416
    /**
417
     * Método que entrega un arreglo con todos los datos de los contribuyentes
418
     * que operan con factura electrónica descargados desde el SII
419
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
420
     * @version 2017-07-07
421
     */
422
    public static function getContribuyentes(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null, $dia = null)
423
    {
424
        // solicitar token
425
        $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
0 ignored issues
show
Documentation introduced by
$Firma is of type object<sasco\LibreDTE\FirmaElectronica>, but the function expects a object<sasco\LibreDTE\Sii\objeto>|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
426
        if (!$token)
0 ignored issues
show
Bug Best Practice introduced by
The expression $token of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
427
            return false;
428
        // definir ambiente y servidor
429
        $ambiente = self::getAmbiente($ambiente);
430
        $servidor = self::$config['servidor'][$ambiente];
431
        // preparar consulta curl
432
        $curl = curl_init();
433
        $header = [
434
            'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)',
435
            'Referer: https://'.$servidor.'.sii.cl/cvc/dte/ee_empresas_dte.html',
436
            'Cookie: TOKEN='.$token,
437
            'Accept-Encoding' => 'gzip, deflate, sdch',
438
        ];
439
        $dia = $dia===null ? date('Ymd') : str_replace('-', '', $dia);
440
        $url = 'https://'.$servidor.'.sii.cl/cvc_cgi/dte/ce_empresas_dwnld?NOMBRE_ARCHIVO=ce_empresas_dwnld_'.$dia.'.csv';
441
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
442
        curl_setopt($curl, CURLOPT_URL, $url);
443
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
444
        // si no se debe verificar el SSL se asigna opción a curl, además si
445
        // se está en el ambiente de producción y no se verifica SSL se
446
        // generará un error de nivel E_USER_NOTICE
447 View Code Duplication
        if (!self::$verificar_ssl) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
448
            if ($ambiente==self::PRODUCCION) {
449
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
450
                \sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING);
0 ignored issues
show
Documentation introduced by
$msg is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
LOG_WARNING is of type integer, but the function expects a object<sasco\LibreDTE\Gravedad>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
451
            }
452
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
453
        }
454
        // realizar consulta curl
455
        $response = curl_exec($curl);
456
        if (!$response)
457
            return false;
458
        // cerrar sesión curl
459
        curl_close($curl);
460
        // entregar datos del archivo CSV
461
        ini_set('memory_limit', '1024M');
462
        $lines = explode("\n", $response);
463
        $n_lines = count($lines);
464
        $data = [];
465 View Code Duplication
        for ($i=1; $i<$n_lines; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
466
            $row = str_getcsv($lines[$i], ';', '');
467
            unset($lines[$i]);
468
            if (!isset($row[5]))
469
                continue;
470
            for ($j=0; $j<6; $j++)
471
                $row[$j] = trim($row[$j]);
472
            $row[1] = utf8_decode($row[1]);
473
            $row[4] = strtolower($row[4]);
474
            $row[5] = strtolower($row[5]);
475
            $data[] = $row;
476
        }
477
        return $data;
478
    }
479
480
    
481
    /**
482
     * Método que entrega un arreglo con todas los documentos tributarios electrónicos
483
     * recepcionados en el SII
484
     * @param Firma digital del certificado
485
     * @param Ambiente de prueba
486
     * @param Rut empresa a consultar
487
     * @param Desde fecha a consultar. Formato d-m-Y
488
     * @param Hasta fecha a consultar. Formato d-m-Y
489
     * @return Dirección regional del SII
490
     * [0] Lin.
491
     * [1] Rut del Emisor
492
     * [2] Razón Social Emisor
493
     * [3] Tipo Dte
494
     * [4] Folio Dte
495
     * [5] Fecha Emisión
496
     * [6] Monto Total
497
     * [7] Fecha Recepción
498
     * [8] Número Envío
499
     * @author Adonias Vasquez (adonias.vasquez[at]epys.cl)
500
     * @version 2017-09-30
501
     */
502
    public static function getDTERecibidos(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null, $rut = null, $desde = null, $hasta = null)
503
    {
504
        // solicitar token
505
        $token = \sasco\LibreDTE\Sii\Autenticacion::getToken($Firma);
0 ignored issues
show
Documentation introduced by
$Firma is of type object<sasco\LibreDTE\FirmaElectronica>, but the function expects a object<sasco\LibreDTE\Sii\objeto>|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
506
        if (!$token)
0 ignored issues
show
Bug Best Practice introduced by
The expression $token of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
507
            return false;
508
509
        // RUT_NS Y DV_NS
510
        list($rut_ns, $dv_ns) = explode('-', $rut);
511
512
        // definir ambiente y servidor
513
        $ambiente = self::getAmbiente($ambiente);
514
        $servidor = self::$config['servidor'][$ambiente];
515
516
        // preparar consulta curl
517
        $curl = curl_init();
518
        $header = [
519
            'User-Agent: User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)',
520
            'Referer: https://' . $servidor . '.sii.cl',
521
            'Cookie: TOKEN=' . $token . '; RUT_NS='.$rut_ns.'; DV_NS='.$dv_ns,
522
            'Accept-Encoding' => 'gzip, deflate, sdch',
523
        ];
524
        $desde = $desde === null ? date('01-m-Y', strtotime('-60 days')) : $desde; // Consulto últimos 2 meses
525
        $hasta = $hasta === null ? date('d-m-Y') : $hasta; //Hasta fecha actual
526
527
        $fields = [
528
            'DESDE' => urlencode($desde),
529
            'HASTA' => urlencode($hasta),
530
            'RUT' => urlencode($rut),
531
            'TIPO_CONSULTA' => '' // En blancopara que genere un CSV
532
        ];
533
534
        $url = 'https://' . $servidor . '.sii.cl/cgi_dte/consultaDTE/wsDTEConsRecCont.sh';
535
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
536
        curl_setopt($curl, CURLOPT_URL, $url);
537
        curl_setopt($curl, CURLOPT_POST, 1);
538
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($fields));
539
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
540
        curl_setopt($curl, CURLINFO_HEADER_OUT, true); // capture the header info
541
542
543
        // si no se debe verificar el SSL se asigna opción a curl, además si
544
        // se está en el ambiente de producción y no se verifica SSL se
545
        // generará un error de nivel E_USER_NOTICE
546 View Code Duplication
        if (!self::$verificar_ssl) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
547
            if ($ambiente == self::PRODUCCION) {
548
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
549
                \sasco\LibreDTE\Log::write(Estado::ENVIO_SSL_SIN_VERIFICAR, $msg, LOG_WARNING);
0 ignored issues
show
Documentation introduced by
$msg is of type integer|string, but the function expects a object<sasco\LibreDTE\Mensaje>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
LOG_WARNING is of type integer, but the function expects a object<sasco\LibreDTE\Gravedad>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
550
            }
551
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
552
        }
553
554
        // realizar consulta curl
555
        $response = curl_exec($curl);
556
        if (!$response)
557
            return false;
558
        // cerrar sesión curl
559
        curl_close($curl);
560
        // entregar datos del archivo CSV
561
        ini_set('memory_limit', '1024M');
562
        $lines = explode("\n", $response);
563
        $n_lines = count($lines);
564
        $data = [];
565 View Code Duplication
        for ($i = 1; $i < $n_lines; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
566
            $row = str_getcsv($lines[$i], ';', '');
567
            unset($lines[$i]);
568
            for ($j = 0; $j < 6; $j++)
569
                $row[$j] = trim($row[$j]);
570
            $row[1] = utf8_decode($row[1]);
571
            $row[2] = utf8_decode($row[2]);
572
            $row[3] = utf8_decode($row[3]);
573
574
            $data[] = $row;
575
        }
576
577
        return $data;
578
    }
579
    /**
580
     * Método que entrega la dirección regional según la comuna que se esté
581
     * consultando
582
     * @param comuna de la sucursal del emior o bien código de la sucursal del SII
583
     * @return Dirección regional del SII
584
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
585
     * @version 2016-06-03
586
     */
587
    public static function getDireccionRegional($comuna)
588
    {
589
        if (!is_numeric($comuna)) {
590
            $direccion = mb_strtoupper($comuna, 'UTF-8');
591
            return isset(self::$direcciones_regionales[$direccion]) ? self::$direcciones_regionales[$direccion] : $direccion;
592
        }
593
        return 'SUC '.$comuna;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'SUC ' . $comuna; (string) is incompatible with the return type documented by sasco\LibreDTE\Sii::getDireccionRegional of type sasco\LibreDTE\Dirección.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
594
    }
595
596
}
597