Completed
Push — master ( 8037fb...a56aaf )
by Esteban De La Fuente
03:02
created

Sii   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 811
Duplicated Lines 7.4 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 7
Bugs 0 Features 0
Metric Value
wmc 69
c 7
b 0
f 0
lcom 2
cbo 3
dl 60
loc 811
rs 3.102

14 Methods

Rating   Name   Duplication   Size   Complexity  
A setServidor() 0 4 1
A getServidor() 0 4 1
A wsdl() 0 20 4
A setVerificarSSL() 0 4 1
A getVerificarSSL() 0 4 1
A cert() 0 17 4
A setAmbiente() 0 8 3
A getAmbiente() 0 10 3
A getIVA() 0 4 1
B getContribuyentes() 7 57 9
A getURL() 0 4 1
C request() 25 58 16
F enviar() 28 92 20
A getDireccionRegional() 0 11 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Sii often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Sii, and based on these observations, apply Extract Interface, too.

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 2018-01-17
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
        'ARICA' => 'ARICA',
55
        'CAMARONES' => 'ARICA',
56
        'PUTRE' => 'ARICA',
57
        'GENERAL LAGOS' => 'ARICA',
58
        'IQUIQUE' => 'IQUIQUE',
59
        'PICA' => 'IQUIQUE',
60
        'POZO ALMONTE' => 'IQUIQUE',
61
        'HUARA' => 'IQUIQUE',
62
        'CAMIÑA' => 'IQUIQUE',
63
        'COLCHANE' => 'IQUIQUE',
64
        'ALTO HOSPICIO' => 'IQUIQUE',
65
        'ANTOFAGASTA' => 'ANTOFAGASTA',
66
        'MEJILLONES' => 'ANTOFAGASTA',
67
        'SIERRA GORDA' => 'ANTOFAGASTA',
68
        'CALAMA' => 'CALAMA',
69
        'SAN PEDRO DE ATACAMA' => 'CALAMA',
70
        'OLLAGUE' => 'CALAMA',
71
        'TOCOPILLA' => 'TOCOPILLA',
72
        'MARÍA ELENA' => 'TOCOPILLA',
73
        'TALTAL' => 'TALTAL',
74
        'COPIAPÓ' => 'COPIAPÓ',
75
        'CALDERA' => 'COPIAPÓ',
76
        'TIERRA AMARILLA' => 'COPIAPÓ',
77
        'CHAÑARAL' => 'CHAÑARAL',
78
        'DIEGO DE ALMAGRO' => 'CHAÑARAL',
79
        'VALLENAR' => 'VALLENAR',
80
        'FREIRINA' => 'VALLENAR',
81
        'HUASCO' => 'VALLENAR',
82
        'ALTO DEL CARMEN' => 'VALLENAR',
83
        'LA SERENA' => 'LA SERENA',
84
        'LA HIGUERA' => 'LA SERENA',
85
        'PAIHUANO' => 'LA SERENA',
86
        'ANDACOLLO' => 'LA SERENA',
87
        'VICUÑA' => 'LA SERENA',
88
        'OVALLE' => 'OVALLE',
89
        'MONTE PATRIA' => 'OVALLE',
90
        'PUNITAQUI' => 'OVALLE',
91
        'COMBARBALÁ' => 'OVALLE',
92
        'RÍO HURTADO' => 'OVALLE',
93
        'ILLAPEL' => 'ILLAPEL',
94
        'SALAMANCA' => 'ILLAPEL',
95
        'LOS VILOS' => 'ILLAPEL',
96
        'CANELA' => 'ILLAPEL',
97
        'COMQUIMBO' => 'COQUIMBO',
98
        'VALPARAÍSO' => 'VALPARAÍSO',
99
        'CASABLANCA' => 'VALPARAÍSO',
100
        'JUAN FERNÁNDEZ' => 'VALPARAÍSO',
101
        'ISLA DE PASCUA' => 'VALPARAÍSO',
102
        'CONCÓN' => 'VIÑA DEL MAR',
103
        'QUINTERO' => 'VIÑA DEL MAR',
104
        'PUCHUNCAVÍ' => 'VIÑA DEL MAR',
105
        'VIÑA DEL MAR' => 'VIÑA DEL MAR',
106
        'LA LIGUA' => 'LA LIGUA',
107
        'PETORCA' => 'LA LIGUA',
108
        'CABILDO' => 'LA LIGUA',
109
        'ZAPALLAR' => 'LA LIGUA',
110
        'PAPUDO' => 'LA LIGUA',
111
        'SAN ANTONIO' => 'SAN ANTONIO',
112
        'SANTO DOMINGO' => 'SAN ANTONIO',
113
        'CARTAGENA' => 'SAN ANTONIO',
114
        'EL TABO' => 'SAN ANTONIO',
115
        'EL QUISCO' => 'SAN ANTONIO',
116
        'ALGARROBO' => 'SAN ANTONIO',
117
        'QUILLOTA' => 'QUILLOTA',
