Completed
Push — master ( 75ac31...11edf4 )
by Tobias
08:15 queued 06:58
created

src/Loader/XliffLoader.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the PHP Translation package.
5
 *
6
 * (c) PHP Translation team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Translation\SymfonyStorage\Loader;
13
14
use Nyholm\NSA;
15
use Symfony\Component\Translation\Loader\XliffFileLoader;
16
use Symfony\Component\Translation\MessageCatalogue;
17
use Symfony\Component\Translation\Exception\InvalidResourceException;
18
use Translation\SymfonyStorage\Loader\Port\SymfonyPort;
19
20
/**
21
 * This class is an ugly hack to allow loading Xliff from string content.
22
 *
23
 * @author Tobias Nyholm <[email protected]>
24
 */
25
class XliffLoader extends XliffFileLoader
26
{
27
    /**
28
     * @var SymfonyPort|null
29
     */
30
    private $sfPort;
31
32
    /**
33
     * @param string           $content   xml content
34
     * @param MessageCatalogue $catalogue
35
     * @param string           $domain
36
     */
37 6
    public function extractFromContent($content, MessageCatalogue $catalogue, $domain)
38
    {
39
        try {
40 6
            $dom = $this->loadFileContent($content);
41 6
        } catch (\InvalidArgumentException $e) {
42 2
            throw new InvalidResourceException(sprintf('Unable to load data: %s', $e->getMessage()), $e->getCode(), $e);
43 2
        }
44
45 4
        if (method_exists($this, 'getVersionNumber')) {
46 4
            $xliffVersion = NSA::invokeMethod($this, 'getVersionNumber', $dom);
47 4
            NSA::invokeMethod($this, 'validateSchema', $xliffVersion, $dom, NSA::invokeMethod($this, 'getSchema', $xliffVersion));
48 4
        } else {
49
            // Symfony 2.7
50
            if (null === $this->sfPort) {
51
                $this->sfPort = new SymfonyPort();
52
            }
53
            $xliffVersion = $this->sfPort->getVersionNumber($dom);
0 ignored issues
show
Deprecated Code introduced by
The method Translation\SymfonyStora...ort::getVersionNumber() has been deprecated with message: Will be removed when we drop support for SF2.7

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
54
        }
55
56 4
        if ('1.2' === $xliffVersion) {
57 2
            if (method_exists($this, 'extractXliff1')) {
58 2
                NSA::invokeMethod($this, 'extractXliff1', $dom, $catalogue, $domain);
59 2
            } else {
60 1
                if (null === $this->sfPort) {
61
                    $this->sfPort = new SymfonyPort();
62 1
                }
63
                $this->sfPort->extractXliff1($dom, $catalogue, $domain);
0 ignored issues
show
Deprecated Code introduced by
The method Translation\SymfonyStora...nyPort::extractXliff1() has been deprecated with message: Will be removed when we drop support for SF2.7

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
64
            }
65 2
        }
66
67 4
        if ('2.0' === $xliffVersion) {
68 2
            if (null === $this->sfPort) {
69 2
                $this->sfPort = new SymfonyPort();
70 2
            }
71 2
            $this->sfPort->extractXliff2($dom, $catalogue, $domain);
72 2
        }
73 4
    }
74
75
    /**
76
     * Loads an XML file.
77
     *
78
     * Taken and modified from Symfony\Component\Config\Util\XmlUtils
79
     *
80
     * @author Fabien Potencier <[email protected]>
81
     * @author Martin Hasoň <[email protected]>
82
     *
83
     * @param string $content An XML file path
84
     *
85
     * @return \DOMDocument
86
     *
87
     * @throws \InvalidArgumentException When loading of XML file returns error
88
     */
89 6
    private function loadFileContent($content)
90
    {
91 6
        if ('' === trim($content)) {
92 1
            throw new \InvalidArgumentException('Content does not contain valid XML, it is empty.');
93
        }
94
95 5
        $internalErrors = libxml_use_internal_errors(true);
96 5
        $disableEntities = libxml_disable_entity_loader(true);
97 5
        libxml_clear_errors();
98
99 5
        $dom = new \DOMDocument();
100 5
        $dom->validateOnParse = true;
101 5
        if (!$dom->loadXML($content, LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) {
102 1
            libxml_disable_entity_loader($disableEntities);
103
104 1
            throw new \InvalidArgumentException(implode("\n", static::getXmlErrors($internalErrors)));
0 ignored issues
show
Since getXmlErrors() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getXmlErrors() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
105
        }
106
107 4
        $dom->normalizeDocument();
108
109 4
        libxml_use_internal_errors($internalErrors);
110 4
        libxml_disable_entity_loader($disableEntities);
111
112 4
        foreach ($dom->childNodes as $child) {
113 4
            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
114
                throw new \InvalidArgumentException('Document types are not allowed.');
115
            }
116 4
        }
117
118 4
        libxml_clear_errors();
119 4
        libxml_use_internal_errors($internalErrors);
120
121 4
        return $dom;
122
    }
123
124 1
    private function getXmlErrors($internalErrors)
0 ignored issues
show
Consider using a different method name as you override a private method of the parent class.

Overwriting private methods is generally fine as long as you also use private visibility. It might still be preferable for understandability to use a different method name.

Loading history...
125
    {
126 1
        $errors = [];
127 1
        foreach (libxml_get_errors() as $error) {
128 1
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
129 1
                LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR',
130 1
                $error->code,
131 1
                trim($error->message),
132 1
                $error->file ?: 'n/a',
133 1
                $error->line,
134 1
                $error->column
135 1
            );
136 1
        }
137
138 1
        libxml_clear_errors();
139 1
        libxml_use_internal_errors($internalErrors);
140
141 1
        return $errors;
142
    }
143
}
144