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 : * Most of the work of the {@link LoggerPatternLayout} class
23 : * is delegated to the {@link LoggerPatternParser} class.
24 : *
25 : * <p>It is this class that parses conversion patterns and creates
26 : * a chained list of {@link LoggerPatternConverter} converters.</p>
27 : *
28 : * @version $Revision: 1163520 $
29 : * @package log4php
30 : * @subpackage helpers
31 : *
32 : * @since 0.3
33 : */
34 : class LoggerPatternParser {
35 :
36 : const ESCAPE_CHAR = '%';
37 :
38 : const LITERAL_STATE = 0;
39 : const CONVERTER_STATE = 1;
40 : const MINUS_STATE = 2;
41 : const DOT_STATE = 3;
42 : const MIN_STATE = 4;
43 : const MAX_STATE = 5;
44 :
45 : const FULL_LOCATION_CONVERTER = 1000;
46 : const METHOD_LOCATION_CONVERTER = 1001;
47 : const CLASS_LOCATION_CONVERTER = 1002;
48 : const FILE_LOCATION_CONVERTER = 1003;
49 : const LINE_LOCATION_CONVERTER = 1004;
50 :
51 : const RELATIVE_TIME_CONVERTER = 2000;
52 : const THREAD_CONVERTER = 2001;
53 : const LEVEL_CONVERTER = 2002;
54 : const NDC_CONVERTER = 2003;
55 : const MESSAGE_CONVERTER = 2004;
56 :
57 : const DATE_FORMAT_ISO8601 = 'Y-m-d H:i:s,u';
58 : const DATE_FORMAT_ABSOLUTE = 'H:i:s';
59 : const DATE_FORMAT_DATE = 'd M Y H:i:s,u';
60 :
61 : private $state;
62 : private $currentLiteral;
63 : private $patternLength;
64 : private $i;
65 :
66 : /**
67 : * @var LoggerPatternConverter
68 : */
69 : private $head = null;
70 :
71 : /**
72 : * @var LoggerPatternConverter
73 : */
74 : private $tail = null;
75 :
76 : /**
77 : * @var LoggerFormattingInfo
78 : */
79 : private $formattingInfo;
80 :
81 : /**
82 : * @var string pattern to parse
83 : */
84 : private $pattern;
85 :
86 : /**
87 : * Constructor
88 : *
89 : * @param string $pattern
90 : */
91 : public function __construct($pattern) {
92 19 : $this->pattern = $pattern;
93 19 : $this->patternLength = strlen($pattern);
94 19 : $this->formattingInfo = new LoggerFormattingInfo();
95 19 : $this->state = self::LITERAL_STATE;
96 19 : }
97 :
98 : /**
99 : * @param LoggerPatternConverter $pc
100 : */
101 : public function addToList($pc) {
102 16 : if($this->head == null) {
103 16 : $this->head = $pc;
104 16 : $this->tail = $this->head;
105 16 : } else {
106 14 : $this->tail->next = $pc;
107 14 : $this->tail = $this->tail->next;
108 : }
109 16 : }
110 :
111 : /**
112 : * @return string
113 : */
114 : public function extractOption() {
115 13 : if(($this->i < $this->patternLength) and ($this->pattern{$this->i} == '{')) {
116 9 : $end = strpos($this->pattern, '}' , $this->i);
117 9 : if($end !== false) {
118 9 : $r = substr($this->pattern, ($this->i + 1), ($end - $this->i - 1));
119 9 : $this->i= $end + 1;
120 9 : return $r;
121 : }
122 0 : }
123 13 : return null;
124 : }
125 :
126 : /**
127 : * The option is expected to be in decimal and positive. In case of
128 : * error, zero is returned.
129 : */
130 : public function extractPrecisionOption() {
131 12 : $opt = $this->extractOption();
132 12 : $r = 0;
133 12 : if($opt !== null) {
134 0 : if(is_numeric($opt)) {
135 0 : $r = (int)$opt;
136 0 : if($r <= 0) {
137 0 : $r = 0;
138 0 : }
139 0 : }
140 0 : }
141 12 : return $r;
142 : }
143 :
144 :
145 : /** Parser.
146 : *
147 : * @return LoggerPatternConverter Returns $this->head.
148 : */
149 : public function parse() {
150 19 : $c = '';
151 19 : $this->i = 0;
152 19 : $this->currentLiteral = '';
153 19 : while($this->i < $this->patternLength) {
154 16 : $c = $this->pattern{$this->i++};
155 :
156 16 : switch($this->state) {
157 16 : case self::LITERAL_STATE:
158 : // In literal state, the last char is always a literal.
159 16 : if($this->i == $this->patternLength) {
160 1 : $this->currentLiteral .= $c;
161 1 : continue;
162 : }
163 16 : if($c == self::ESCAPE_CHAR) {
164 : // peek at the next char.
165 16 : switch($this->pattern{$this->i}) {
166 16 : case self::ESCAPE_CHAR:
167 0 : $this->currentLiteral .= $c;
168 0 : $this->i++; // move pointer
169 0 : break;
170 16 : case 'n':
171 7 : $this->currentLiteral .= PHP_EOL;
172 7 : $this->i++; // move pointer
173 7 : break;
174 16 : default:
175 16 : if(strlen($this->currentLiteral) != 0) {
176 14 : $this->addToList(new LoggerLiteralPatternConverter($this->currentLiteral));
177 14 : }
178 16 : $this->currentLiteral = $c;
179 16 : $this->state = self::CONVERTER_STATE;
180 16 : $this->formattingInfo->reset();
181 16 : }
182 16 : } else {
183 14 : $this->currentLiteral .= $c;
184 : }
185 16 : break;
186 16 : case self::CONVERTER_STATE:
187 16 : $this->currentLiteral .= $c;
188 : switch($c) {
189 16 : case '-':
190 9 : $this->formattingInfo->leftAlign = true;
191 9 : break;
192 16 : case '.':
193 0 : $this->state = self::DOT_STATE;
194 0 : break;
195 16 : default:
196 16 : if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
197 9 : $this->formattingInfo->min = ord($c) - ord('0');
198 9 : $this->state = self::MIN_STATE;
199 9 : } else {
200 16 : $this->finalizeConverter($c);
201 : }
202 16 : } // switch
203 16 : break;
204 9 : case self::MIN_STATE:
205 9 : $this->currentLiteral .= $c;
206 9 : if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
207 0 : $this->formattingInfo->min = ($this->formattingInfo->min * 10) + (ord($c) - ord('0'));
208 9 : } else if ($c == '.') {
209 0 : $this->state = self::DOT_STATE;
210 0 : } else {
211 9 : $this->finalizeConverter($c);
212 : }
213 9 : break;
214 0 : case self::DOT_STATE:
215 0 : $this->currentLiteral .= $c;
216 0 : if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
217 0 : $this->formattingInfo->max = ord($c) - ord('0');
218 0 : $this->state = self::MAX_STATE;
219 0 : } else {
220 0 : $this->state = self::LITERAL_STATE;
221 : }
222 0 : break;
223 0 : case self::MAX_STATE:
224 0 : $this->currentLiteral .= $c;
225 0 : if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
226 0 : $this->formattingInfo->max = ($this->formattingInfo->max * 10) + (ord($c) - ord('0'));
227 0 : } else {
228 0 : $this->finalizeConverter($c);
229 0 : $this->state = self::LITERAL_STATE;
230 : }
231 0 : break;
232 16 : } // switch
233 16 : } // while
234 19 : if(strlen($this->currentLiteral) != 0) {
235 8 : $this->addToList(new LoggerLiteralPatternConverter($this->currentLiteral));
236 8 : }
237 19 : return $this->head;
238 : }
239 :
240 : public function finalizeConverter($c) {
241 16 : $pc = null;
242 : switch($c) {
243 16 : case 'c':
244 12 : $pc = new LoggerCategoryPatternConverter($this->formattingInfo, $this->extractPrecisionOption());
245 12 : $this->currentLiteral = '';
246 12 : break;
247 16 : case 'C':
248 7 : $pc = new LoggerClassNamePatternConverter($this->formattingInfo, self::CLASS_LOCATION_CONVERTER);
249 7 : $this->currentLiteral = '';
250 7 : break;
251 14 : case 'd':
252 9 : $dateFormatStr = self::DATE_FORMAT_ISO8601; // ISO8601_DATE_FORMAT;
253 9 : $dOpt = $this->extractOption();
254 :
255 9 : if($dOpt !== null)
256 9 : $dateFormatStr = $dOpt;
257 :
258 9 : if($dateFormatStr == 'ISO8601') {
259 0 : $df = self::DATE_FORMAT_ISO8601;
260 9 : } else if($dateFormatStr == 'ABSOLUTE') {
261 0 : $df = self::DATE_FORMAT_ABSOLUTE;
262 9 : } else if($dateFormatStr == 'DATE') {
263 0 : $df = self::DATE_FORMAT_DATE;
264 0 : } else {
265 9 : $df = $dateFormatStr;
266 9 : if($df == null) {
267 0 : $df = self::DATE_FORMAT_ISO8601;
268 0 : }
269 : }
270 9 : $pc = new LoggerDatePatternConverter($this->formattingInfo, $df);
271 9 : $this->currentLiteral = '';
272 9 : break;
273 14 : case 'F':
274 9 : $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::FILE_LOCATION_CONVERTER);
275 9 : $this->currentLiteral = '';
276 9 : break;
277 14 : case 'l':
278 0 : $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::FULL_LOCATION_CONVERTER);
279 0 : $this->currentLiteral = '';
280 0 : break;
281 14 : case 'L':
282 9 : $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::LINE_LOCATION_CONVERTER);
283 9 : $this->currentLiteral = '';
284 9 : break;
285 14 : case 'm':
286 14 : $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::MESSAGE_CONVERTER);
287 14 : $this->currentLiteral = '';
288 14 : break;
289 13 : case 'M':
290 0 : $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::METHOD_LOCATION_CONVERTER);
291 0 : $this->currentLiteral = '';
292 0 : break;
293 13 : case 'p':
294 13 : $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::LEVEL_CONVERTER);
295 13 : $this->currentLiteral = '';
296 13 : break;
297 7 : case 'r':
298 0 : $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::RELATIVE_TIME_CONVERTER);
299 0 : $this->currentLiteral = '';
300 0 : break;
301 7 : case 't':
302 3 : $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::THREAD_CONVERTER);
303 3 : $this->currentLiteral = '';
304 3 : break;
305 4 : case 'x':
306 0 : $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::NDC_CONVERTER);
307 0 : $this->currentLiteral = '';
308 0 : break;
309 4 : case 'X':
310 4 : $xOpt = $this->extractOption();
311 4 : $pc = new LoggerMDCPatternConverter($this->formattingInfo, $xOpt);
312 4 : $this->currentLiteral = '';
313 4 : break;
314 0 : default:
315 0 : $pc = new LoggerLiteralPatternConverter($this->currentLiteral);
316 0 : $this->currentLiteral = '';
317 0 : }
318 16 : $this->addConverter($pc);
319 16 : }
320 :
321 : public function addConverter($pc) {
322 16 : $this->currentLiteral = '';
323 : // Add the pattern converter to the list.
324 16 : $this->addToList($pc);
325 : // Next pattern is assumed to be a literal.
326 16 : $this->state = self::LITERAL_STATE;
327 : // Reset formatting info
328 16 : $this->formattingInfo->reset();
329 16 : }
330 : }
331 :
|