118
        'NOGALES' => 'QUILLOTA',
119
        'HIJUELAS' => 'QUILLOTA',
120
        'LA CALERA' => 'QUILLOTA',
121
        'LA CRUZ' => 'QUILLOTA',
122
        'LIMACHE' => 'QUILLOTA',
123
        'OLMUÉ' => 'QUILLOTA',
124
        'SAN FELIPE' => 'SAN FELIPE',
125
        'PANQUEHUE' => 'SAN FELIPE',
126
        'CATEMU' => 'SAN FELIPE',
127
        'PUTAENDO' => 'SAN FELIPE',
128
        'SANTA MARÍA' => 'SAN FELIPE',
129
        'LLAY LLAY' => 'SAN FELIPE',
130
        'LOS ANDES' => 'LOS ANDES',
131
        'CALLE LARGA' => 'LOS ANDES',
132
        'SAN ESTEBAN' => 'LOS ANDES',
133
        'RINCONADA' => 'LOS ANDES',
134
        'VILLA ALEMANA' => 'VILLA ALEMANA',
135
        'QUILPUÉ' => 'VILLA ALEMANA',
136
        'RANCAGUA' => 'RANCAGUA',
137
        'MACHALÍ' => 'RANCAGUA',
138
        'GRANEROS' => 'RANCAGUA',
139
        'SAN FRANCISCO DE MOSTAZAL' => 'RANCAGUA',
140
        'DOÑIHUE' => 'RANCAGUA',
141
        'CODEGUA' => 'RANCAGUA',
142
        'RENGO' => 'RANCAGUA',
143
        'COLTAUCO' => 'RANCAGUA',
144
        'REQUINOA' => 'RANCAGUA',
145
        'OLIVAR' => 'RANCAGUA',
146
        'MALLOA' => 'RANCAGUA',
147
        'COINCO' => 'RANCAGUA',
148
        'QUINTA DE TILCOCO' => 'RANCAGUA',
149
        'SAN FERNANDO' => 'SAN FERNANDO',
150
        'CHIMBARONGO' => 'SAN FERNANDO',
151
        'NANCAGUA' => 'SAN FERNANDO',
152
        'PLACILLA' => 'SAN FERNANDO',
153
        'SANTA CRUZ' => 'SANTA CRUZ',
154
        'LOLOL' => 'SANTA CRUZ',
155
        'PALMILLA' => 'SANTA CRUZ',
156
        'PERALILLO' => 'SANTA CRUZ',
157
        'CHÉPICA' => 'SANTA CRUZ',
158
        'PUMANQUE' => 'SANTA CRUZ',
159
        'SAN VICENTE' => 'SAN VICENTE TAGUA TAGUA',
160
        'LAS CABRAS' => 'SAN VICENTE TAGUA TAGUA',
161
        'PEUMO' => 'SAN VICENTE TAGUA TAGUA',
162
        'PICHIDEGUA' => 'SAN VICENTE TAGUA TAGUA',
163
        'PICHILEMU' => 'PICHILEMU',
164
        'PAREDONES' => 'PICHILEMU',
165
        'MARCHIGUE' => 'PICHILEMU',
166
        'LITUECHE' => 'PICHILEMU',
167
        'LA ESTRELLA' => 'PICHILEMU',
168
        'TALCA' => 'TALCA',
169
        'SAN CLEMENTE' => 'TALCA',
170
        'PELARCO' => 'TALCA',
171
        'RÍO CLARO' => 'TALCA',
172
        'PENCAHUE' => 'TALCA',
173
        'MAULE' => 'TALCA',
174
        'CUREPTO' => 'TALCA',
175
        'SAN JAVIER' => 'TALCA',
176
        'LINARES' => 'LINARES',
177
        'YERBAS BUENAS' => 'LINARES',
178
        'COLBÚN' => 'LINARES',
179
        'LONGAVÍ' => 'LINARES',
180
        'VILLA ALEGRE' => 'LINARES',
181
        'CONSTITUCIÓN' => 'CONSTITUCIÓN',
182
        'EMPEDRADO' => 'CONSTITUCIÓN',
183
        'CAUQUENES' => 'CAUQUENES',
184
        'PELLUHUE' => 'CAUQUENES',
185
        'CHANCO' => 'CAUQUENES',
186
        'PARRAL' => 'PARRAL',
187
        'RETIRO' => 'PARRAL',
188
        'CURICÓ' => 'CURICÓ',
189
        'TENO' => 'CURICÓ',
190
        'ROMERAL' => 'CURICÓ',
191
        'MOLINA' => 'CURICÓ',
192
        'HUALAÑE' => 'CURICÓ',
193
        'SAGRADA FAMILIA' => 'CURICÓ',
194
        'LICANTÉN' => 'CURICÓ',
195
        'VICHUQUÉN' => 'CURICÓ',
196
        'RAUCO' => 'CURICÓ',
197
        'CONCEPCIÓN' => 'CONCEPCIÓN',
