<?php
/**
* Copyright (c) 2009, Laurent Laville <pear@laurent-laville.org>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the authors nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* PHP version 5
*
* @category PEAR
* @package PEAR_TestListener
* @author Laurent Laville <pear@laurent-laville.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version CVS: $Id:$
* @link http://pear.laurent-laville.org/pepr/PEAR_TestListener
* @since File available since Release 0.3.0a1
*/
require_once 'PHPUnit/Util/Configuration.php';
PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
/**
* Wrapper for the PHPUnit XML configuration file.
*
* @category PEAR
* @package PEAR_TestListener
* @author Laurent Laville <pear@laurent-laville.org>
* @license http://www.opensource.org/licenses/bsd-license.php BSD
* @version Release: @package_version@
* @link http://pear.laurent-laville.org/pepr/PEAR_TestListener
* @since Class available since Release 0.3.0a1
*/
class PEAR_TestListener_Configuration extends PHPUnit_Util_Configuration
{
/**
* Instances of PEAR_TestListener_Configuration for each XML configuration file
*
* @var array
*/
private static $_instances = array();
/**
* Loads a PHPUnit configuration file.
*
* @param string $filename XML configuration filename
*/
public function __construct($filename)
{
parent::__construct($filename);
}
/**
* Returns a PHPUnit configuration object.
*
* @param string $filename XML configuration filename
*
* @return PEAR_TestListener_Configuration
*/
public static function getInstance($filename)
{
$realpath = realpath($filename);
if ($realpath === false) {
throw new RuntimeException(
sprintf('Could not read "%s".', $filename)
);
}
if (!isset(self::$_instances[$realpath])) {
self::$_instances[$realpath]
= new PEAR_TestListener_Configuration($realpath);
}
return self::$_instances[$realpath];
}
/**
* Returns the logger configuration.
*
* @return array
*/
public function getLoggerConfiguration()
{
$priorities = 'all|debug|info|notice|warning|error|critical|alert|emergency';
$mPattern = '/(MASK|MAX|MIN)?\s*'
.'('.$priorities.')+\s*'
.'(\&|\||\^)?\s*'
.'('.$priorities.')?'
.'/i';
$result = array();
foreach ($this->xpath->query('loggers') as $logger) {
if ($logger->childNodes->item(1) instanceof DOMElement) {
foreach ($logger->childNodes as $handler) {
if ($handler instanceof DOMElement) {
$_children = array();
$conf = array();
switch ($handler->tagName) {
case 'composite' :
if ($handler->childNodes->item(1)
instanceof DOMElement
) {
foreach ($handler->childNodes as $child) {
if ($child instanceof DOMElement) {
$ident
= (string)$child->getAttribute('ident');
if (!empty($ident)) {
$_children[$ident] = $child->tagName;
}
}
}
}
$ident = $name = $level = $mask = '';
break;
case 'file' :
case 'mail' :
case 'sqlite' :
$ident = (string)$handler->getAttribute('ident');
$name = (string)$handler->getAttribute('name');
if (empty($ident) || empty($name)) {
// misconfiguring file handler, skip this entry
continue 2;
}
$level = (string)$handler->getAttribute('level');
$mask = (string)$handler->getAttribute('mask');
if (preg_match($mPattern, $mask, $matches)) {
$mask = $this->logMaskAdapter($matches);
if ($mask === false) {
// mask error, skip this entry
continue 2;
}
}
$cleanId = str_replace(' ', '_', $ident);
$name = str_replace(
array('%Y', '%M', '%D', '%I'),
array(date('Y'), date('m'), date('d'), $cleanId),
$name
);
break;
case 'null' :
$ident = (string)$handler->getAttribute('ident');
$name = $level = '';
break;
}
// common conf option for all handlers
if ($handler->childNodes->item(1) instanceof DOMElement) {
foreach ($handler->childNodes as $cfg) {
if ($cfg instanceof DOMElement) {
if ($cfg->tagName == 'conf') {
if ($cfg->childNodes->item(1)
instanceof DOMElement
) {
foreach ($cfg->childNodes as $option) {
if ($option instanceof DOMElement) {
$conf[$option->tagName]
= $option->nodeValue;
}
}
}
}
}
}
if (isset($conf['filename'])) {
// only sqlite handler used it
$conf['filename'] = str_replace(
array('%Y','%M','%D', '%I'),
array(date('Y'), date('m'), date('d'), $cleanId),
$conf['filename']
);
}
}
if (empty($level)) {
$level = 'debug';
}
$result[] = array(
'type' => $handler->tagName,
'name' => $name,
'ident' => $ident,
'conf' => $conf,
'level' => $level,
'mask' => $mask,
'_children' => $_children
);
}
}
}
}
return $result;
}
/**
* Compute a complex mask
*
* To compute the mask for a specific level, use for example: MASK notice
*
* To compute the mask for all priorities up to, and including error level,
* use: MAX error
*
* To compute the mask for all priorities greater than or equal to a warning
* level, use: MIN warning
*
* Masks can be be combined using bitwise operations like:
* all ^ info
* info & notice
* error | warning
*
* @param array $matches complex mask condition matches
*
* @return mixed The current level mask, or FALSE on mask definition error
*/
protected function logMaskAdapter($matches)
{
if (!empty($matches[1])) {
// MASK, MIN or MAX
if (!empty($matches[3]) || !empty($matches[4])) {
// mask error
return false;
}
$minMAX = strtoupper($matches[1]);
$priority = Log::stringToPriority(strtolower($matches[2]));
$mask = Log::$minMAX($priority);
} elseif (!empty($matches[3])) {
// bitwise operator
if (empty($matches[2]) || empty($matches[4])) {
// mask error
return false;
}
if ($matches[2] == 'all') {
$p1 = PEAR_LOG_ALL;
} else {
$p1 = Log::stringToPriority(strtolower($matches[2]));
$p1 = Log::MASK($p1);
}
if ($matches[4] == 'all') {
$p2 = PEAR_LOG_ALL;
} else {
$p2 = Log::stringToPriority(strtolower($matches[4]));
$p2 = Log::MASK($p2);
}
switch ($matches[3]) {
case '^':
$mask = $p1 ^ $p2;
break;
case '&':
$mask = $p1 & $p2;
break;
case '|':
$mask = $p1 | $p2;
break;
}
} else {
// mask error
return false;
}
$mask = dechex($mask);
return $mask;
}
}
?>