1 <?php |
|
2 /** |
|
3 * Licensed to the Apache Software Foundation (ASF) under one or more |
|
4 * contributor license agreements. See the NOTICE file distributed with |
|
5 * this work for additional information regarding copyright ownership. |
|
6 * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
7 * (the "License"); you may not use this file except in compliance with |
|
8 * the License. You may obtain a copy of the License at |
|
9 * |
|
10 * http://www.apache.org/licenses/LICENSE-2.0 |
|
11 * |
|
12 * Unless required by applicable law or agreed to in writing, software |
|
13 * distributed under the License is distributed on an "AS IS" BASIS, |
|
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15 * See the License for the specific language governing permissions and |
|
16 * limitations under the License. |
|
17 * |
|
18 * @package log4php |
|
19 */ |
|
20 |
|
21 /** |
|
22 * Converts XML configuration files to a PHP array. |
|
23 * |
|
24 * @package log4php |
|
25 * @subpackage configurators |
|
26 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
|
27 * @version $Revision: 1394956 $ |
|
28 * @since 2.2 |
|
29 */ |
|
30 class LoggerConfigurationAdapterXML implements LoggerConfigurationAdapter { |
|
31 /** Path to the XML schema used for validation. */ |
|
32 const SCHEMA_PATH = '/../xml/log4php.xsd'; |
|
33 |
|
34 private $config = array( |
|
35 'appenders' => array(), |
|
36 'loggers' => array(), |
|
37 'renderers' => array(), |
|
38 ); |
|
39 |
|
40 public function convert($url) { |
|
41 $xml = $this->loadXML($url); |
|
42 |
|
43 $this->parseConfiguration($xml); |
|
44 |
|
45 // Parse the <root> node |
|
46 if (isset($xml->root)) { |
|
47 $this->parseRootLogger($xml->root); |
|
48 } |
|
49 |
|
50 // Process <logger> nodes |
|
51 foreach ($xml->logger as $logger) { |
|
52 $this->parseLogger($logger); |
|
53 } |
|
54 |
|
55 // Process <appender> nodes |
|
56 foreach ($xml->appender as $appender) { |
|
57 $this->parseAppender($appender); |
|
58 } |
|
59 |
|
60 // Process <renderer> nodes |
|
61 foreach ($xml->renderer as $rendererNode) { |
|
62 $this->parseRenderer($rendererNode); |
|
63 } |
|
64 |
|
65 // Process <defaultRenderer> node |
|
66 foreach ($xml->defaultRenderer as $rendererNode) { |
|
67 $this->parseDefaultRenderer($rendererNode); |
|
68 } |
|
69 |
|
70 return $this->config; |
|
71 } |
|
72 |
|
73 /** |
|
74 * Loads and validates the XML. |
|
75 * @param string $url Input XML. |
|
76 */ |
|
77 private function loadXML($url) { |
|
78 if (!file_exists($url)) { |
|
79 throw new LoggerException("File [$url] does not exist."); |
|
80 } |
|
81 |
|
82 libxml_clear_errors(); |
|
83 $oldValue = libxml_use_internal_errors(true); |
|
84 |
|
85 // Load XML |
|
86 $xml = @simplexml_load_file($url); |
|
87 if ($xml === false) { |
|
88 |
|
89 $errorStr = ""; |
|
90 foreach (libxml_get_errors() as $error) { |
|
91 $errorStr .= $error->message; |
|
92 } |
|
93 |
|
94 throw new LoggerException("Error loading configuration file: " . trim($errorStr)); |
|
95 } |
|
96 |
|
97 libxml_clear_errors(); |
|
98 libxml_use_internal_errors($oldValue); |
|
99 |
|
100 return $xml; |
|
101 } |
|
102 |
|
103 /** |
|
104 * Parses the <configuration> node. |
|
105 */ |
|
106 private function parseConfiguration(SimpleXMLElement $xml) { |
|
107 $attributes = $xml->attributes(); |
|
108 if (isset($attributes['threshold'])) { |
|
109 $this->config['threshold'] = (string)$attributes['threshold']; |
|
110 } |
|
111 } |
|
112 |
|
113 /** Parses an <appender> node. */ |
|
114 private function parseAppender(SimpleXMLElement $node) { |
|
115 $name = $this->getAttributeValue($node, 'name'); |
|
116 if (empty($name)) { |
|
117 $this->warn("An <appender> node is missing the required 'name' attribute. Skipping appender definition."); |
|
118 return; |
|
119 } |
|
120 |
|
121 $appender = array(); |
|
122 $appender['class'] = $this->getAttributeValue($node, 'class'); |
|
123 |
|
124 if (isset($node['threshold'])) { |
|
125 $appender['threshold'] = $this->getAttributeValue($node, 'threshold'); |
|
126 } |
|
127 |
|
128 if (isset($node->layout)) { |
|
129 $appender['layout'] = $this->parseLayout($node->layout, $name); |
|
130 } |
|
131 |
|
132 if (count($node->param) > 0) { |
|
133 $appender['params'] = $this->parseParameters($node); |
|
134 } |
|
135 |
|
136 foreach ($node->filter as $filterNode) { |
|
137 $appender['filters'][] = $this->parseFilter($filterNode); |
|
138 } |
|
139 |
|
140 $this->config['appenders'][$name] = $appender; |
|
141 } |
|
142 |
|
143 /** Parses a <layout> node. */ |
|
144 private function parseLayout(SimpleXMLElement $node, $appenderName) { |
|
145 $layout = array(); |
|
146 $layout['class'] = $this->getAttributeValue($node, 'class'); |
|
147 |
|
148 if (count($node->param) > 0) { |
|
149 $layout['params'] = $this->parseParameters($node); |
|
150 } |
|
151 |
|
152 return $layout; |
|
153 } |
|
154 |
|
155 /** Parses any <param> child nodes returning them in an array. */ |
|
156 private function parseParameters($paramsNode) { |
|
157 $params = array(); |
|
158 |
|
159 foreach ($paramsNode->param as $paramNode) { |
|
160 if (empty($paramNode['name'])) { |
|
161 $this->warn("A <param> node is missing the required 'name' attribute. Skipping parameter."); |
|
162 continue; |
|
163 } |
|
164 |
|
165 $name = $this->getAttributeValue($paramNode, 'name'); |
|
166 $value = $this->getAttributeValue($paramNode, 'value'); |
|
167 |
|
168 $params[$name] = $value; |
|
169 } |
|
170 |
|
171 return $params; |
|
172 } |
|
173 |
|
174 /** Parses a <root> node. */ |
|
175 private function parseRootLogger(SimpleXMLElement $node) { |
|
176 $logger = array(); |
|
177 |
|
178 if (isset($node->level)) { |
|
179 $logger['level'] = $this->getAttributeValue($node->level, 'value'); |
|
180 } |
|
181 |
|
182 $logger['appenders'] = $this->parseAppenderReferences($node); |
|
183 |
|
184 $this->config['rootLogger'] = $logger; |
|
185 } |
|
186 |
|
187 /** Parses a <logger> node. */ |
|
188 private function parseLogger(SimpleXMLElement $node) { |
|
189 $logger = array(); |
|
190 |
|
191 $name = $this->getAttributeValue($node, 'name'); |
|
192 if (empty($name)) { |
|
193 $this->warn("A <logger> node is missing the required 'name' attribute. Skipping logger definition."); |
|
194 return; |
|
195 } |
|
196 |
|
197 if (isset($node->level)) { |
|
198 $logger['level'] = $this->getAttributeValue($node->level, 'value'); |
|
199 } |
|
200 |
|
201 if (isset($node['additivity'])) { |
|
202 $logger['additivity'] = $this->getAttributeValue($node, 'additivity'); |
|
203 } |
|
204 |
|
205 $logger['appenders'] = $this->parseAppenderReferences($node); |
|
206 |
|
207 // Check for duplicate loggers |
|
208 if (isset($this->config['loggers'][$name])) { |
|
209 $this->warn("Duplicate logger definition [$name]. Overwriting."); |
|
210 } |
|
211 |
|
212 $this->config['loggers'][$name] = $logger; |
|
213 } |
|
214 |
|
215 /** |
|
216 * Parses a <logger> node for appender references and returns them in an array. |
|
217 * |
|
218 * Previous versions supported appender-ref, as well as appender_ref so both |
|
219 * are parsed for backward compatibility. |
|
220 */ |
|
221 private function parseAppenderReferences(SimpleXMLElement $node) { |
|
222 $refs = array(); |
|
223 foreach ($node->appender_ref as $ref) { |
|
224 $refs[] = $this->getAttributeValue($ref, 'ref'); |
|
225 } |
|
226 |
|
227 foreach ($node->{'appender-ref'} as $ref) { |
|
228 $refs[] = $this->getAttributeValue($ref, 'ref'); |
|
229 } |
|
230 |
|
231 return $refs; |
|
232 } |
|
233 |
|
234 /** Parses a <filter> node. */ |
|
235 private function parseFilter($filterNode) { |
|
236 $filter = array(); |
|
237 $filter['class'] = $this->getAttributeValue($filterNode, 'class'); |
|
238 |
|
239 if (count($filterNode->param) > 0) { |
|
240 $filter['params'] = $this->parseParameters($filterNode); |
|
241 } |
|
242 |
|
243 return $filter; |
|
244 } |
|
245 |
|
246 /** Parses a <renderer> node. */ |
|
247 private function parseRenderer(SimpleXMLElement $node) { |
|
248 $renderedClass = $this->getAttributeValue($node, 'renderedClass'); |
|
249 $renderingClass = $this->getAttributeValue($node, 'renderingClass'); |
|
250 |
|
251 $this->config['renderers'][] = compact('renderedClass', 'renderingClass'); |
|
252 } |
|
253 |
|
254 /** Parses a <defaultRenderer> node. */ |
|
255 private function parseDefaultRenderer(SimpleXMLElement $node) { |
|
256 $renderingClass = $this->getAttributeValue($node, 'renderingClass'); |
|
257 |
|
258 // Warn on duplicates |
|
259 if (isset($this->config['defaultRenderer'])) { |
|
260 $this->warn("Duplicate <defaultRenderer> node. Overwriting."); |
|
261 } |
|
262 |
|
263 $this->config['defaultRenderer'] = $renderingClass; |
|
264 } |
|
265 |
|
266 // ****************************************** |
|
267 // ** Helper methods ** |
|
268 // ****************************************** |
|
269 |
|
270 private function getAttributeValue(SimpleXMLElement $node, $name) { |
|
271 return isset($node[$name]) ? (string)$node[$name] : null; |
|
272 } |
|
273 |
|
274 private function warn($message) { |
|
275 trigger_error("log4php: " . $message, E_USER_WARNING); |
|
276 } |
|
277 } |
|
278 |
|