198
        'CHIGUAYANTE' => 'CONCEPCIÓN',
199
        'SAN PEDRO DE LA PAZ' => 'CONCEPCIÓN',
200
        'PENCO' => 'CONCEPCIÓN',
201
        'HUALQUI' => 'CONCEPCIÓN',
202
        'FLORIDA' => 'CONCEPCIÓN',
203
        'TOMÉ' => 'CONCEPCIÓN',
204
        'CORONEL' => 'CONCEPCIÓN',
205
        'LOTA' => 'CONCEPCIÓN',
206
        'SANTA JUANA' => 'CONCEPCIÓN',
207
        'ARAUCO' => 'CONCEPCIÓN',
208
        'CHILLÁN' => 'CHILLÁN',
209
        'PINTO' => 'CHILLÁN',
210
        'EL CARMEN' => 'CHILLÁN',
211
        'SAN IGNACIO' => 'CHILLÁN',
212
        'PEMUCO' => 'CHILLÁN',
213
        'YUNGAY' => 'CHILLÁN',
214
        'BULNES' => 'CHILLÁN',
215
        'QUILLÓN' => 'CHILLÁN',
216
        'RANQUIL' => 'CHILLÁN',
217
        'PORTEZUELO' => 'CHILLÁN',
218
        'COELEMU' => 'CHILLÁN',
219
        'TREHUACO' => 'CHILLÁN',
220
        'QUIRIHUE' => 'CHILLÁN',
221
        'COBQUECURA' => 'CHILLÁN',
222
        'NINHUE' => 'CHILLÁN',
223
        'CHILLÁN VIEJO' => 'CHILLÁN',
224
        'LOS ÁNGELES' => 'LOS ÁNGELES',
225
        'SANTA BARBARA' => 'LOS ÁNGELES',
226
        'LAJA' => 'LOS ÁNGELES',
227
        'QUILLECO' => 'LOS ÁNGELES',
228
        'NACIMIENTO' => 'LOS ÁNGELES',
229
        'NEGRETE' => 'LOS ÁNGELES',
230
        'MULCHÉN' => 'LOS ÁNGELES',
231
        'QUILACO' => 'LOS ÁNGELES',
232
        'YUMBEL' => 'LOS ÁNGELES',
233
        'CABRERO' => 'LOS ÁNGELES',
234
        'SAN ROSENDO' => 'LOS ÁNGELES',
235
        'TUCAPEL' => 'LOS ÁNGELES',
236
        'ANTUCO' => 'LOS ÁNGELES',
237
        'ALTO BÍO-BÍO' => 'LOS ÁNGELES',
238
        'SAN CARLOS' => 'SAN CARLOS',
239
        'SAN GREGORIO DE ÑINQUEN' => 'SAN CARLOS',
240
        'SAN NICOLÁS' => 'SAN CARLOS',
241
        'SAN FABIÁN DE ALICO' => 'SAN CARLOS',
242
        'TALCAHUANO' => 'TALCAHUANO',
243
        'HUALPÉN' => 'TALCAHUANO',
244
        'LEBU' => 'LEBU',
245
        'CURANILAHUE' => 'LEBU',
246
        'LOS ALAMOS' => 'LEBU',
247
        'CAÑETE' => 'LEBU',
248
        'CONTULMO' => 'LEBU',
249
        'TIRÚA' => 'LEBU',
250
        'TEMUCO' => 'TEMUCO',
251
        'VILCÚN' => 'TEMUCO',
252
        'FREIRE' => 'TEMUCO',
253
        'CUNCO' => 'TEMUCO',
254
        'LAUTARO' => 'TEMUCO',
255
        'PERQUENCO' => 'TEMUCO',
256
        'GALVARINO' => 'TEMUCO',
257
        'NUEVA IMPERIAL' => 'TEMUCO',
258
        'CARAHUE' => 'TEMUCO',
259
        'PUERTO SAAVEDRA' => 'TEMUCO',
260
        'PITRUFQUÉN' => 'TEMUCO',
261
        'GORBEA' => 'TEMUCO',
262
        'TOLTÉN' => 'TEMUCO',
263
        'LONCOCHE' => 'TEMUCO',
264
        'MELIPEUCO' => 'TEMUCO',
265
        'TEODORO SCHMIDT' => 'TEMUCO',
266
        'PADRE LAS CASAS' => 'TEMUCO',
267
        'CHOLCHOL' => 'TEMUCO',
268
        'ANGOL' => 'ANGOL',
269
        'PURÉN' => 'ANGOL',
270
        'LOS SAUCES' => 'ANGOL',
271
        'REINACO' => 'ANGOL',
272
        'COLLIPULLI' => 'ANGOL',
273
        'ERCILLA' => 'ANGOL',
274
        'VICTORIA' => 'VICTORIA',
275
        'TRAIGUÉN' => 'VICTORIA',
276
        'LUMACO' => 'VICTORIA',
277
        'CURACAUTÍN' => 'VICTORIA',
278
        'LONQUIMAY' => 'VICTORIA',
