Completed
Pull Request — master (#50)
by
unknown
03:33
created

XML::loadXML()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
rs 8.8571
cc 5
eloc 8
nc 8
nop 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 35 and the first side effect is on line 28.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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
// errores de XML se almacenarán internamente y no serán procesados por PHP
27
// se deberán recuperar con: libxml_get_errors()
28
libxml_use_internal_errors(true);
29
30
/**
31
 * Clase para trabajar con XMLs
32
 * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
33
 * @version 2017-01-20
34
 */
35
class XML extends \DomDocument
36
{
37
38
    /**
39
     * Constructor de la clase XML
40
     * @param version Versión del documento XML
41
     * @param encoding Codificación del documento XML
42
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
43
     * @version 2015-08-05
44
     */
45
    public function __construct($version = '1.0', $encoding = 'ISO-8859-1')
46
    {
47
        parent::__construct($version, $encoding);
48
        $this->formatOutput = true;
49
    }
50
51
    /**
52
     * Método que genera nodos XML a partir de un arreglo
53
     * @param data Arreglo con los datos que se usarán para generar XML
54
     * @param namespace Arreglo con el espacio de nombres para el XML que se generará (URI y prefijo)
55
     * @param parent DOMElement padre para los elementos, o =null para que sea la raíz
56
     * @return Objeto \sasco\LibreDTE\XML
57
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
58
     * @version 2017-10-22
59
     */
60
    public function generate(array $data, array $namespace = null, \DOMElement &$parent = null)
61
    {
62
        if ($parent===null) {
63
            $parent = &$this;
64
        }
65
        foreach ($data as $key => $value) {
66
            if ($key=='@attributes') {
67
                if ($value!==false) {
68
                    foreach ($value as $attr => $val) {
69
                        if ($val!==false) {
70
                            $parent->setAttribute($attr, $val);
0 ignored issues
show
Bug introduced by
The method setAttribute does only exist in DOMElement, but not in sasco\LibreDTE\XML.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
71
                        }
72
                    }
73
                }
74
            } else if ($key=='@value') {
75
                $parent->nodeValue = $this->sanitize($value);
76
            } else {
77
                if (is_array($value)) {
78
                    if (!empty($value)) {
79
                        $keys = array_keys($value);
80
                        if (!is_int($keys[0])) {
81
                            $value = [$value];
82
                        }
83
                        foreach ($value as $value2) {
84
                            if ($namespace) {
85
                                $Node = $this->createElementNS($namespace[0], $namespace[1].':'.$key);
86
                            } else {
87
                                $Node = $this->createElement($key);
88
                            }
89
                            $parent->appendChild($Node);
90
                            $this->generate($value2, $namespace, $Node);
91
                        }
92
                    }
93
                } else {
94
                    if (is_object($value) and $value instanceof \DOMElement) {
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...
95
                        $Node = $this->importNode($value, true);
96
                        $parent->appendChild($Node);
97
                    } else {
98
                        if ($value!==false) {
99
                            if ($namespace) {
100
                                $Node = $this->createElementNS($namespace[0], $namespace[1].':'.$key, $this->iso2utf($this->sanitize($value)));
101
                            } else {
102
                                $Node = $this->createElement($key, $this->iso2utf($this->sanitize($value)));
103
                            }
104
                            $parent->appendChild($Node);
105
                        }
106
                    }
107
                }
108
            }
109
        }
110
        return $this;
111
    }
112
113
    /**
114
     * Método que sanitiza los valores que son asignados a los tags del XML
115
     * @param txt String que que se asignará como valor al nodo XML
116
     * @return String sanitizado
117
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
118
     * @version 2015-09-02
119
     */
120
    private function sanitize($txt)
121
    {
122
        // si no se paso un texto o bien es un número no se hace nada
123
        if (!$txt or is_numeric($txt))
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...
124
            return $txt;
125
        // convertir "predefined entities" de XML
126
        $txt = str_replace(
127
            ['&amp;', '&#38;', '&lt;', '&#60;', '&gt;', '&#62', '&quot;', '&#34;', '&apos;', '&#39;'],
128
            ['&', '&', '<', '<', '>', '>', '"', '"', '\'', '\''],
129
            $txt
130
        );
131
        $txt = str_replace('&', '&amp;', $txt);
132
        /*$txt = str_replace(
133
            ['&', '"', '\''],
134
            ['&amp;', '&quot;', '&apos;'],
135
            $txt
136
        );*/
137
        // entregar texto sanitizado
138
        return $txt;
139
    }
140
141
    /**
142
     * Método que carga un string XML en el Objeto
143
     * @param source String con el documento XML a cargar
144
     * @param options Opciones para la carga del XML
145
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
146
     * @version 2016-11-21
147
     */
148
     public function loadXML($source, $options = null)
149
     {
150
         $tRetorno = $source ? parent::load($this->iso2utf($source), $options) : false;
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (load() instead of loadXML()). Are you sure this is correct? If so, you might want to change this to $this->load().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
151
152
         if (!$tRetorno){
153
           $tRetorno = parent::loadXML($this->iso2utf($source), $options);
154
         }
155
         if(!$tRetorno){
156
           foreach (libxml_get_errors() as $error) {
157
               echo "Error: " . $error->message;
158
           }
159
         }
160
         return $tRetorno;
161
     }
162
163
    /**
164
     * Método para realizar consultas XPATH al documento XML
165
     * @param expression Expresión XPath a ejecutar
166
     * @return DOMNodeList
167
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
168
     * @version 2015-08-05
169
     */
170
    public function xpath($expression)
171
    {
172
        return (new \DOMXPath($this))->query($expression);
173
    }
174
175
    /**
176
     * Método que entrega el código XML aplanado y con la codificación que
177
     * corresponde
178
     * @param xpath XPath para consulta al XML y extraer sólo una parte
179
     * @return String con código XML aplanado
180
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
181
     * @version 2017-01-20
182
     */
183
    public function getFlattened($xpath = null)
184
    {
185
        if ($xpath) {
186
            $node = $this->xpath($xpath)->item(0);
187
            if (!$node)
188
                return false;
189
            $xml = $this->utf2iso($node->C14N());
190
            $xml = $this->fixEntities($xml);
191
        } else {
192
            $xml = $this->C14N();
193
        }
194
        $xml = preg_replace("/\>\n\s+\</", '><', $xml);
195
        $xml = preg_replace("/\>\n\t+\</", '><', $xml);
196
        $xml = preg_replace("/\>\n+\</", '><', $xml);
197
        return trim($xml);
198
    }
199
200
    /**
201
     * Método que codifica el string como ISO-8859-1 si es que fue pasado como
202
     * UTF-8
203
     * @param string String en UTF-8 o ISO-8859-1
204
     * @return String en ISO-8859-1
205
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
206
     * @version 2016-04-03
207
     */
208
    private function utf2iso($string)
209
    {
210
        return mb_detect_encoding($string, ['UTF-8', 'ISO-8859-1']) != 'ISO-8859-1' ? utf8_decode($string) : $string;
211
    }
212
213
    /**
214
     * Método que codifica el string como UTF-8 si es que fue pasado como
215
     * ISO-8859-1
216
     * @param string String en UTF-8 o ISO-8859-1
217
     * @return String en ISO-8859-1
218
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
219
     * @version 2016-04-03
220
     */
221
    private function iso2utf($string)
222
    {
223
        return $string;
224
        //return mb_detect_encoding($string, ['ISO-8859-1', 'UTF-8']) == 'ISO-8859-1' ? utf8_encode($string) : $string;
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
225
    }
226
227
    /**
228
     * Método que convierte el XML a un arreglo
229
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
230
     * @version 2016-06-11
231
     */
232
    public function toArray(\DOMElement $dom = null, array &$array = null, $arregloNodos = false)
233
    {
234
        // determinar valores de parámetros
235
        if (!$dom)
236
            $dom = $this->documentElement;
237
        if (!$dom)
238
            return false;
239
        if ($array===null)
240
            $array = [$dom->tagName => null];
241
        // agregar atributos del nodo
242
        if ($dom->hasAttributes()) {
243
            $array[$dom->tagName]['@attributes'] = [];
244
            foreach ($dom->attributes as $attribute) {
245
                $array[$dom->tagName]['@attributes'][$attribute->name] = $attribute->value;
246
            }
247
        }
248
        // agregar nodos hijos
249
        if ($dom->hasChildNodes()) {
250
            foreach($dom->childNodes as $child) {
251
                if ($child instanceof \DOMText) {
252
                    $textContent = trim($child->textContent);
253
                    if ($textContent!="") {
254
                        if ($dom->childNodes->length==1 and empty($array[$dom->tagName])) {
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...
255
                            $array[$dom->tagName] = $textContent;
256
                        } else
257
                            $array[$dom->tagName]['@value'] = $textContent;
258
                    }
259
                }
260
                else if ($child instanceof \DOMElement) {
261
                    $nodos_gemelos = $this->countTwins($dom, $child->tagName);
262
                    if ($nodos_gemelos==1) {
263
                        if ($arregloNodos)
264
                            $this->toArray($child, $array);
265
                        else
266
                            $this->toArray($child, $array[$dom->tagName]);
267
                    }
268
                    // crear arreglo con nodos hijos que tienen el mismo nombre de tag
269
                    else {
270
                        if (!isset($array[$dom->tagName][$child->tagName]))
271
                            $array[$dom->tagName][$child->tagName] = [];
272
                        $siguiente = count($array[$dom->tagName][$child->tagName]);
273
                        $array[$dom->tagName][$child->tagName][$siguiente] = [];
274
                        $this->toArray($child, $array[$dom->tagName][$child->tagName][$siguiente], true);
275
                    }
276
                }
277
            }
278
        }
279
        // entregar arreglo
280
        return $array;
281
    }
282
283
    /**
284
     * Método que cuenta los nodos con el mismo nombre hijos deun DOMElement
285
     * No sirve usar: $dom->getElementsByTagName($tagName)->length ya que esto
286
     * entrega todos los nodos con el nombre, sean hijos, nietos, etc.
287
     * @return Cantidad de nodos hijos con el mismo nombre en el DOMElement
288
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
289
     * @version 2015-09-07
290
     */
291
    private function countTwins(\DOMElement $dom, $tagName)
292
    {
293
        $twins = 0;
294
        foreach ($dom->childNodes as $child) {
295
            if ($child instanceof \DOMElement and $child->tagName==$tagName)
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...
296
                $twins++;
297
        }
298
        return $twins;
299
    }
300
301
    /**
302
     * Método que entrega los errores de libxml que pueden existir
303
     * @return Arreglo con los errores XML que han ocurrido
304
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
305
     * @version 2015-09-18
306
     */
307
    public function getErrors()
308
    {
309
        $errors = [];
310
        foreach (libxml_get_errors() as $e)
311
            $errors[] = $e->message;
312
        return $errors;
313
    }
314
315
    /**
316
     * Método que entrega el nombre del tag raíz del XML
317
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
318
     * @version 2015-12-14
319
     */
320
    public function getName()
321
    {
322
        return $this->documentElement->tagName;
323
    }
324
325
    /**
326
     * Método que entrega el nombre del archivo del schema del XML
327
     * @return Nombre del schema o bien =false si no se encontró
328
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
329
     * @version 2015-12-14
330
     */
331
    public function getSchema()
332
    {
333
        $schemaLocation = $this->documentElement->getAttribute('xsi:schemaLocation');
334
        if (!$schemaLocation or strpos($schemaLocation, ' ')===false)
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...
335
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by sasco\LibreDTE\XML::getSchema of type sasco\LibreDTE\Nombre.

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...
336
        list($uri, $xsd) = explode(' ', $schemaLocation);
0 ignored issues
show
Unused Code introduced by
The assignment to $uri is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
337
        return $xsd;
338
    }
339
340
    /**
341
     * Wrapper para saveXML() y corregir entities
342
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
343
     * @version 2017-01-20
344
     */
345
    public function saveXML(\DOMNode $node = null, $options = null)
346
    {
347
        $xml = parent::saveXML($node, $options);
348
        $xml = $this->fixEntities($xml);
349
        return $xml;
350
    }
351
352
    /**
353
     * Wrapper para C14N() y corregir entities
354
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
355
     * @version 2017-01-20
356
     */
357
    public function C14N($exclusive = null, $with_comments = null, array $xpath = null, array $ns_prefixes = null)
358
    {
359
        $xml = parent::C14N($exclusive, $with_comments, $xpath, $ns_prefixes);
360
        $xml = $this->fixEntities($xml);
361
        return $xml;
362
    }
363
364
    /**
365
     * Método que corrige las entities ' (&apos;) y " (&quot;) ya que el SII no
366
     * respeta el estándar y las requiere convertidas
367
     * @author Esteban De La Fuente Rubio, DeLaF (esteban[at]sasco.cl)
368
     * @version 2017-01-20
369
     */
370
    private function fixEntities($xml)
371
    {
372
        $newXML = '';
373
        $n_letras = strlen($xml);
374
        $convertir = false;
375
        for ($i=0; $i<$n_letras; ++$i) {
376
            if ($xml[$i]=='>')
377
                $convertir = true;
378
            if ($xml[$i]=='<')
379
                $convertir = false;
380
            if ($convertir) {
381
                $l = $xml[$i]=='\'' ? '&apos;' : ($xml[$i]=='"' ? '&quot;' : $xml[$i]);
382
            } else {
383
                $l = $xml[$i];
384
            }
385
            $newXML .= $l;
386
        }
387
        return $newXML;
388
    }
389
390
}
391