<?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;
 
    }
 
}
 
?>