279
        'VILLARRICA' => 'VILLARRICA',
280
        'PUCÓN' => 'VILLARRICA',
281
        'CURARREHUE' => 'VILLARRICA',
282
        'VALDIVIA' => 'VALDIVIA',
283
        'MARIQUINA' => 'VALDIVIA',
284
        'LANCO' => 'LANCO',
285
        'MÁFIL' => 'VALDIVIA',
286
        'CORRAL' => 'VALDIVIA',
287
        'LOS LAGOS' => 'VALDIVIA',
288
        'PAILLACO' => 'VALDIVIA',
289
        'PANGUIPULLI' => 'PANGUIPULLI',
290
        'LA UNIÓN' => 'LA UNIÓN',
291
        'FUTRONO' => 'VALDIVIA',
292
        'RÍO BUENO' => 'LA UNIÓN',
293
        'LAGO RANCO' => 'LA UNIÓN',
294
        'PUERTO MONTT' => 'PUERTO MONTT',
295
        'CALBUCO' => 'PUERTO MONTT',
296
        'MAULLÍN' => 'PUERTO MONTT',
297
        'LOS MUERMOS' => 'PUERTO MONTT',
298
        'HUALAIHUÉ' => 'PUERTO MONTT',
299
        'PUERTO VARAS' => 'PUERTO VARAS',
300
        'COCHAMÓ' => 'PUERTO VARAS',
301
        'FRESIA' => 'PUERTO VARAS',
302
        'LLANQUIHUE' => 'PUERTO VARAS',
303
        'FRUTILLAR' => 'PUERTO VARAS',
304
        'ANCUD' => 'ANCUD',
305
        'QUEMCHI' => 'ANCUD',
306
        'OSORNO' => 'OSORNO',
307
        'PUYEHUE' => 'OSORNO',
308
        'PURRANQUE' => 'OSORNO',
309
        'RÍO NEGRO' => 'OSORNO',
310
        'SAN PABLO' => 'OSORNO',
311
        'SAN JUAN DE LA COSTA' => 'OSORNO',
312
        'PUERTO OCTAY' => 'OSORNO',
313
        'CASTRO' => 'CASTRO',
314
        'CURACO DE VÉLEZ' => 'CASTRO',
315
        'CHOCHI' => 'CASTRO',
316
        'DALCAHUE' => 'CASTRO',
317
        'PUQUELDÓN' => 'CASTRO',
318
        'QUEILÉN' => 'CASTRO',
319
        'QUELLÓN' => 'CASTRO',
320
        'CHAITÉN' => 'CHAITÉN',
321
        'PALENA' => 'CHAITÉN',
322
        'FUTALEUFÚ' => 'CHAITÉN',
323
        'COYHAIQUE' => 'COYHAIQUE',
324
        'RÍO IBAÑEZ' => 'COYHAIQUE',
325
        'O`HIGGINS' => 'COCHRANE',
326
        'TORTEL' => 'COCHRANE',
327
        'AYSÉN' => 'AYSÉN',
328
        'CISNES' => 'AYSÉN',
329
        'LAGO VERDE' => 'AYSÉN',
330
        'GUAITECAS' => 'AYSÉN',
331
        'CHILE CHICO' => 'CHILE CHICO',
332
        'COCHRANE' => 'COCHRANE',
333
        'GUADAL' => 'COCHRANE',
334
        'PUERTO BELTRAND' => 'COCHRANE',
335
        'PUNTA ARENAS' => 'PUNTA ARENAS',
336
        'RÍO VERDE' => 'PUNTA ARENAS',
337
        'SAN GREGORIO' => 'PUNTA ARENAS',
338
        'LAGUNA BLANCA' => 'PUNTA ARENAS',
339
        'CABO DE HORNOS' => 'PUNTA ARENAS',
340
        'PUERTO NATALES' => 'PUERTO NATALES',
341
        'TORRES DEL PAINE' => 'PUERTO NATALES',
342
        'PORVENIR' => 'PORVENIR',
343
        'PRIMAVERA' => 'PORVENIR',
344
        'TIMAUKEL' => 'PORVENIR',
345
        'INDEPENDENCIA' => 'SANTIAGO NORTE',
346
        'RECOLETA' => 'SANTIAGO NORTE',
347
        'HUECHURABA' => 'SANTIAGO NORTE',
348
        'CONCHALÍ' => 'SANTIAGO NORTE',
349
        'QUILICURA' => 'SANTIAGO NORTE',
350
        'COLINA' => 'SANTIAGO NORTE',
351
        'LAMPA' => 'SANTIAGO NORTE',
352
        'TILTIL' => 'SANTIAGO NORTE',
353
        'SANTIAGO' => 'SANTIAGO CENTRO',
354
        'CERRO NAVIA' => 'SANTIAGO PONIENTE',
355
        'CURACAVÍ' => 'SANTIAGO PONIENTE',
356
        'ESTACIÓN CENTRAL' => 'SANTIAGO PONIENTE',
