Overview

Packages

  • log4php
    • appenders
    • configurators
    • filters
    • helpers
    • layouts
    • pattern
    • renderers

Classes

  • LoggerFormattingInfo
  • LoggerOptionConverter
  • LoggerPatternConverter
  • LoggerPatternParser
  • LoggerUtils
  • Overview
  • Package
  • Class
  • Tree
  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: 1395467 $ 
 29:  * @package log4php
 30:  * @subpackage helpers
 31:  *
 32:  * @since 0.3
 33:  */
 34: class LoggerPatternParser {
 35: 
 36:     /** Escape character for conversion words in the conversion pattern. */
 37:     const ESCAPE_CHAR = '%';
 38:     
 39:     /** Maps conversion words to relevant converters. */
 40:     private $converterMap;
 41:     
 42:     /** Conversion pattern used in layout. */
 43:     private $pattern;
 44:     
 45:     /** Regex pattern used for parsing the conversion pattern. */
 46:     private $regex;
 47:     
 48:     /** 
 49:      * First converter in the chain. 
 50:      * @var LoggerPatternConverter
 51:      */
 52:     private $head;
 53:     
 54:     /** Last converter in the chain. */
 55:     private $tail;
 56:     
 57:     public function __construct($pattern, $converterMap) {
 58:         $this->pattern = $pattern;
 59:         $this->converterMap = $converterMap;
 60:         
 61:         // Construct the regex pattern
 62:         $this->regex = 
 63:             '/' .                       // Starting regex pattern delimiter
 64:             self::ESCAPE_CHAR .         // Character which marks the start of the conversion pattern
 65:             '(?P<modifiers>[0-9.-]*)' . // Format modifiers (optional)
 66:             '(?P<word>[a-zA-Z]+)' .     // The conversion word
 67:             '(?P<option>{[^}]*})?' .    // Conversion option in braces (optional)
 68:             '/';                        // Ending regex pattern delimiter
 69:     }
 70:     
 71:     /** 
 72:      * Parses the conversion pattern string, converts it to a chain of pattern
 73:      * converters and returns the first converter in the chain.
 74:      * 
 75:      * @return LoggerPatternConverter
 76:      */
 77:     public function parse() {
 78:         
 79:         // Skip parsing if the pattern is empty
 80:         if (empty($this->pattern)) {
 81:             $this->addLiteral('');
 82:             return $this->head;
 83:         }
 84:         
 85:         // Find all conversion words in the conversion pattern
 86:         $count = preg_match_all($this->regex, $this->pattern, $matches, PREG_OFFSET_CAPTURE);
 87:         if ($count === false) {
 88:             $error = error_get_last();
 89:             throw new LoggerException("Failed parsing layotut pattern: {$error['message']}");
 90:         }
 91:         
 92:         $prevEnd = 0;
 93:         
 94:         foreach($matches[0] as $key => $item) {
 95:             
 96:             // Locate where the conversion command starts and ends
 97:             $length = strlen($item[0]);
 98:             $start = $item[1];
 99:             $end = $item[1] + $length;
100:         
101:             // Find any literal expressions between matched commands
102:             if ($start > $prevEnd) {
103:                 $literal = substr($this->pattern, $prevEnd, $start - $prevEnd);
104:                 $this->addLiteral($literal);
105:             }
106:             
107:             // Extract the data from the matched command
108:             $word = !empty($matches['word'][$key]) ? $matches['word'][$key][0] : null;
109:             $modifiers = !empty($matches['modifiers'][$key]) ? $matches['modifiers'][$key][0] : null;
110:             $option = !empty($matches['option'][$key]) ? $matches['option'][$key][0] : null;
111:             
112:             // Create a converter and add it to the chain
113:             $this->addConverter($word, $modifiers, $option);
114:             
115:             $prevEnd = $end;
116:         }
117: 
118:         // Add any trailing literals
119:         if ($end < strlen($this->pattern)) {
120:             $literal = substr($this->pattern, $end);
121:             $this->addLiteral($literal);
122:         }
123:         
124:         return $this->head;
125:     }
126:     
127:     /** 
128:      * Adds a literal converter to the converter chain. 
129:      * @param string $string The string for the literal converter.
130:      */
131:     private function addLiteral($string) {
132:         $converter = new LoggerPatternConverterLiteral($string);
133:         $this->addToChain($converter);
134:     }
135:     
136:     /**
137:      * Adds a non-literal converter to the converter chain.
138:      * 
139:      * @param string $word The conversion word, used to determine which 
140:      *  converter will be used.
141:      * @param string $modifiers Formatting modifiers.
142:      * @param string $option Option to pass to the converter.
143:      */
144:     private function addConverter($word, $modifiers, $option) {
145:         $formattingInfo = $this->parseModifiers($modifiers);
146:         $option = trim($option, "{} ");
147:         
148:         if (isset($this->converterMap[$word])) {
149:             $converter = $this->getConverter($word, $formattingInfo, $option);
150:             $this->addToChain($converter);  
151:         } else {
152:             trigger_error("log4php: Invalid keyword '%$word' in converison pattern. Ignoring keyword.", E_USER_WARNING);
153:         }
154:     }
155:     
156:     /**
157:      * Determines which converter to use based on the conversion word. Creates 
158:      * an instance of the converter using the provided formatting info and 
159:      * option and returns it.
160:      * 
161:      * @param string $word The conversion word.
162:      * @param LoggerFormattingInfo $info Formatting info.
163:      * @param string $option Converter option.
164:      * 
165:      * @throws LoggerException 
166:      * 
167:      * @return LoggerPatternConverter
168:      */
169:     private function getConverter($word, $info, $option) {
170:         if (!isset($this->converterMap[$word])) {
171:             throw new LoggerException("Invalid keyword '%$word' in converison pattern. Ignoring keyword.");
172:         }
173:         
174:         $converterClass = $this->converterMap[$word];
175:         if(!class_exists($converterClass)) {
176:             throw new LoggerException("Class '$converterClass' does not exist.");
177:         }
178:         
179:         $converter = new $converterClass($info, $option);
180:         if(!($converter instanceof LoggerPatternConverter)) {
181:             throw new LoggerException("Class '$converterClass' is not an instance of LoggerPatternConverter.");
182:         }
183:         
184:         return $converter;
185:     }
186:     
187:     /** Adds a converter to the chain and updates $head and $tail pointers. */
188:     private function addToChain(LoggerPatternConverter $converter) {
189:         if (!isset($this->head)) {
190:             $this->head = $converter;
191:             $this->tail = $this->head;
192:         } else {
193:             $this->tail->next = $converter;
194:             $this->tail = $this->tail->next;
195:         }
196:     }
197:     
198:     /**
199:      * Parses the formatting modifiers and produces the corresponding 
200:      * LoggerFormattingInfo object.
201:      * 
202:      * @param string $modifier
203:      * @return LoggerFormattingInfo
204:      * @throws LoggerException
205:      */
206:     private function parseModifiers($modifiers) {
207:         $info = new LoggerFormattingInfo();
208:     
209:         // If no modifiers are given, return default values
210:         if (empty($modifiers)) {
211:             return $info;
212:         }
213:     
214:         // Validate
215:         $pattern = '/^(-?[0-9]+)?\.?-?[0-9]+$/';
216:         if (!preg_match($pattern, $modifiers)) {
217:             trigger_error("log4php: Invalid modifier in conversion pattern: [$modifiers]. Ignoring modifier.", E_USER_WARNING);
218:             return $info;
219:         }
220:     
221:         $parts = explode('.', $modifiers);
222:     
223:         if (!empty($parts[0])) {
224:             $minPart = (integer) $parts[0];
225:             $info->min = abs($minPart);
226:             $info->padLeft = ($minPart > 0);
227:         }
228:     
229:         if (!empty($parts[1])) {
230:             $maxPart = (integer) $parts[1];
231:             $info->max = abs($maxPart);
232:             $info->trimLeft = ($maxPart < 0);
233:         }
234:     
235:         return $info;
236:     }
237: }
238: 
Apache log4php API documentation generated by ApiGen 2.8.0