Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
42.86% |
3 / 7 |
CRAP | |
85.37% |
70 / 82 |
LoggerPatternParser | |
0.00% |
0 / 1 |
|
42.86% |
3 / 7 |
25.81 | |
85.37% |
70 / 82 |
__construct($pattern, $converterMap) | |
100.00% |
1 / 1 |
1 | |
100.00% |
9 / 9 |
|||
parse() | |
0.00% |
0 / 1 |
9.03 | |
92.59% |
25 / 27 |
|||
addLiteral($string) | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
addConverter($word, $modifiers, $option) | |
0.00% |
0 / 1 |
2.01 | |
87.50% |
7 / 8 |
|||
getConverter($word, $info, $option) | |
0.00% |
0 / 1 |
4.59 | |
66.67% |
6 / 9 |
|||
addToChain(LoggerPatternConverter $converter) | |
100.00% |
1 / 1 |
2 | |
100.00% |
7 / 7 |
|||
parseModifiers($modifiers) | |
0.00% |
0 / 1 |
5.79 | |
68.42% |
13 / 19 |
<?php | |
/** | |
* Licensed to the Apache Software Foundation (ASF) under one or more | |
* contributor license agreements. See the NOTICE file distributed with | |
* this work for additional information regarding copyright ownership. | |
* The ASF licenses this file to You under the Apache License, Version 2.0 | |
* (the "License"); you may not use this file except in compliance with | |
* the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
* | |
* @package log4php | |
*/ | |
/** | |
* Most of the work of the {@link LoggerPatternLayout} class | |
* is delegated to the {@link LoggerPatternParser} class. | |
* | |
* <p>It is this class that parses conversion patterns and creates | |
* a chained list of {@link LoggerPatternConverter} converters.</p> | |
* | |
* @version $Revision: 1395467 $ | |
* @package log4php | |
* @subpackage helpers | |
* | |
* @since 0.3 | |
*/ | |
class LoggerPatternParser { | |
/** Escape character for conversion words in the conversion pattern. */ | |
const ESCAPE_CHAR = '%'; | |
/** Maps conversion words to relevant converters. */ | |
private $converterMap; | |
/** Conversion pattern used in layout. */ | |
private $pattern; | |
/** Regex pattern used for parsing the conversion pattern. */ | |
private $regex; | |
/** | |
* First converter in the chain. | |
* @var LoggerPatternConverter | |
*/ | |
private $head; | |
/** Last converter in the chain. */ | |
private $tail; | |
public function __construct($pattern, $converterMap) { | |
$this->pattern = $pattern; | |
$this->converterMap = $converterMap; | |
// Construct the regex pattern | |
$this->regex = | |
'/' . // Starting regex pattern delimiter | |
self::ESCAPE_CHAR . // Character which marks the start of the conversion pattern | |
'(?P<modifiers>[0-9.-]*)' . // Format modifiers (optional) | |
'(?P<word>[a-zA-Z]+)' . // The conversion word | |
'(?P<option>{[^}]*})?' . // Conversion option in braces (optional) | |
'/'; // Ending regex pattern delimiter | |
} | |
/** | |
* Parses the conversion pattern string, converts it to a chain of pattern | |
* converters and returns the first converter in the chain. | |
* | |
* @return LoggerPatternConverter | |
*/ | |
public function parse() { | |
// Skip parsing if the pattern is empty | |
if (empty($this->pattern)) { | |
$this->addLiteral(''); | |
return $this->head; | |
} | |
// Find all conversion words in the conversion pattern | |
$count = preg_match_all($this->regex, $this->pattern, $matches, PREG_OFFSET_CAPTURE); | |
if ($count === false) { | |
$error = error_get_last(); | |
throw new LoggerException("Failed parsing layotut pattern: {$error['message']}"); | |
} | |
$prevEnd = 0; | |
foreach($matches[0] as $key => $item) { | |
// Locate where the conversion command starts and ends | |
$length = strlen($item[0]); | |
$start = $item[1]; | |
$end = $item[1] + $length; | |
// Find any literal expressions between matched commands | |
if ($start > $prevEnd) { | |
$literal = substr($this->pattern, $prevEnd, $start - $prevEnd); | |
$this->addLiteral($literal); | |
} | |
// Extract the data from the matched command | |
$word = !empty($matches['word'][$key]) ? $matches['word'][$key][0] : null; | |
$modifiers = !empty($matches['modifiers'][$key]) ? $matches['modifiers'][$key][0] : null; | |
$option = !empty($matches['option'][$key]) ? $matches['option'][$key][0] : null; | |
// Create a converter and add it to the chain | |
$this->addConverter($word, $modifiers, $option); | |
$prevEnd = $end; | |
} | |
// Add any trailing literals | |
if ($end < strlen($this->pattern)) { | |
$literal = substr($this->pattern, $end); | |
$this->addLiteral($literal); | |
} | |
return $this->head; | |
} | |
/** | |
* Adds a literal converter to the converter chain. | |
* @param string $string The string for the literal converter. | |
*/ | |
private function addLiteral($string) { | |
$converter = new LoggerPatternConverterLiteral($string); | |
$this->addToChain($converter); | |
} | |
/** | |
* Adds a non-literal converter to the converter chain. | |
* | |
* @param string $word The conversion word, used to determine which | |
* converter will be used. | |
* @param string $modifiers Formatting modifiers. | |
* @param string $option Option to pass to the converter. | |
*/ | |
private function addConverter($word, $modifiers, $option) { | |
$formattingInfo = $this->parseModifiers($modifiers); | |
$option = trim($option, "{} "); | |
if (isset($this->converterMap[$word])) { | |
$converter = $this->getConverter($word, $formattingInfo, $option); | |
$this->addToChain($converter); | |
} else { | |
trigger_error("log4php: Invalid keyword '%$word' in converison pattern. Ignoring keyword.", E_USER_WARNING); | |
} | |
} | |
/** | |
* Determines which converter to use based on the conversion word. Creates | |
* an instance of the converter using the provided formatting info and | |
* option and returns it. | |
* | |
* @param string $word The conversion word. | |
* @param LoggerFormattingInfo $info Formatting info. | |
* @param string $option Converter option. | |
* | |
* @throws LoggerException | |
* | |
* @return LoggerPatternConverter | |
*/ | |
private function getConverter($word, $info, $option) { | |
if (!isset($this->converterMap[$word])) { | |
throw new LoggerException("Invalid keyword '%$word' in converison pattern. Ignoring keyword."); | |
} | |
$converterClass = $this->converterMap[$word]; | |
if(!class_exists($converterClass)) { | |
throw new LoggerException("Class '$converterClass' does not exist."); | |
} | |
$converter = new $converterClass($info, $option); | |
if(!($converter instanceof LoggerPatternConverter)) { | |
throw new LoggerException("Class '$converterClass' is not an instance of LoggerPatternConverter."); | |
} | |
return $converter; | |
} | |
/** Adds a converter to the chain and updates $head and $tail pointers. */ | |
private function addToChain(LoggerPatternConverter $converter) { | |
if (!isset($this->head)) { | |
$this->head = $converter; | |
$this->tail = $this->head; | |
} else { | |
$this->tail->next = $converter; | |
$this->tail = $this->tail->next; | |
} | |
} | |
/** | |
* Parses the formatting modifiers and produces the corresponding | |
* LoggerFormattingInfo object. | |
* | |
* @param string $modifier | |
* @return LoggerFormattingInfo | |
* @throws LoggerException | |
*/ | |
private function parseModifiers($modifiers) { | |
$info = new LoggerFormattingInfo(); | |
// If no modifiers are given, return default values | |
if (empty($modifiers)) { | |
return $info; | |
} | |
// Validate | |
$pattern = '/^(-?[0-9]+)?\.?-?[0-9]+$/'; | |
if (!preg_match($pattern, $modifiers)) { | |
trigger_error("log4php: Invalid modifier in conversion pattern: [$modifiers]. Ignoring modifier.", E_USER_WARNING); | |
return $info; | |
} | |
$parts = explode('.', $modifiers); | |
if (!empty($parts[0])) { | |
$minPart = (integer) $parts[0]; | |
$info->min = abs($minPart); | |
$info->padLeft = ($minPart > 0); | |
} | |
if (!empty($parts[1])) { | |
$maxPart = (integer) $parts[1]; | |
$info->max = abs($maxPart); | |
$info->trimLeft = ($maxPart < 0); | |
} | |
return $info; | |
} | |
} |