357
        'LO PRADO' => 'SANTIAGO PONIENTE',
358
        'PUDAHUEL' => 'SANTIAGO PONIENTE',
359
        'QUINTA NORMAL' => 'SANTIAGO PONIENTE',
360
        'RENCA' => 'SANTIAGO PONIENTE',
361
        'MELIPILLA' => 'MELIPILLA',
362
        'SAN PEDRO' => 'MELIPILLA',
363
        'ALHUÉ' => 'MELIPILLA',
364
        'MARÍA PINTO' => 'MELIPILLA',
365
        'MAIPÚ' => 'MAIPÚ',
366
        'CERRILLOS' => 'MAIPÚ',
367
        'PADRE HURTADO' => 'MAIPÚ',
368
        'PEÑAFLOR' => 'MAIPÚ',
369
        'TALAGANTE' => 'MAIPÚ',
370
        'EL MONTE' => 'MAIPÚ',
371
        'ISLA DE MAIPO' => 'MAIPÚ',
372
        'LAS CONDES' => 'SANTIAGO ORIENTE',
373
        'VITACURA' => 'SANTIAGO ORIENTE',
374
        'LO BARNECHEA' => 'SANTIAGO ORIENTE',
375
        'ÑUÑOA' => 'ÑUÑOA',
376
        'LA REINA' => 'ÑUÑOA',
377
        'MACUL' => 'ÑUÑOA',
378
        'PEÑALOLÉN' => 'ÑUÑOA',
379
        'PROVIDENCIA' => 'PROVIDENCIA',
380
        'SAN MIGUEL' => 'SANTIAGO SUR',
381
        'LA CISTERNA' => 'SANTIAGO SUR',
382
        'SAN JOAQUÍN' => 'SANTIAGO SUR',
383
        'PEDRO AGUIRRE CERDA' => 'SANTIAGO SUR',
384
        'LO ESPEJO' => 'SANTIAGO SUR',
385
        'LA GRANJA' => 'SANTIAGO SUR',
386
        'LA PINTANA' => 'SANTIAGO SUR',
387
        'SAN RAMÓN' => 'SANTIAGO SUR',
388
        'LA FLORIDA' => 'LA FLORIDA',
389
        'PUENTE ALTO' => 'LA FLORIDA',
390
        'PIRQUE' => 'LA FLORIDA',
391
        'SAN JOSÉ DE MAIPO' => 'LA FLORIDA',
392
        'SAN BERNARDO' => 'SAN BERNARDO',
393
        'CALERA DE TANGO' => 'SAN BERNARDO',
394
        'EL BOSQUE' => 'SAN BERNARDO',
395
        'BUIN' => 'BUIN',
396
        'PAINE' => 'BUIN',
397
    ]; /// Direcciones regionales del SII según la comuna
398
399
    /**
400
     * Método que permite asignar el nombre del servidor del SII que se
401
     * usará para las consultas al SII
402
     * @param servidor Servidor que se usará, si es https://maullin2.sii.cl, entonces se debe pasar como valor maullin2
403
     * @param certificacion Permite definir si se está cambiando el servidor de certificación o el de producción
404
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
405
     * @version 2015-09-14
406
     */
407
    public static function setServidor($servidor = 'maullin', $certificacion = Sii::CERTIFICACION)
408
    {
409
        self::$config['servidor'][$certificacion] = $servidor;
410
    }
411
412
    /**
413
     * Método que entrega el nombre del servidor a usar según el ambiente
414
     * @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará
415
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
416
     * @version 2016-08-01
417
     */
418
    public static function getServidor($ambiente = null)
419
    {
420
        return self::$config['servidor'][self::getAmbiente($ambiente)];
421
    }
422
423
    /**
424
     * Método que entrega la URL de un recurso en el SII según el ambiente que se esté usando
425
     * @param recurso Recurso del sitio del SII que se desea obtener la URL
426
     * @param ambiente Ambiente que se desea obtener el servidor, si es null se autodetectará
427
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
428
     * @version 2017-09-11
429
     */
430
    public static function getURL($recurso, $ambiente = null)
431
    {
432
        return 'https://'.self::getServidor($ambiente).'.sii.cl'.$recurso;
433
    }
434
435
    /**
436
     * Método para obtener el WSDL
437
     *
438
     * \code{.php}
439
     *   $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed'); // WSDL para pedir semilla
440
     * \endcode
441
     *
442
     * Para forzar el uso del WSDL de certificación hay dos maneras, una es
443
     * pasando un segundo parámetro al método get con valor Sii::CERTIFICACION:
444
     *
445
     * \code{.php}
446
     *   $wsdl = \sasco\LibreDTE\Sii::wsdl('CrSeed', \sasco\LibreDTE\Sii::CERTIFICACION);
447
     * \endcode
448
     *
449
     * La otra manera, para evitar este segundo parámetro, es asignar el valor a
450
     * través de la configuración:
451
     *
452
     * \code{.php}
453
     *   \sasco\LibreDTE\Sii::setAmbiente(\sasco\LibreDTE\Sii::CERTIFICACION);
454
     * \endcode
455
     *
456
     * @param servicio Servicio por el cual se está solicitando su WSDL
457
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática)
458
     * @return URL del WSDL del servicio según ambiente solicitado
459
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
460
     * @version 2016-06-11
461
     */
