[CLEANUP] Correct case for SimpleXMLElement
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Localization / Parser / LocallangXmlParser.php
1 <?php
2 namespace TYPO3\CMS\Core\Localization\Parser;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\ArrayUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException;
20
21 /**
22 * Parser for XML locallang file.
23 *
24 * @author Dominique Feyer <dfeyer@reelpeek.net>
25 */
26 class LocallangXmlParser extends AbstractXmlParser {
27
28 /**
29 * Associative array of "filename => parsed data" pairs.
30 *
31 * @var array
32 */
33 protected $parsedTargetFiles;
34
35 /**
36 * Returns parsed representation of XML file.
37 *
38 * @param string $sourcePath Source file path
39 * @param string $languageKey Language key
40 * @param string $charset Charset
41 * @return array
42 */
43 public function getParsedData($sourcePath, $languageKey, $charset = '') {
44 $this->sourcePath = $sourcePath;
45 $this->languageKey = $languageKey;
46 $this->charset = $this->getCharset($languageKey, $charset);
47 // Parse source
48 $parsedSource = $this->parseXmlFile();
49 // Parse target
50 $localizedTargetPath = GeneralUtility::getFileAbsFileName(GeneralUtility::llXmlAutoFileName($this->sourcePath, $this->languageKey));
51 $targetPath = $this->languageKey !== 'default' && @is_file($localizedTargetPath) ? $localizedTargetPath : $this->sourcePath;
52 try {
53 $parsedTarget = $this->getParsedTargetData($targetPath);
54 } catch (InvalidXmlFileException $e) {
55 $parsedTarget = $this->getParsedTargetData($this->sourcePath);
56 }
57 $LOCAL_LANG = array();
58 $LOCAL_LANG[$languageKey] = $parsedSource;
59 ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG[$languageKey], $parsedTarget);
60 return $LOCAL_LANG;
61 }
62
63 /**
64 * Returns array representation of XLIFF data, starting from a root node.
65 *
66 * @param \SimpleXMLElement $root XML root element
67 * @param string $element Target or Source
68 * @return array
69 */
70 protected function doParsingFromRootForElement(\SimpleXMLElement $root, $element) {
71 $bodyOfFileTag = $root->data->languageKey;
72 // Check if the source llxml file contains localized records
73 $localizedBodyOfFileTag = $root->data->xpath('languageKey[@index=\'' . $this->languageKey . '\']');
74 if ($element === 'source' || $this->languageKey === 'default') {
75 $parsedData = $this->getParsedDataForElement($bodyOfFileTag, $element);
76 } else {
77 $parsedData = array();
78 }
79 if ($element === 'target' && isset($localizedBodyOfFileTag[0]) && $localizedBodyOfFileTag[0] instanceof \SimpleXMLElement) {
80 $parsedDataTarget = $this->getParsedDataForElement($localizedBodyOfFileTag[0], $element);
81 $mergedData = $parsedDataTarget + $parsedData;
82 if ($this->languageKey === 'default') {
83 $parsedData = array_intersect_key($mergedData, $parsedData, $parsedDataTarget);
84 } else {
85 $parsedData = array_intersect_key($mergedData, $parsedDataTarget);
86 }
87 }
88 return $parsedData;
89 }
90
91 /**
92 * Parse the given language key tag
93 *
94 * @param \SimpleXMLElement $bodyOfFileTag
95 * @param string $element
96 * @return array
97 */
98 protected function getParsedDataForElement(\SimpleXMLElement $bodyOfFileTag, $element) {
99 $parsedData = array();
100 $children = $bodyOfFileTag->children();
101 if ($children->count() === 0) {
102 // Check for externally-referenced resource:
103 // <languageKey index="fr">EXT:yourext/path/to/localized/locallang.xml</languageKey>
104 $reference = sprintf('%s', $bodyOfFileTag);
105 if (substr($reference, -4) === '.xml') {
106 return $this->getParsedTargetData(GeneralUtility::getFileAbsFileName($reference));
107 }
108 }
109 /** @var \SimpleXMLElement $translationElement */
110 foreach ($children as $translationElement) {
111 if ($translationElement->getName() === 'label') {
112 $parsedData[(string)$translationElement['index']][0] = array(
113 $element => (string)$translationElement
114 );
115 }
116 }
117 return $parsedData;
118 }
119
120 /**
121 * Returns array representation of XLIFF data, starting from a root node.
122 *
123 * @param \SimpleXMLElement $root A root node
124 * @return array An array representing parsed XLIFF
125 */
126 protected function doParsingFromRoot(\SimpleXMLElement $root) {
127 return $this->doParsingFromRootForElement($root, 'source');
128 }
129
130 /**
131 * Returns array representation of XLIFF data, starting from a root node.
132 *
133 * @param \SimpleXMLElement $root A root node
134 * @return array An array representing parsed XLIFF
135 */
136 protected function doParsingTargetFromRoot(\SimpleXMLElement $root) {
137 return $this->doParsingFromRootForElement($root, 'target');
138 }
139
140 /**
141 * Returns parsed representation of XML file.
142 *
143 * Parses XML if it wasn't done before. Caches parsed data.
144 *
145 * @param string $path An absolute path to XML file
146 * @return array Parsed XML file
147 */
148 public function getParsedTargetData($path) {
149 if (!isset($this->parsedTargetFiles[$path])) {
150 $this->parsedTargetFiles[$path] = $this->parseXmlTargetFile($path);
151 }
152 return $this->parsedTargetFiles[$path];
153 }
154
155 /**
156 * Reads and parses XML file and returns internal representation of data.
157 *
158 * @param string $targetPath Path of the target file
159 * @return array
160 * @throws \TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException
161 */
162 protected function parseXmlTargetFile($targetPath) {
163 $rootXmlNode = FALSE;
164 if (file_exists($targetPath)) {
165 $rootXmlNode = simplexml_load_file($targetPath, 'SimpleXMLElement', LIBXML_NOWARNING);
166 }
167 if (!isset($rootXmlNode) || $rootXmlNode === FALSE) {
168 throw new InvalidXmlFileException('The path provided does not point to existing and accessible well-formed XML file (' . $targetPath . ').', 1278155987);
169 }
170 return $this->doParsingTargetFromRoot($rootXmlNode);
171 }
172
173 }