462
    public static function wsdl($servicio, $ambiente = null)
463
    {
464
        // determinar ambiente que se debe usar
465
        $ambiente = self::getAmbiente($ambiente);
466
        // entregar WSDL local (modificados para ambiente de certificación)
467
        if ($ambiente==self::CERTIFICACION) {
468
            $wsdl = dirname(dirname(__FILE__)).'/wsdl/'.self::$config['servidor'][$ambiente].'/'.$servicio.'.jws';
469
            if (is_readable($wsdl))
470
                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...
471
        }
472
        // entregar WSDL oficial desde SII
473
        $location = isset(self::$config['wsdl'][$servicio]) ? self::$config['wsdl'][$servicio] : self::$config['wsdl']['*'];
474
        $wsdl = str_replace(
475
            ['{servidor}', '{servicio}'],
476
            [self::$config['servidor'][$ambiente], $servicio],
477
            $location
478
        );
479
        // entregar wsdl
480
        return $wsdl;
481
    }
482
483
    /**
484
     * Método para realizar una solicitud al servicio web del SII
485
     * @param wsdl Nombre del WSDL que se usará
486
     * @param request Nombre de la función que se ejecutará en el servicio web
487
     * @param args Argumentos que se pasarán al servicio web
488
     * @param retry Intentos que se realizarán como máximo para obtener respuesta
489
     * @return Objeto SimpleXMLElement con la espuesta del servicio web consultado
490
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
491
     * @version 2016-08-28
492
     */
493
    public static function request($wsdl, $request, $args = null, $retry = null)
494
    {
495
        if (is_numeric($args)) {
496
            $retry = (int)$args;
497
            $args = null;
498
        }
499
        if (!$retry)
500
            $retry = self::$retry;
501
        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...
502
            $args = [$args];
503 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...
504
            if (self::getAmbiente()==self::PRODUCCION) {
505
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
506
                \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...
507
            }
508
            $options = ['stream_context' => stream_context_create([
509
                'ssl' => [
510
                    'verify_peer' => false,
511
                    'verify_peer_name' => false,
512
                    'allow_self_signed' => true
513
                ]
514
            ])];
515
        } else {
516
            $options = [];
517
        }
518
        try {
519
            $soap = new \SoapClient(self::wsdl($wsdl), $options);
520
        } catch (\Exception $e) {
521
            $msg = $e->getMessage();
522 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...
523
                $msg .= ': '.$e->getTrace()[0]['args'][1];
524
            }
525
            \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...
526
            return false;
527
        }
528
        for ($i=0; $i<$retry; $i++) {
529
            try {
530
                if ($args) {
531
                    $body = call_user_func_array([$soap, $request], $args);
532
                } else {
533
                    $body = $soap->$request();
534
                }
535
                break;
536
            } catch (\Exception $e) {
537
                $msg = $e->getMessage();
538 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...
539
                    $msg .= ': '.$e->getTrace()[0]['args'][1];
540
                }
541
                \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...
542
                $body = null;
543
            }
544
        }
545 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...
546
            \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...
547
            return false;
548
        }
549
        return new \SimpleXMLElement($body, LIBXML_COMPACT);
550
    }
551
552
    /**
553
     * Método que permite indicar si se debe o no verificar el certificado SSL
554
     * del SII
555
     * @param verificar =true si se quiere verificar certificado, =false en caso que no (por defecto se verifica)
556
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
557
     * @version 2015-09-16
558
     */
559
    public static function setVerificarSSL($verificar = true)
560
    {
561
        self::$verificar_ssl = $verificar;
562
    }
563
564
    /**
565
     * Método que indica si se está o no verificando el SSL en las conexiones al SII
566
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
567
     * @version 2017-05-11
568
     */
569
    public static function getVerificarSSL()
570
    {
571
        return self::$verificar_ssl;
572
    }
573
574
    /**
575
     * Método que realiza el envío de un DTE al SII
576
     * Referencia: http://www.sii.cl/factura_electronica/factura_mercado/envio.pdf
577
     * @param usuario RUN del usuario que envía el DTE
578
     * @param empresa RUT de la empresa emisora del DTE
579
     * @param dte Documento XML con el DTE que se desea enviar a SII
580
     * @param token Token de autenticación automática ante el SII
581
     * @param gzip Permite enviar el archivo XML comprimido al servidor
582
     * @param retry Intentos que se realizarán como máximo para obtener respuesta
583
     * @return Respuesta XML desde SII o bien null si no se pudo obtener respuesta
584
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
585
     * @version 2017-10-23
586
     */
587
    public static function enviar($usuario, $empresa, $dte, $token, $gzip = false, $retry = null)
588
    {
589
        // definir datos que se usarán en el envío
590
        list($rutSender, $dvSender) = explode('-', str_replace('.', '', $usuario));
591
        list($rutCompany, $dvCompany) = explode('-', str_replace('.', '', $empresa));
592
        if (strpos($dte, '<?xml')===false) {
593
            $dte = '<?xml version="1.0" encoding="ISO-8859-1"?>'."\n".$dte;
594
        }
595 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...
596
            $file = sys_get_temp_dir().'/dte_'.md5(microtime().$token.$dte).'.'.($gzip?'gz':'xml');
597
        } while (file_exists($file));
598
        if ($gzip) {
599
            $dte = gzencode($dte);
600 View Code Duplication
            if ($dte===false) {
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...
601
                \sasco\LibreDTE\Log::write(Estado::ENVIO_ERROR_GZIP, Estado::get(Estado::ENVIO_ERROR_GZIP));
0 ignored issues
show
Documentation introduced by
\sasco\LibreDTE\Estado::...tado::ENVIO_ERROR_GZIP) 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...
602
                return false;
603
            }
604
        }
605
        file_put_contents($file, $dte);
606
        $data = [
607
            'rutSender' => $rutSender,
608
            'dvSender' => $dvSender,
609
            'rutCompany' => $rutCompany,
610
            'dvCompany' => $dvCompany,
611
            'archivo' => curl_file_create(
612
                $file,
613
                $gzip ? 'application/gzip' : 'application/xml',
614
                basename($file)
615
            ),
616
        ];
617
        // definir reintentos si no se pasaron
618
        if (!$retry) {
619
            $retry = self::$retry;
620
        }
621
        // crear sesión curl con sus opciones
622
        $curl = curl_init();
623
        $header = [
624
            'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)',
625
            'Referer: https://libredte.cl',
626
            'Cookie: TOKEN='.$token,
627
        ];
628
        $url = 'https://'.self::$config['servidor'][self::getAmbiente()].'.sii.cl/cgi_dte/UPL/DTEUpload';
629
        curl_setopt($curl, CURLOPT_POST, true);
630
        curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
631
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
632
        curl_setopt($curl, CURLOPT_URL, $url);
633
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
634
        // si no se debe verificar el SSL se asigna opción a curl, además si
635
        // se está en el ambiente de producción y no se verifica SSL se
636
        // generará una entrada en el log
637 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...
638
            if (self::getAmbiente()==self::PRODUCCION) {
639
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
640
                \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...
641
            }
642
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
643
        }
644
        // enviar XML al SII
645 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...
646
            $response = curl_exec($curl);
647
            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...
648
                break;
649
            }
650
        }
651
        unlink($file);
652
        // verificar respuesta del envío y entregar error en caso que haya uno
653 View Code Duplication
        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...
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...
654
            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...
655
                \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...
656
            }
657
            if ($response=='Error 500') {
658
                \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...
659
            }
660
            return false;
661
        }
662
        // cerrar sesión curl
663
        curl_close($curl);
664
        // crear XML con la respuesta y retornar
665
        try {
666
            $xml = new \SimpleXMLElement($response, LIBXML_COMPACT);
667
        } 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...
668
            \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...
669
            return false;
670
        }
671
        if ($xml->STATUS!=0) {
672
            \sasco\LibreDTE\Log::write(
673
                $xml->STATUS,
674
                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...
675
            );
676
        }
677
        return $xml;
678
    }
679
680
    /**
681
     * Método para obtener la clave pública (certificado X.509) del SII
682
     *
683
     * \code{.php}
684
     *   $pub_key = \sasco\LibreDTE\Sii::cert(100); // Certificado IDK 100 (certificación)
685
     * \endcode
686
     *
687
     * @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
688
     * @return Contenido del certificado
689
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
690
     * @version 2015-09-16
691
     */
692
    public static function cert($idk = null)
693
    {
694
        // si se pasó un idk y existe el archivo asociado se entrega
695
        if ($idk) {
696
            $cert = dirname(dirname(__FILE__)).'/certs/'.$idk.'.cer';
697
            if (is_readable($cert))
698
                return file_get_contents($cert);
699
        }
700
        // buscar certificado y entregar si existe o =false si no
701
        $ambiente = self::getAmbiente();
702
        $cert = dirname(dirname(__FILE__)).'/certs/'.self::$config['certs'][$ambiente].'.cer';
703
        if (!is_readable($cert)) {
704
            \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...
705
            return false;
706
        }
707
        return file_get_contents($cert);
708
    }
709
710
    /**
711
     * Método que asigna el ambiente que se usará por defecto (si no está
712
     * asignado con la constante _LibreDTE_CERTIFICACION_)
713
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION
714
     * @warning No se está verificando SSL en ambiente de certificación
715
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
716
     * @version 2016-08-28
717
     */
718
    public static function setAmbiente($ambiente = self::PRODUCCION)
719
    {
720
        $ambiente = $ambiente ? self::CERTIFICACION : self::PRODUCCION;
721
        if ($ambiente==self::CERTIFICACION) {
722
            self::setVerificarSSL(false);
723
        }
724
        self::$ambiente = $ambiente;
725
    }
726
727
    /**
728
     * Método que determina el ambiente que se debe utilizar: producción o
729
     * certificación
730
     * @param ambiente Ambiente a usar: Sii::PRODUCCION o Sii::CERTIFICACION o null (para detección automática)
731
     * @return Ambiente que se debe utilizar
732
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
733
     * @version 2015-09-07
734
     */
735
    public static function getAmbiente($ambiente = null)
736
    {
737
        if ($ambiente===null) {
738
            if (defined('_LibreDTE_CERTIFICACION_'))
739
                $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 743 which is incompatible with the return type documented by sasco\LibreDTE\Sii::getAmbiente of type sasco\LibreDTE\Ambiente.
Loading history...
740
            else
741
                $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 743 which is incompatible with the return type documented by sasco\LibreDTE\Sii::getAmbiente of type sasco\LibreDTE\Ambiente.
Loading history...
742
        }
743
        return $ambiente;
744
    }
745
746
    /**
747
     * Método que entrega la tasa de IVA vigente
748
     * @return Tasa de IVA vigente
749
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
750
     * @version 2015-09-03
751
     */
752
    public static function getIVA()
753
    {
754
        return self::IVA;
755
    }
756
757
    /**
758
     * Método que entrega un arreglo con todos los datos de los contribuyentes
759
     * que operan con factura electrónica descargados desde el SII
760
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
761
     * @version 2017-07-07
762
     */
763
    public static function getContribuyentes(\sasco\LibreDTE\FirmaElectronica $Firma, $ambiente = null, $dia = null)
764
    {
765
        // solicitar token
766
        $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...
767
        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...
768
            return false;
769
        // definir ambiente y servidor
770
        $ambiente = self::getAmbiente($ambiente);
771
        $servidor = self::$config['servidor'][$ambiente];
772
        // preparar consulta curl
773
        $curl = curl_init();
774
        $header = [
775
            'User-Agent: Mozilla/4.0 (compatible; PROG 1.0; LibreDTE)',
776
            'Referer: https://'.$servidor.'.sii.cl/cvc/dte/ee_empresas_dte.html',
777
            'Cookie: TOKEN='.$token,
778
            'Accept-Encoding' => 'gzip, deflate, sdch',
779
        ];
780
        $dia = $dia===null ? date('Ymd') : str_replace('-', '', $dia);
781
        $url = 'https://'.$servidor.'.sii.cl/cvc_cgi/dte/ce_empresas_dwnld?NOMBRE_ARCHIVO=ce_empresas_dwnld_'.$dia.'.csv';
782
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
783
        curl_setopt($curl, CURLOPT_URL, $url);
784
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
785
        // si no se debe verificar el SSL se asigna opción a curl, además si
786
        // se está en el ambiente de producción y no se verifica SSL se
787
        // generará un error de nivel E_USER_NOTICE
788 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...
789
            if ($ambiente==self::PRODUCCION) {
790
                $msg = Estado::get(Estado::ENVIO_SSL_SIN_VERIFICAR);
791
                \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...
792
            }
793
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
794
        }
795
        // realizar consulta curl
796
        $response = curl_exec($curl);
797
        if (!$response)
798
            return false;
799
        // cerrar sesión curl
800
        curl_close($curl);
801
        // entregar datos del archivo CSV
802
        ini_set('memory_limit', '1024M');
803
        $lines = explode("\n", $response);
804
        $n_lines = count($lines);
805
        $data = [];
806
        for ($i=1; $i<$n_lines; $i++) {
807
            $row = str_getcsv($lines[$i], ';', '');
808
            unset($lines[$i]);
809
            if (!isset($row[5]))
810
                continue;
811
            for ($j=0; $j<6; $j++)
812
                $row[$j] = trim($row[$j]);
813
            $row[1] = utf8_decode($row[1]);
814
            $row[4] = strtolower($row[4]);
815
            $row[5] = strtolower($row[5]);
816
            $data[] = $row;
817
        }
818
        return $data;
819
    }
820
821
    /**
822
     * Método que entrega la dirección regional según la comuna que se esté
823
     * consultando
824
     * @param comuna de la sucursal del emior o bien código de la sucursal del SII
825
     * @return Dirección regional del SII
826
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
827
     * @version 2017-11-07
828
     */
829
    public static function getDireccionRegional($comuna)
830
    {
831
        if (!$comuna) {
832
            return 'N.N.';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return 'N.N.'; (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...
833
        }
834
        if (!is_numeric($comuna)) {
835
            $direccion = mb_strtoupper($comuna, 'UTF-8');
836
            return isset(self::$direcciones_regionales[$direccion]) ? self::$direcciones_regionales[$direccion] : $direccion;
837
        }
838
        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...
839
    }
840
841
}
842