From 61399a522fbcb6d968f83aeeb7d21990500b8655 Mon Sep 17 00:00:00 2001
From: meus
Date: Sun, 14 Oct 2007 22:43:29 +0000
Subject: [PATCH] added spikephpcoverage but did not get it to work. if
somebody wants to play with it look in DoctrineTest on line 122. fixed the
coverage report a little
---
tests/DoctrineTest.php | 24 +
tests/DoctrineTest/Coverage.php | 11 +-
tests/coverage/index.php | 1 +
vendor/spikephpcoverage/CoverageRecorder.php | 443 +++++++
vendor/spikephpcoverage/PEAR.php | 1055 +++++++++++++++++
vendor/spikephpcoverage/XML/Parser.php | 690 +++++++++++
vendor/spikephpcoverage/XML/Parser/Simple.php | 297 +++++
vendor/spikephpcoverage/cli/driver.php | 267 +++++
vendor/spikephpcoverage/cli/instrument.php | 305 +++++
.../conf/phpcoverage.conf.php | 26 +
.../parser/BasicXmlParser.php | 171 +++
.../parser/CoverageXmlParser.php | 92 ++
vendor/spikephpcoverage/parser/PHPParser.php | 425 +++++++
vendor/spikephpcoverage/parser/Parser.php | 217 ++++
vendor/spikephpcoverage/phpcoverage.inc.php | 56 +
.../phpcoverage.remote.bottom.inc.php | 51 +
.../phpcoverage.remote.top.inc.php | 151 +++
.../remote/RemoteCoverageRecorder.php | 309 +++++
.../remote/XdebugTraceReader.php | 125 ++
.../reporter/CoverageReporter.php | 302 +++++
.../reporter/HtmlCoverageReporter.php | 616 ++++++++++
.../reporter/css/spikesource.css | 1035 ++++++++++++++++
.../reporter/html/footer.html | 6 +
.../reporter/html/header.html | 17 +
.../reporter/html/indexfooter.html | 18 +
.../reporter/html/indexheader.html | 41 +
.../reporter/images/arrow_down.gif | Bin 0 -> 169 bytes
.../reporter/images/arrow_up.gif | Bin 0 -> 169 bytes
.../reporter/images/spacer.gif | Bin 0 -> 43 bytes
.../images/spikesource/phpcoverage.gif | Bin 0 -> 4271 bytes
.../reporter/js/sort_spikesource.js | 310 +++++
.../spikephpcoverage/util/CoverageLogger.php | 103 ++
vendor/spikephpcoverage/util/Utility.php | 224 ++++
33 files changed, 7383 insertions(+), 5 deletions(-)
create mode 100644 vendor/spikephpcoverage/CoverageRecorder.php
create mode 100644 vendor/spikephpcoverage/PEAR.php
create mode 100644 vendor/spikephpcoverage/XML/Parser.php
create mode 100644 vendor/spikephpcoverage/XML/Parser/Simple.php
create mode 100644 vendor/spikephpcoverage/cli/driver.php
create mode 100644 vendor/spikephpcoverage/cli/instrument.php
create mode 100644 vendor/spikephpcoverage/conf/phpcoverage.conf.php
create mode 100644 vendor/spikephpcoverage/parser/BasicXmlParser.php
create mode 100644 vendor/spikephpcoverage/parser/CoverageXmlParser.php
create mode 100644 vendor/spikephpcoverage/parser/PHPParser.php
create mode 100644 vendor/spikephpcoverage/parser/Parser.php
create mode 100644 vendor/spikephpcoverage/phpcoverage.inc.php
create mode 100644 vendor/spikephpcoverage/phpcoverage.remote.bottom.inc.php
create mode 100644 vendor/spikephpcoverage/phpcoverage.remote.top.inc.php
create mode 100644 vendor/spikephpcoverage/remote/RemoteCoverageRecorder.php
create mode 100644 vendor/spikephpcoverage/remote/XdebugTraceReader.php
create mode 100644 vendor/spikephpcoverage/reporter/CoverageReporter.php
create mode 100644 vendor/spikephpcoverage/reporter/HtmlCoverageReporter.php
create mode 100644 vendor/spikephpcoverage/reporter/css/spikesource.css
create mode 100644 vendor/spikephpcoverage/reporter/html/footer.html
create mode 100644 vendor/spikephpcoverage/reporter/html/header.html
create mode 100644 vendor/spikephpcoverage/reporter/html/indexfooter.html
create mode 100644 vendor/spikephpcoverage/reporter/html/indexheader.html
create mode 100644 vendor/spikephpcoverage/reporter/images/arrow_down.gif
create mode 100644 vendor/spikephpcoverage/reporter/images/arrow_up.gif
create mode 100644 vendor/spikephpcoverage/reporter/images/spacer.gif
create mode 100644 vendor/spikephpcoverage/reporter/images/spikesource/phpcoverage.gif
create mode 100644 vendor/spikephpcoverage/reporter/js/sort_spikesource.js
create mode 100644 vendor/spikephpcoverage/util/CoverageLogger.php
create mode 100644 vendor/spikephpcoverage/util/Utility.php
diff --git a/tests/DoctrineTest.php b/tests/DoctrineTest.php
index 5081dbe3c..15e2b1126 100644
--- a/tests/DoctrineTest.php
+++ b/tests/DoctrineTest.php
@@ -118,6 +118,29 @@ class DoctrineTest
//generate coverage report
if (isset($options['coverage'])) {
+
+ /*
+ * The below code will not work for me (meus). It would be nice if
+ * somebody could give it a try. Just replace this block of code
+ * with the one below
+ *
+ define('PHPCOVERAGE_HOME', dirname(dirname(__FILE__)) . '/vendor/spikephpcoverage');
+ require_once PHPCOVERAGE_HOME . '/CoverageRecorder.php';
+ require_once PHPCOVERAGE_HOME . '/reporter/HtmlCoverageReporter.php';
+
+ $covReporter = new HtmlCoverageReporter('Doctrine Code Coverage Report', '', 'coverage2');
+
+ $includePaths = array('../lib');
+ $excludePaths = array();
+ $cov = new CoverageRecorder($includePaths, $excludePaths, $covReporter);
+
+ $cov->startInstrumentation();
+ $testGroup->run($reporter, $filter);
+ $cov->stopInstrumentation();
+
+ $cov->generateReport();
+ $covReporter->printTextSummary();
+ */
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
$testGroup->run($reporter, $filter);
$result['coverage'] = xdebug_get_code_coverage();
@@ -126,6 +149,7 @@ class DoctrineTest
require_once dirname(__FILE__) . '/DoctrineTest/Coverage.php';
$coverageGeneration = new DoctrineTest_Coverage();
$coverageGeneration->generateReport();
+ // */
} else {
$testGroup->run($reporter, $filter);
}
diff --git a/tests/DoctrineTest/Coverage.php b/tests/DoctrineTest/Coverage.php
index 26051f14b..a8aedae11 100644
--- a/tests/DoctrineTest/Coverage.php
+++ b/tests/DoctrineTest/Coverage.php
@@ -98,13 +98,14 @@ class DoctrineTest_Coverage
$totals['notcovered'] , '';
foreach($coveredArray as $class => $info){
- echo ' | ' . $class . ' | ' . $info['percentage'] . ' % | ' . $info['total'] . ' | ' . $info['covered'] . ' | ' . $info['maybe'] . ' | ' . $info['notcovered']. ' | ';
+
+ echo '
';
if ( $info['type'] == "covered") {
- echo ' | ', $class , ' | ';
- } else {
- echo 'not tested | ';
+ echo '', $class , '';
+ }else{
+ echo $class;
}
- echo '
';
+ echo '' . $info['percentage'] . ' % | ' . $info['total'] . ' | ' . $info['covered'] . ' | ' . $info['maybe'] . ' | ' . $info['notcovered']. ' | ';
}
}
diff --git a/tests/coverage/index.php b/tests/coverage/index.php
index fbbe13b25..18a91ccb4 100644
--- a/tests/coverage/index.php
+++ b/tests/coverage/index.php
@@ -40,6 +40,7 @@ $revision = $svn_info[1];
Coverage report for Doctrine
Report generated against revision getRevision(); ?> current HEAD revision is .
Default mode shows results sorted by percentage with highest first. Customize the ordering with the following GET parameters:
- order = covered|total|maybe|notcovered|percentage
- flip=true
+Files that are not linked in the below table are not tested at all.
|
diff --git a/vendor/spikephpcoverage/CoverageRecorder.php b/vendor/spikephpcoverage/CoverageRecorder.php
new file mode 100644
index 000000000..0e93ed863
--- /dev/null
+++ b/vendor/spikephpcoverage/CoverageRecorder.php
@@ -0,0 +1,443 @@
+
+
+ * @version $Revision: 14665 $
+ */
+ class CoverageRecorder {
+
+ // {{{ Members
+
+ protected $includePaths;
+ protected $excludePaths;
+ protected $reporter;
+ protected $coverageData;
+ protected $isRemote = false;
+ protected $stripped = false;
+ protected $phpCoverageFiles = array("phpcoverage.inc.php");
+ protected $version;
+ protected $logger;
+
+ /**
+ * What extensions are treated as php files.
+ *
+ * @param "php" Array of extension strings
+ */
+ protected $phpExtensions;
+
+ // }}}
+ // {{{ Constructor
+
+ /**
+ * Constructor (PHP5 only)
+ *
+ * @param $includePaths Directories to be included in code coverage report
+ * @param $excludePaths Directories to be excluded from code coverage report
+ * @param $reporter Instance of a Reporter subclass
+ * @access public
+ */
+ public function __construct(
+ $includePaths=array("."),
+ $excludePaths=array(),
+ $reporter="new HtmlCoverageReporter()"
+ ) {
+
+ $this->includePaths = $includePaths;
+ $this->excludePaths = $excludePaths;
+ $this->reporter = $reporter;
+ // Set back reference
+ $this->reporter->setCoverageRecorder($this);
+ $this->excludeCoverageDir();
+ $this->version = "0.8";
+
+ // Configuration
+ global $spc_config;
+ $this->phpExtensions = $spc_config['extensions'];
+ global $util;
+ $this->logger = $util->getLogger();
+ }
+
+ // }}}
+ // {{{ public function startInstrumentation()
+
+ /**
+ * Starts the code coverage recording
+ *
+ * @access public
+ */
+ public function startInstrumentation() {
+ if(extension_loaded("xdebug")) {
+ xdebug_start_code_coverage();
+ return true;
+ }
+ $this->logger->critical("[CoverageRecorder::startInstrumentation()] "
+ . "ERROR: Xdebug not loaded.", __FILE__, __LINE__);
+ return false;
+ }
+
+ // }}}
+ // {{{ public function stopInstrumentation()
+
+ /**
+ * Stops code coverage recording
+ *
+ * @access public
+ */
+ public function stopInstrumentation() {
+ if(extension_loaded("xdebug")) {
+ $this->coverageData = xdebug_get_code_coverage();
+ xdebug_stop_code_coverage();
+ $this->logger->debug("[CoverageRecorder::stopInstrumentation()] Code coverage: " . print_r($this->coverageData, true),
+ __FILE__, __LINE__);
+ return true;
+ }
+ else {
+ $this->logger->critical("[CoverageRecorder::stopInstrumentation()] Xdebug not loaded.", __FILE__, __LINE__);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ public function generateReport()
+
+ /**
+ * Generate the code coverage report
+ *
+ * @access public
+ */
+ public function generateReport() {
+ if($this->isRemote) {
+ $this->logger->info("[CoverageRecorder::generateReport()] "
+ ."Writing report.", __FILE__, __LINE__);
+ }
+ else {
+ $this->logger->info("[CoverageRecorder::generateReport()] "
+ . "Writing report:\t\t", __FILE__, __LINE__);
+ }
+ $this->logger->debug("[CoverageRecoder::generateReport()] " . print_r($this->coverageData, true),
+ __FILE__, __LINE__);
+ $this->unixifyCoverageData();
+ $this->coverageData = $this->stripCoverageData();
+ $this->reporter->generateReport($this->coverageData);
+ if($this->isRemote) {
+ $this->logger->info("[CoverageRecorder::generateReport()] [done]", __FILE__, __LINE__);
+ }
+ else {
+ $this->logger->info("[done]", __FILE__, __LINE__);
+ }
+ }
+
+ // }}}
+ /*{{{ protected function removeAbsentPaths() */
+
+ /**
+ * Remove the directories that do not exist from the input array
+ *
+ * @param &$dirs Array of directory names
+ * @access protected
+ */
+ protected function removeAbsentPaths(&$dirs) {
+ for($i = 0; $i < count($dirs); $i++) {
+ if(! file_exists($dirs[$i])) {
+ // echo "Not found: " . $dirs[$i] . "\n";
+ $this->errors[] = "Not found: " . $dirs[$i]
+ . ". Removing ...";
+ array_splice($dirs, $i, 1);
+ $i--;
+ }
+ else {
+ $dirs[$i] = realpath($dirs[$i]);
+ }
+ }
+ }
+
+ /*}}}*/
+ // {{{ protected function processSourcePaths()
+
+ /**
+ * Processes and validates the source directories
+ *
+ * @access protected
+ */
+ protected function processSourcePaths() {
+ $this->removeAbsentPaths($this->includePaths);
+ $this->removeAbsentPaths($this->excludePaths);
+
+ sort($this->includePaths, SORT_STRING);
+ }
+
+ // }}}
+ /*{{{ protected function getFilesAndDirs() */
+
+ /**
+ * Get the list of files that match the extensions in $this->phpExtensions
+ *
+ * @param $dir Root directory
+ * @param &$files Array of filenames to append to
+ * @access protected
+ */
+ protected function getFilesAndDirs($dir, &$files) {
+ global $util;
+ $dirs[] = $dir;
+ while(count($dirs) > 0) {
+ $currDir = realpath(array_pop($dirs));
+ if(!is_readable($currDir)) {
+ continue;
+ }
+ //echo "Current Dir: $currDir \n";
+ $currFiles = scandir($currDir);
+ //print_r($currFiles);
+ for($j = 0; $j < count($currFiles); $j++) {
+ if($currFiles[$j] == "." || $currFiles[$j] == "..") {
+ continue;
+ }
+ $currFiles[$j] = $currDir . "/" . $currFiles[$j];
+ //echo "Current File: " . $currFiles[$j] . "\n";
+ if(is_file($currFiles[$j])) {
+ $pathParts = pathinfo($currFiles[$j]);
+ if(isset($pathParts['extension']) && in_array($pathParts['extension'], $this->phpExtensions)) {
+ $files[] = $util->replaceBackslashes($currFiles[$j]);
+ }
+ }
+ if(is_dir($currFiles[$j])) {
+ $dirs[] = $currFiles[$j];
+ }
+ }
+ }
+ }
+
+ /*}}}*/
+ /*{{{ protected function addFiles() */
+
+ /**
+ * Add all source files to the list of files that need to be parsed.
+ *
+ * @access protected
+ */
+ protected function addFiles() {
+ global $util;
+ $files = array();
+ for($i = 0; $i < count($this->includePaths); $i++) {
+ $this->includePaths[$i] = $util->replaceBackslashes($this->includePaths[$i]);
+ if(is_dir($this->includePaths[$i])) {
+ //echo "Calling getFilesAndDirs with " . $this->includePaths[$i] . "\n";
+ $this->getFilesAndDirs($this->includePaths[$i], $files);
+ }
+ else if(is_file($this->includePaths[$i])) {
+ $files[] = $this->includePaths[$i];
+ }
+ }
+
+ $this->logger->debug("Found files:" . print_r($files, true),
+ __FILE__, __LINE__);
+ for($i = 0; $i < count($this->excludePaths); $i++) {
+ $this->excludePaths[$i] = $util->replaceBackslashes($this->excludePaths[$i]);
+ }
+
+ for($i = 0; $i < count($files); $i++) {
+ for($j = 0; $j < count($this->excludePaths); $j++) {
+ $this->logger->debug($files[$i] . "\t" . $this->excludePaths[$j] . "\n", __FILE__, __LINE__);
+ if(strpos($files[$i], $this->excludePaths[$j]) === 0) {
+ continue;
+ }
+ }
+ if(!array_key_exists($files[$i], $this->coverageData)) {
+ $this->coverageData[$files[$i]] = array();
+ }
+ }
+ }
+
+ /*}}}*/
+ // {{{ protected function stripCoverageData()
+
+ /**
+ * Removes the unwanted coverage data from the recordings
+ *
+ * @return Processed coverage data
+ * @access protected
+ */
+ protected function stripCoverageData() {
+ if($this->stripped) {
+ $this->logger->debug("[CoverageRecorder::stripCoverageData()] Already stripped!", __FILE__, __LINE__);
+ return $this->coverageData;
+ }
+ $this->stripped = true;
+ if(empty($this->coverageData)) {
+ $this->logger->warn("[CoverageRecorder::stripCoverageData()] No coverage data found.", __FILE__, __LINE__);
+ return $this->coverageData;
+ }
+ $this->processSourcePaths();
+ $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!",
+ __FILE__, __LINE__);
+ $this->logger->debug(print_r($this->includePaths, true),
+ __FILE__, __LINE__);
+ $this->logger->debug(print_r($this->excludePaths, true),
+ __FILE__, __LINE__);
+ $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!",
+ __FILE__, __LINE__);
+ $this->addFiles();
+ $altCoverageData = array();
+ foreach ($this->coverageData as $filename => &$lines) {
+ $preserve = false;
+ $realFile = $filename;
+ for($i = 0; $i < count($this->includePaths); $i++) {
+ if(strpos($realFile, $this->includePaths[$i]) === 0) {
+ $preserve = true;
+ }
+ else {
+ $this->logger->debug("File: " . $realFile
+ . "\nDoes not match: " . $this->includePaths[$i],
+ __FILE__, __LINE__);
+ }
+ }
+ // Exclude dirs have a precedence over includes.
+ for($i = 0; $i < count($this->excludePaths); $i++) {
+ if(strpos($realFile, $this->excludePaths[$i]) === 0) {
+ $preserve = false;
+ }
+ else if(in_array(basename($realFile), $this->phpCoverageFiles)) {
+ $preserve = false;
+ }
+ }
+ if($preserve) {
+ // Should be preserved
+ $altCoverageData[$filename] = $lines;
+ }
+ }
+
+ array_multisort($altCoverageData, SORT_STRING);
+ return $altCoverageData;
+ }
+
+ // }}}
+ /*{{{ protected function unixifyCoverageData() */
+
+ /**
+ * Convert filepaths in coverage data to forward slash separated
+ * paths.
+ *
+ * @access protected
+ */
+ protected function unixifyCoverageData() {
+ global $util;
+ $tmpCoverageData = array();
+ foreach($this->coverageData as $file => &$lines) {
+ $tmpCoverageData[$util->replaceBackslashes(realpath($file))] = $lines;
+ }
+ $this->coverageData = $tmpCoverageData;
+ }
+
+ /*}}}*/
+ // {{{ public function getErrors()
+
+ /**
+ * Returns the errors array containing all error encountered so far.
+ *
+ * @return Array of error messages
+ * @access public
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ // }}}
+ // {{{ public function logErrors()
+
+ /**
+ * Writes all error messages to error log
+ *
+ * @access public
+ */
+ public function logErrors() {
+ $this->logger->error(print_r($this->errors, true),
+ __FILE__, __LINE__);
+ }
+
+ // }}}
+ /*{{{ Getters and Setters */
+
+ public function getIncludePaths() {
+ return $this->includePaths;
+ }
+
+ public function setIncludePaths($includePaths) {
+ $this->includePaths = $includePaths;
+ }
+
+ public function getExcludePaths() {
+ return $this->excludePaths;
+ }
+
+ public function setExcludePaths($excludePaths) {
+ $this->excludePaths = $excludePaths;
+ $this->excludeCoverageDir();
+ }
+
+ public function getReporter() {
+ return $this->reporter;
+ }
+
+ public function setReporter(&$reporter) {
+ $this->reporter = $reporter;
+ }
+
+ public function getPhpExtensions() {
+ return $this->phpExtensions;
+ }
+
+ public function setPhpExtensions(&$extensions) {
+ $this->phpExtensions = $extensions;
+ }
+
+ public function getVersion() {
+ return $this->version;
+ }
+
+ /*}}}*/
+ /*{{{ public function excludeCoverageDir() */
+
+ /**
+ * Exclude the directory containing the coverage measurement code.
+ *
+ * @access public
+ */
+ public function excludeCoverageDir() {
+ $f = __FILE__;
+ if(is_link($f)) {
+ $f = readlink($f);
+ }
+ $this->excludePaths[] = realpath(dirname($f));
+ }
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/PEAR.php b/vendor/spikephpcoverage/PEAR.php
new file mode 100644
index 000000000..865b8a155
--- /dev/null
+++ b/vendor/spikephpcoverage/PEAR.php
@@ -0,0 +1,1055 @@
+ |
+// | Stig Bakken |
+// | Tomas V.V.Cox |
+// +--------------------------------------------------------------------+
+//
+// $Id: PEAR.php,v 1.82.2.1 2005/03/28 16:46:06 cellog Exp $
+//
+
+define('PEAR_ERROR_RETURN', 1);
+define('PEAR_ERROR_PRINT', 2);
+define('PEAR_ERROR_TRIGGER', 4);
+define('PEAR_ERROR_DIE', 8);
+define('PEAR_ERROR_CALLBACK', 16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+define('PEAR_ZE2', (function_exists('version_compare') &&
+ version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+ define('OS_WINDOWS', true);
+ define('OS_UNIX', false);
+ define('PEAR_OS', 'Windows');
+} else {
+ define('OS_WINDOWS', false);
+ define('OS_UNIX', true);
+ define('PEAR_OS', 'Unix'); // blatant assumption
+}
+
+// instant backwards compatibility
+if (!defined('PATH_SEPARATOR')) {
+ if (OS_WINDOWS) {
+ define('PATH_SEPARATOR', ';');
+ } else {
+ define('PATH_SEPARATOR', ':');
+ }
+}
+
+$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs'] = array();
+$GLOBALS['_PEAR_error_handler_stack'] = array();
+
+@ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes. Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix). Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters. Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded. If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken
+ * @see http://pear.php.net/manual/
+ */
+class PEAR
+{
+ // {{{ properties
+
+ /**
+ * Whether to enable internal debug messages.
+ *
+ * @var bool
+ * @access private
+ */
+ var $_debug = false;
+
+ /**
+ * Default error mode for this object.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_mode = null;
+
+ /**
+ * Default error options used for this object when error mode
+ * is PEAR_ERROR_TRIGGER.
+ *
+ * @var int
+ * @access private
+ */
+ var $_default_error_options = null;
+
+ /**
+ * Default error handler (callback) for this object, if error mode is
+ * PEAR_ERROR_CALLBACK.
+ *
+ * @var string
+ * @access private
+ */
+ var $_default_error_handler = '';
+
+ /**
+ * Which class to use for error objects.
+ *
+ * @var string
+ * @access private
+ */
+ var $_error_class = 'PEAR_Error';
+
+ /**
+ * An array of expected errors.
+ *
+ * @var array
+ * @access private
+ */
+ var $_expected_errors = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * Constructor. Registers this object in
+ * $_PEAR_destructor_object_list for destructor emulation if a
+ * destructor object exists.
+ *
+ * @param string $error_class (optional) which class to use for
+ * error objects, defaults to PEAR_Error.
+ * @access public
+ * @return void
+ */
+ function PEAR($error_class = null)
+ {
+ $classname = strtolower(get_class($this));
+ if ($this->_debug) {
+ print "PEAR constructor called, class=$classname\n";
+ }
+ if ($error_class !== null) {
+ $this->_error_class = $error_class;
+ }
+ while ($classname && strcasecmp($classname, "pear")) {
+ $destructor = "_$classname";
+ if (method_exists($this, $destructor)) {
+ global $_PEAR_destructor_object_list;
+ $_PEAR_destructor_object_list[] = &$this;
+ if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+ register_shutdown_function("_PEAR_call_destructors");
+ $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+ }
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * Destructor (the emulated type of...). Does nothing right now,
+ * but is included for forward compatibility, so subclass
+ * destructors should always call it.
+ *
+ * See the note in the class desciption about output from
+ * destructors.
+ *
+ * @access public
+ * @return void
+ */
+ function _PEAR() {
+ if ($this->_debug) {
+ printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+ }
+ }
+
+ // }}}
+ // {{{ getStaticProperty()
+
+ /**
+ * If you have a class that's mostly/entirely static, and you need static
+ * properties, you can use this method to simulate them. Eg. in your method(s)
+ * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+ * You MUST use a reference, or they will not persist!
+ *
+ * @access public
+ * @param string $class The calling classname, to prevent clashes
+ * @param string $var The variable to retrieve.
+ * @return mixed A reference to the variable. If not set it will be
+ * auto initialised to NULL.
+ */
+ function &getStaticProperty($class, $var)
+ {
+ static $properties;
+ return $properties[$class][$var];
+ }
+
+ // }}}
+ // {{{ registerShutdownFunc()
+
+ /**
+ * Use this function to register a shutdown method for static
+ * classes.
+ *
+ * @access public
+ * @param mixed $func The function name (or array of class/method) to call
+ * @param mixed $args The arguments to pass to the function
+ * @return void
+ */
+ function registerShutdownFunc($func, $args = array())
+ {
+ $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+ }
+
+ // }}}
+ // {{{ isError()
+
+ /**
+ * Tell whether a value is a PEAR error.
+ *
+ * @param mixed $data the value to test
+ * @param int $code if $data is an error object, return true
+ * only if $code is a string and
+ * $obj->getMessage() == $code or
+ * $code is an integer and $obj->getCode() == $code
+ * @access public
+ * @return bool true if parameter is an error
+ */
+ function isError($data, $code = null)
+ {
+ if (is_a($data, 'PEAR_Error')) {
+ if (is_null($code)) {
+ return true;
+ } elseif (is_string($code)) {
+ return $data->getMessage() == $code;
+ } else {
+ return $data->getCode() == $code;
+ }
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ setErrorHandling()
+
+ /**
+ * Sets how errors generated by this object should be handled.
+ * Can be invoked both in objects and statically. If called
+ * statically, setErrorHandling sets the default behaviour for all
+ * PEAR objects. If called in an object, setErrorHandling sets
+ * the default behaviour for that object.
+ *
+ * @param int $mode
+ * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options
+ * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+ * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ *
+ * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+ * to be the callback function or method. A callback
+ * function is a string with the name of the function, a
+ * callback method is an array of two elements: the element
+ * at index 0 is the object, and the element at index 1 is
+ * the name of the method to call in the object.
+ *
+ * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+ * a printf format string used when printing the error
+ * message.
+ *
+ * @access public
+ * @return void
+ * @see PEAR_ERROR_RETURN
+ * @see PEAR_ERROR_PRINT
+ * @see PEAR_ERROR_TRIGGER
+ * @see PEAR_ERROR_DIE
+ * @see PEAR_ERROR_CALLBACK
+ * @see PEAR_ERROR_EXCEPTION
+ *
+ * @since PHP 4.0.5
+ */
+
+ function setErrorHandling($mode = null, $options = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $setmode = &$this->_default_error_mode;
+ $setoptions = &$this->_default_error_options;
+ } else {
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ }
+
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ expectError()
+
+ /**
+ * This method is used to tell which errors you expect to get.
+ * Expected errors are always returned with error mode
+ * PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
+ * and this method pushes a new element onto it. The list of
+ * expected errors are in effect until they are popped off the
+ * stack with the popExpect() method.
+ *
+ * Note that this method can not be called statically
+ *
+ * @param mixed $code a single error code or an array of error codes to expect
+ *
+ * @return int the new depth of the "expected errors" stack
+ * @access public
+ */
+ function expectError($code = '*')
+ {
+ if (is_array($code)) {
+ array_push($this->_expected_errors, $code);
+ } else {
+ array_push($this->_expected_errors, array($code));
+ }
+ return sizeof($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ popExpect()
+
+ /**
+ * This method pops one element off the expected error codes
+ * stack.
+ *
+ * @return array the list of error codes that were popped
+ */
+ function popExpect()
+ {
+ return array_pop($this->_expected_errors);
+ }
+
+ // }}}
+ // {{{ _checkDelExpect()
+
+ /**
+ * This method checks unsets an error code if available
+ *
+ * @param mixed error code
+ * @return bool true if the error code was unset, false otherwise
+ * @access private
+ * @since PHP 4.3.0
+ */
+ function _checkDelExpect($error_code)
+ {
+ $deleted = false;
+
+ foreach ($this->_expected_errors AS $key => $error_array) {
+ if (in_array($error_code, $error_array)) {
+ unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+ $deleted = true;
+ }
+
+ // clean up empty arrays
+ if (0 == count($this->_expected_errors[$key])) {
+ unset($this->_expected_errors[$key]);
+ }
+ }
+ return $deleted;
+ }
+
+ // }}}
+ // {{{ delExpect()
+
+ /**
+ * This method deletes all occurences of the specified element from
+ * the expected error codes stack.
+ *
+ * @param mixed $error_code error code that should be deleted
+ * @return mixed list of error codes that were deleted or error
+ * @access public
+ * @since PHP 4.3.0
+ */
+ function delExpect($error_code)
+ {
+ $deleted = false;
+
+ if ((is_array($error_code) && (0 != count($error_code)))) {
+ // $error_code is a non-empty array here;
+ // we walk through it trying to unset all
+ // values
+ foreach($error_code as $key => $error) {
+ if ($this->_checkDelExpect($error)) {
+ $deleted = true;
+ } else {
+ $deleted = false;
+ }
+ }
+ return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ } elseif (!empty($error_code)) {
+ // $error_code comes alone, trying to unset it
+ if ($this->_checkDelExpect($error_code)) {
+ return true;
+ } else {
+ return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+ }
+ } else {
+ // $error_code is empty
+ return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+ }
+ }
+
+ // }}}
+ // {{{ raiseError()
+
+ /**
+ * This method is a wrapper that returns an instance of the
+ * configured error class with this object's default error
+ * handling applied. If the $mode and $options parameters are not
+ * specified, the object's defaults are used.
+ *
+ * @param mixed $message a text error message or a PEAR error object
+ *
+ * @param int $code a numeric error code (it is up to your class
+ * to define these if you want to use codes)
+ *
+ * @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+ * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+ * PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+ *
+ * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+ * specifies the PHP-internal error level (one of
+ * E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+ * If $mode is PEAR_ERROR_CALLBACK, this
+ * parameter specifies the callback function or
+ * method. In other error modes this parameter
+ * is ignored.
+ *
+ * @param string $userinfo If you need to pass along for example debug
+ * information, this parameter is meant for that.
+ *
+ * @param string $error_class The returned error object will be
+ * instantiated from this class, if specified.
+ *
+ * @param bool $skipmsg If true, raiseError will only pass error codes,
+ * the error message parameter will be dropped.
+ *
+ * @access public
+ * @return object a PEAR error object
+ * @see PEAR::setErrorHandling
+ * @since PHP 4.0.5
+ */
+ function raiseError($message = null,
+ $code = null,
+ $mode = null,
+ $options = null,
+ $userinfo = null,
+ $error_class = null,
+ $skipmsg = false)
+ {
+ // The error is yet a PEAR error object
+ if (is_object($message)) {
+ $code = $message->getCode();
+ $userinfo = $message->getUserInfo();
+ $error_class = $message->getType();
+ $message->error_message_prefix = '';
+ $message = $message->getMessage();
+ }
+
+ if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+ if ($exp[0] == "*" ||
+ (is_int(reset($exp)) && in_array($code, $exp)) ||
+ (is_string(reset($exp)) && in_array($message, $exp))) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ }
+ // No mode given, try global ones
+ if ($mode === null) {
+ // Class error handler
+ if (isset($this) && isset($this->_default_error_mode)) {
+ $mode = $this->_default_error_mode;
+ $options = $this->_default_error_options;
+ // Global error handler
+ } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+ $mode = $GLOBALS['_PEAR_default_error_mode'];
+ $options = $GLOBALS['_PEAR_default_error_options'];
+ }
+ }
+
+ if ($error_class !== null) {
+ $ec = $error_class;
+ } elseif (isset($this) && isset($this->_error_class)) {
+ $ec = $this->_error_class;
+ } else {
+ $ec = 'PEAR_Error';
+ }
+ if ($skipmsg) {
+ return new $ec($code, $mode, $options, $userinfo);
+ } else {
+ return new $ec($message, $code, $mode, $options, $userinfo);
+ }
+ }
+
+ // }}}
+ // {{{ throwError()
+
+ /**
+ * Simpler form of raiseError with fewer options. In most cases
+ * message, code and userinfo are enough.
+ *
+ * @param string $message
+ *
+ */
+ function throwError($message = null,
+ $code = null,
+ $userinfo = null)
+ {
+ if (isset($this) && is_a($this, 'PEAR')) {
+ return $this->raiseError($message, $code, null, null, $userinfo);
+ } else {
+ return PEAR::raiseError($message, $code, null, null, $userinfo);
+ }
+ }
+
+ // }}}
+ function staticPushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ $stack[] = array($def_mode, $def_options);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $def_mode = $mode;
+ $def_options = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $def_mode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $def_options = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ function staticPopErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ $setmode = &$GLOBALS['_PEAR_default_error_mode'];
+ $setoptions = &$GLOBALS['_PEAR_default_error_options'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ switch ($mode) {
+ case PEAR_ERROR_EXCEPTION:
+ case PEAR_ERROR_RETURN:
+ case PEAR_ERROR_PRINT:
+ case PEAR_ERROR_TRIGGER:
+ case PEAR_ERROR_DIE:
+ case null:
+ $setmode = $mode;
+ $setoptions = $options;
+ break;
+
+ case PEAR_ERROR_CALLBACK:
+ $setmode = $mode;
+ // class/object method callback
+ if (is_callable($options)) {
+ $setoptions = $options;
+ } else {
+ trigger_error("invalid error callback", E_USER_WARNING);
+ }
+ break;
+
+ default:
+ trigger_error("invalid error mode", E_USER_WARNING);
+ break;
+ }
+ return true;
+ }
+
+ // {{{ pushErrorHandling()
+
+ /**
+ * Push a new error handler on top of the error handler options stack. With this
+ * you can easily override the actual error handler for some code and restore
+ * it later with popErrorHandling.
+ *
+ * @param mixed $mode (same as setErrorHandling)
+ * @param mixed $options (same as setErrorHandling)
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::setErrorHandling
+ */
+ function pushErrorHandling($mode, $options = null)
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $def_mode = &$this->_default_error_mode;
+ $def_options = &$this->_default_error_options;
+ } else {
+ $def_mode = &$GLOBALS['_PEAR_default_error_mode'];
+ $def_options = &$GLOBALS['_PEAR_default_error_options'];
+ }
+ $stack[] = array($def_mode, $def_options);
+
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ $stack[] = array($mode, $options);
+ return true;
+ }
+
+ // }}}
+ // {{{ popErrorHandling()
+
+ /**
+ * Pop the last error handler used
+ *
+ * @return bool Always true
+ *
+ * @see PEAR::pushErrorHandling
+ */
+ function popErrorHandling()
+ {
+ $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+ array_pop($stack);
+ list($mode, $options) = $stack[sizeof($stack) - 1];
+ array_pop($stack);
+ if (isset($this) && is_a($this, 'PEAR')) {
+ $this->setErrorHandling($mode, $options);
+ } else {
+ PEAR::setErrorHandling($mode, $options);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ loadExtension()
+
+ /**
+ * OS independant PHP extension load. Remember to take care
+ * on the correct extension name for case sensitive OSes.
+ *
+ * @param string $ext The extension name
+ * @return bool Success or not on the dl() call
+ */
+ function loadExtension($ext)
+ {
+ if (!extension_loaded($ext)) {
+ // if either returns true dl() will produce a FATAL error, stop that
+ if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+ return false;
+ }
+ if (OS_WINDOWS) {
+ $suffix = '.dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $suffix = '.sl';
+ } elseif (PHP_OS == 'AIX') {
+ $suffix = '.a';
+ } elseif (PHP_OS == 'OSX') {
+ $suffix = '.bundle';
+ } else {
+ $suffix = '.so';
+ }
+ return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+ }
+ return true;
+ }
+
+ // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+ global $_PEAR_destructor_object_list;
+ if (is_array($_PEAR_destructor_object_list) &&
+ sizeof($_PEAR_destructor_object_list))
+ {
+ reset($_PEAR_destructor_object_list);
+ if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
+ $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+ }
+ while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+ $classname = get_class($objref);
+ while ($classname) {
+ $destructor = "_$classname";
+ if (method_exists($objref, $destructor)) {
+ $objref->$destructor();
+ break;
+ } else {
+ $classname = get_parent_class($classname);
+ }
+ }
+ }
+ // Empty the object list to ensure that destructors are
+ // not called more than once.
+ $_PEAR_destructor_object_list = array();
+ }
+
+ // Now call the shutdown functions
+ if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+ foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+ call_user_func_array($value[0], $value[1]);
+ }
+ }
+}
+
+// }}}
+
+class PEAR_Error
+{
+ // {{{ properties
+
+ var $error_message_prefix = '';
+ var $mode = PEAR_ERROR_RETURN;
+ var $level = E_USER_NOTICE;
+ var $code = -1;
+ var $message = '';
+ var $userinfo = '';
+ var $backtrace = null;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Error constructor
+ *
+ * @param string $message message
+ *
+ * @param int $code (optional) error code
+ *
+ * @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
+ * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+ * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+ *
+ * @param mixed $options (optional) error level, _OR_ in the case of
+ * PEAR_ERROR_CALLBACK, the callback function or object/method
+ * tuple.
+ *
+ * @param string $userinfo (optional) additional user/debug info
+ *
+ * @access public
+ *
+ */
+ function PEAR_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+ if ($mode === null) {
+ $mode = PEAR_ERROR_RETURN;
+ }
+ $this->message = $message;
+ $this->code = $code;
+ $this->mode = $mode;
+ $this->userinfo = $userinfo;
+ if (function_exists("debug_backtrace")) {
+ if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+ $this->backtrace = debug_backtrace();
+ }
+ }
+ if ($mode & PEAR_ERROR_CALLBACK) {
+ $this->level = E_USER_NOTICE;
+ $this->callback = $options;
+ } else {
+ if ($options === null) {
+ $options = E_USER_NOTICE;
+ }
+ $this->level = $options;
+ $this->callback = null;
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ } else {
+ $format = $options;
+ }
+ printf($format, $this->getMessage());
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ trigger_error($this->getMessage(), $this->level);
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $msg = $this->getMessage();
+ if (is_null($options) || is_int($options)) {
+ $format = "%s";
+ if (substr($msg, -1) != "\n") {
+ $msg .= "\n";
+ }
+ } else {
+ $format = $options;
+ }
+ die(sprintf($format, $msg));
+ }
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_callable($this->callback)) {
+ call_user_func($this->callback, $this);
+ }
+ }
+ if ($this->mode & PEAR_ERROR_EXCEPTION) {
+ trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING);
+ eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);');
+ }
+ }
+
+ // }}}
+ // {{{ getMode()
+
+ /**
+ * Get the error mode from an error object.
+ *
+ * @return int error mode
+ * @access public
+ */
+ function getMode() {
+ return $this->mode;
+ }
+
+ // }}}
+ // {{{ getCallback()
+
+ /**
+ * Get the callback function/method from an error object.
+ *
+ * @return mixed callback function or object/method array
+ * @access public
+ */
+ function getCallback() {
+ return $this->callback;
+ }
+
+ // }}}
+ // {{{ getMessage()
+
+
+ /**
+ * Get the error message from an error object.
+ *
+ * @return string full error message
+ * @access public
+ */
+ function getMessage()
+ {
+ return ($this->error_message_prefix . $this->message);
+ }
+
+
+ // }}}
+ // {{{ getCode()
+
+ /**
+ * Get error code from an error object
+ *
+ * @return int error code
+ * @access public
+ */
+ function getCode()
+ {
+ return $this->code;
+ }
+
+ // }}}
+ // {{{ getType()
+
+ /**
+ * Get the name of this error/exception.
+ *
+ * @return string error/exception name (type)
+ * @access public
+ */
+ function getType()
+ {
+ return get_class($this);
+ }
+
+ // }}}
+ // {{{ getUserInfo()
+
+ /**
+ * Get additional user-supplied information.
+ *
+ * @return string user-supplied information
+ * @access public
+ */
+ function getUserInfo()
+ {
+ return $this->userinfo;
+ }
+
+ // }}}
+ // {{{ getDebugInfo()
+
+ /**
+ * Get additional debug information supplied by the application.
+ *
+ * @return string debug information
+ * @access public
+ */
+ function getDebugInfo()
+ {
+ return $this->getUserInfo();
+ }
+
+ // }}}
+ // {{{ getBacktrace()
+
+ /**
+ * Get the call backtrace from where the error was generated.
+ * Supported with PHP 4.3.0 or newer.
+ *
+ * @param int $frame (optional) what frame to fetch
+ * @return array Backtrace, or NULL if not available.
+ * @access public
+ */
+ function getBacktrace($frame = null)
+ {
+ if ($frame === null) {
+ return $this->backtrace;
+ }
+ return $this->backtrace[$frame];
+ }
+
+ // }}}
+ // {{{ addUserInfo()
+
+ function addUserInfo($info)
+ {
+ if (empty($this->userinfo)) {
+ $this->userinfo = $info;
+ } else {
+ $this->userinfo .= " ** $info";
+ }
+ }
+
+ // }}}
+ // {{{ toString()
+
+ /**
+ * Make a string representation of this object.
+ *
+ * @return string a string with an object summary
+ * @access public
+ */
+ function toString() {
+ $modes = array();
+ $levels = array(E_USER_NOTICE => 'notice',
+ E_USER_WARNING => 'warning',
+ E_USER_ERROR => 'error');
+ if ($this->mode & PEAR_ERROR_CALLBACK) {
+ if (is_array($this->callback)) {
+ $callback = (is_object($this->callback[0]) ?
+ strtolower(get_class($this->callback[0])) :
+ $this->callback[0]) . '::' .
+ $this->callback[1];
+ } else {
+ $callback = $this->callback;
+ }
+ return sprintf('[%s: message="%s" code=%d mode=callback '.
+ 'callback=%s prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ $callback, $this->error_message_prefix,
+ $this->userinfo);
+ }
+ if ($this->mode & PEAR_ERROR_PRINT) {
+ $modes[] = 'print';
+ }
+ if ($this->mode & PEAR_ERROR_TRIGGER) {
+ $modes[] = 'trigger';
+ }
+ if ($this->mode & PEAR_ERROR_DIE) {
+ $modes[] = 'die';
+ }
+ if ($this->mode & PEAR_ERROR_RETURN) {
+ $modes[] = 'return';
+ }
+ return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+ 'prefix="%s" info="%s"]',
+ strtolower(get_class($this)), $this->message, $this->code,
+ implode("|", $modes), $levels[$this->level],
+ $this->error_message_prefix,
+ $this->userinfo);
+ }
+
+ // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/vendor/spikephpcoverage/XML/Parser.php b/vendor/spikephpcoverage/XML/Parser.php
new file mode 100644
index 000000000..4173fbce4
--- /dev/null
+++ b/vendor/spikephpcoverage/XML/Parser.php
@@ -0,0 +1,690 @@
+ |
+// | Tomas V.V.Cox |
+// | Stephan Schmidt |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parser.php,v 1.28 2006/12/01 16:23:22 schst Exp $
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stig Bakken
+ * @author Tomas V.V.Cox
+ * @author Stephan Schmidt
+ */
+
+/**
+ * uses PEAR's error handling
+ */
+require_once 'PEAR.php';
+
+/**
+ * resource could not be created
+ */
+define('XML_PARSER_ERROR_NO_RESOURCE', 200);
+
+/**
+ * unsupported mode
+ */
+define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
+
+/**
+ * invalid encoding was given
+ */
+define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
+
+/**
+ * specified file could not be read
+ */
+define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
+
+/**
+ * invalid input
+ */
+define('XML_PARSER_ERROR_INVALID_INPUT', 204);
+
+/**
+ * remote file cannot be retrieved in safe mode
+ */
+define('XML_PARSER_ERROR_REMOTE', 205);
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * Notes:
+ * - It requires PHP 4.0.4pl1 or greater
+ * - From revision 1.17, the function names used by the 'func' mode
+ * are in the format "xmltag_$elem", for example: use "xmltag_name"
+ * to handle the tags of your xml file.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stig Bakken
+ * @author Tomas V.V.Cox
+ * @author Stephan Schmidt
+ * @todo create XML_Parser_Namespace to parse documents with namespaces
+ * @todo create XML_Parser_Pull
+ * @todo Tests that need to be made:
+ * - mixing character encodings
+ * - a test using all expat handlers
+ * - options (folding, output charset)
+ * - different parsing modes
+ */
+class XML_Parser extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * XML parser handle
+ *
+ * @var resource
+ * @see xml_parser_create()
+ */
+ var $parser;
+
+ /**
+ * File handle if parsing from a file
+ *
+ * @var resource
+ */
+ var $fp;
+
+ /**
+ * Whether to do case folding
+ *
+ * If set to true, all tag and attribute names will
+ * be converted to UPPER CASE.
+ *
+ * @var boolean
+ */
+ var $folding = true;
+
+ /**
+ * Mode of operation, one of "event" or "func"
+ *
+ * @var string
+ */
+ var $mode;
+
+ /**
+ * Mapping from expat handler function to class method.
+ *
+ * @var array
+ */
+ var $handler = array(
+ 'character_data_handler' => 'cdataHandler',
+ 'default_handler' => 'defaultHandler',
+ 'processing_instruction_handler' => 'piHandler',
+ 'unparsed_entity_decl_handler' => 'unparsedHandler',
+ 'notation_decl_handler' => 'notationHandler',
+ 'external_entity_ref_handler' => 'entityrefHandler'
+ );
+
+ /**
+ * source encoding
+ *
+ * @var string
+ */
+ var $srcenc;
+
+ /**
+ * target encoding
+ *
+ * @var string
+ */
+ var $tgtenc;
+
+ /**
+ * handler object
+ *
+ * @var object
+ */
+ var $_handlerObj;
+
+ /**
+ * valid encodings
+ *
+ * @var array
+ */
+ var $_validEncodings = array('ISO-8859-1', 'UTF-8', 'US-ASCII');
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * Creates an XML parser.
+ *
+ * This is needed for PHP4 compatibility, it will
+ * call the constructor, when a new instance is created.
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * startelement/endelement-type events, "func"
+ * to have it call functions named after elements
+ * @param string $tgenc a valid target encoding
+ */
+ function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ XML_Parser::__construct($srcenc, $mode, $tgtenc);
+ }
+ // }}}
+
+ /**
+ * PHP5 constructor
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * startelement/endelement-type events, "func"
+ * to have it call functions named after elements
+ * @param string $tgenc a valid target encoding
+ */
+ function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ $this->PEAR('XML_Parser_Error');
+
+ $this->mode = $mode;
+ $this->srcenc = $srcenc;
+ $this->tgtenc = $tgtenc;
+ }
+ // }}}
+
+ /**
+ * Sets the mode of the parser.
+ *
+ * Possible modes are:
+ * - func
+ * - event
+ *
+ * You can set the mode using the second parameter
+ * in the constructor.
+ *
+ * This method is only needed, when switching to a new
+ * mode at a later point.
+ *
+ * @access public
+ * @param string mode, either 'func' or 'event'
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function setMode($mode)
+ {
+ if ($mode != 'func' && $mode != 'event') {
+ $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ }
+
+ $this->mode = $mode;
+ return true;
+ }
+
+ /**
+ * Sets the object, that will handle the XML events
+ *
+ * This allows you to create a handler object independent of the
+ * parser object that you are using and easily switch the underlying
+ * parser.
+ *
+ * If no object will be set, XML_Parser assumes that you
+ * extend this class and handle the events in $this.
+ *
+ * @access public
+ * @param object object to handle the events
+ * @return boolean will always return true
+ * @since v1.2.0beta3
+ */
+ function setHandlerObj(&$obj)
+ {
+ $this->_handlerObj = &$obj;
+ return true;
+ }
+
+ /**
+ * Init the element handlers
+ *
+ * @access private
+ */
+ function _initHandlers()
+ {
+ if (!is_resource($this->parser)) {
+ return false;
+ }
+
+ if (!is_object($this->_handlerObj)) {
+ $this->_handlerObj = &$this;
+ }
+ switch ($this->mode) {
+
+ case 'func':
+ xml_set_object($this->parser, $this->_handlerObj);
+ xml_set_element_handler($this->parser, array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
+ break;
+
+ case 'event':
+ xml_set_object($this->parser, $this->_handlerObj);
+ xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
+ break;
+ default:
+ return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ break;
+ }
+
+
+ /**
+ * set additional handlers for character data, entities, etc.
+ */
+ foreach ($this->handler as $xml_func => $method) {
+ if (method_exists($this->_handlerObj, $method)) {
+ $xml_func = 'xml_set_' . $xml_func;
+ $xml_func($this->parser, $method);
+ }
+ }
+ }
+
+ // {{{ _create()
+
+ /**
+ * create the XML parser resource
+ *
+ * Has been moved from the constructor to avoid
+ * problems with object references.
+ *
+ * Furthermore it allows us returning an error
+ * if something fails.
+ *
+ * @access private
+ * @return boolean|object true on success, PEAR_Error otherwise
+ *
+ * @see xml_parser_create
+ */
+ function _create()
+ {
+ if ($this->srcenc === null) {
+ $xp = @xml_parser_create();
+ } else {
+ $xp = @xml_parser_create($this->srcenc);
+ }
+ if (is_resource($xp)) {
+ if ($this->tgtenc !== null) {
+ if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
+ $this->tgtenc)) {
+ return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
+ }
+ }
+ $this->parser = $xp;
+ $result = $this->_initHandlers($this->mode);
+ if ($this->isError($result)) {
+ return $result;
+ }
+ xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
+ return true;
+ }
+ if (!in_array(strtoupper($this->srcenc), $this->_validEncodings)) {
+ return $this->raiseError('invalid source encoding', XML_PARSER_ERROR_INVALID_ENCODING);
+ }
+ return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
+ }
+
+ // }}}
+ // {{{ reset()
+
+ /**
+ * Reset the parser.
+ *
+ * This allows you to use one parser instance
+ * to parse multiple XML documents.
+ *
+ * @access public
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function reset()
+ {
+ $result = $this->_create();
+ if ($this->isError( $result )) {
+ return $result;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ setInputFile()
+
+ /**
+ * Sets the input xml file to be parsed
+ *
+ * @param string Filename (full path)
+ * @return resource fopen handle of the given file
+ * @throws XML_Parser_Error
+ * @see setInput(), setInputString(), parse()
+ * @access public
+ */
+ function setInputFile($file)
+ {
+ /**
+ * check, if file is a remote file
+ */
+ if (eregi('^(http|ftp)://', substr($file, 0, 10))) {
+ if (!ini_get('allow_url_fopen')) {
+ return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
+ }
+ }
+
+ $fp = @fopen($file, 'rb');
+ if (is_resource($fp)) {
+ $this->fp = $fp;
+ return $fp;
+ }
+ return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
+ }
+
+ // }}}
+ // {{{ setInputString()
+
+ /**
+ * XML_Parser::setInputString()
+ *
+ * Sets the xml input from a string
+ *
+ * @param string $data a string containing the XML document
+ * @return null
+ **/
+ function setInputString($data)
+ {
+ $this->fp = $data;
+ return null;
+ }
+
+ // }}}
+ // {{{ setInput()
+
+ /**
+ * Sets the file handle to use with parse().
+ *
+ * You should use setInputFile() or setInputString() if you
+ * pass a string
+ *
+ * @param mixed $fp Can be either a resource returned from fopen(),
+ * a URL, a local filename or a string.
+ * @access public
+ * @see parse()
+ * @uses setInputString(), setInputFile()
+ */
+ function setInput($fp)
+ {
+ if (is_resource($fp)) {
+ $this->fp = $fp;
+ return true;
+ }
+ // see if it's an absolute URL (has a scheme at the beginning)
+ elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
+ return $this->setInputFile($fp);
+ }
+ // see if it's a local file
+ elseif (file_exists($fp)) {
+ return $this->setInputFile($fp);
+ }
+ // it must be a string
+ else {
+ $this->fp = $fp;
+ return true;
+ }
+
+ return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
+ }
+
+ // }}}
+ // {{{ parse()
+
+ /**
+ * Central parsing function.
+ *
+ * @return true|object PEAR error returns true on success, or a PEAR_Error otherwise
+ * @access public
+ */
+ function parse()
+ {
+ /**
+ * reset the parser
+ */
+ $result = $this->reset();
+ if ($this->isError($result)) {
+ return $result;
+ }
+ // if $this->fp was fopened previously
+ if (is_resource($this->fp)) {
+
+ while ($data = fread($this->fp, 4096)) {
+ if (!$this->_parseString($data, feof($this->fp))) {
+ $error = &$this->raiseError();
+ $this->free();
+ return $error;
+ }
+ }
+ // otherwise, $this->fp must be a string
+ } else {
+ if (!$this->_parseString($this->fp, true)) {
+ $error = &$this->raiseError();
+ $this->free();
+ return $error;
+ }
+ }
+ $this->free();
+
+ return true;
+ }
+
+ /**
+ * XML_Parser::_parseString()
+ *
+ * @param string $data
+ * @param boolean $eof
+ * @return bool
+ * @access private
+ * @see parseString()
+ **/
+ function _parseString($data, $eof = false)
+ {
+ return xml_parse($this->parser, $data, $eof);
+ }
+
+ // }}}
+ // {{{ parseString()
+
+ /**
+ * XML_Parser::parseString()
+ *
+ * Parses a string.
+ *
+ * @param string $data XML data
+ * @param boolean $eof If set and TRUE, data is the last piece of data sent in this parser
+ * @throws XML_Parser_Error
+ * @return Pear Error|true true on success or a PEAR Error
+ * @see _parseString()
+ */
+ function parseString($data, $eof = false)
+ {
+ if (!isset($this->parser) || !is_resource($this->parser)) {
+ $this->reset();
+ }
+
+ if (!$this->_parseString($data, $eof)) {
+ $error = &$this->raiseError();
+ $this->free();
+ return $error;
+ }
+
+ if ($eof === true) {
+ $this->free();
+ }
+ return true;
+ }
+
+ /**
+ * XML_Parser::free()
+ *
+ * Free the internal resources associated with the parser
+ *
+ * @return null
+ **/
+ function free()
+ {
+ if (isset($this->parser) && is_resource($this->parser)) {
+ xml_parser_free($this->parser);
+ unset( $this->parser );
+ }
+ if (isset($this->fp) && is_resource($this->fp)) {
+ fclose($this->fp);
+ }
+ unset($this->fp);
+ return null;
+ }
+
+ /**
+ * XML_Parser::raiseError()
+ *
+ * Throws a XML_Parser_Error
+ *
+ * @param string $msg the error message
+ * @param integer $ecode the error message code
+ * @return XML_Parser_Error
+ **/
+ function raiseError($msg = null, $ecode = 0)
+ {
+ $msg = !is_null($msg) ? $msg : $this->parser;
+ $err = &new XML_Parser_Error($msg, $ecode);
+ return parent::raiseError($err);
+ }
+
+ // }}}
+ // {{{ funcStartHandler()
+
+ function funcStartHandler($xp, $elem, $attribs)
+ {
+ $func = 'xmltag_' . $elem;
+ $func = str_replace(array('.', '-', ':'), '_', $func);
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
+ } elseif (method_exists($this->_handlerObj, 'xmltag')) {
+ call_user_func(array(&$this->_handlerObj, 'xmltag'), $xp, $elem, $attribs);
+ }
+ }
+
+ // }}}
+ // {{{ funcEndHandler()
+
+ function funcEndHandler($xp, $elem)
+ {
+ $func = 'xmltag_' . $elem . '_';
+ $func = str_replace(array('.', '-', ':'), '_', $func);
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
+ } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
+ call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
+ }
+ }
+
+ // }}}
+ // {{{ startHandler()
+
+ /**
+ *
+ * @abstract
+ */
+ function startHandler($xp, $elem, &$attribs)
+ {
+ return NULL;
+ }
+
+ // }}}
+ // {{{ endHandler()
+
+ /**
+ *
+ * @abstract
+ */
+ function endHandler($xp, $elem)
+ {
+ return NULL;
+ }
+
+
+ // }}}me
+}
+
+/**
+ * error class, replaces PEAR_Error
+ *
+ * An instance of this class will be returned
+ * if an error occurs inside XML_Parser.
+ *
+ * There are three advantages over using the standard PEAR_Error:
+ * - All messages will be prefixed
+ * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
+ * - messages can be generated from the xml_parser resource
+ *
+ * @package XML_Parser
+ * @access public
+ * @see PEAR_Error
+ */
+class XML_Parser_Error extends PEAR_Error
+{
+ // {{{ properties
+
+ /**
+ * prefix for all messages
+ *
+ * @var string
+ */
+ var $error_message_prefix = 'XML_Parser: ';
+
+ // }}}
+ // {{{ constructor()
+ /**
+ * construct a new error instance
+ *
+ * You may either pass a message or an xml_parser resource as first
+ * parameter. If a resource has been passed, the last error that
+ * happened will be retrieved and returned.
+ *
+ * @access public
+ * @param string|resource message or parser resource
+ * @param integer error code
+ * @param integer error handling
+ * @param integer error level
+ */
+ function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
+ {
+ if (is_resource($msgorparser)) {
+ $code = xml_get_error_code($msgorparser);
+ $msgorparser = sprintf('%s at XML input line %d:%d',
+ xml_error_string($code),
+ xml_get_current_line_number($msgorparser),
+ xml_get_current_column_number($msgorparser));
+ }
+ $this->PEAR_Error($msgorparser, $code, $mode, $level);
+ }
+ // }}}
+}
+?>
\ No newline at end of file
diff --git a/vendor/spikephpcoverage/XML/Parser/Simple.php b/vendor/spikephpcoverage/XML/Parser/Simple.php
new file mode 100644
index 000000000..aa60ec95c
--- /dev/null
+++ b/vendor/spikephpcoverage/XML/Parser/Simple.php
@@ -0,0 +1,297 @@
+ |
+// +----------------------------------------------------------------------+
+//
+// $Id: Simple.php,v 1.6 2005/03/25 17:13:10 schst Exp $
+
+/**
+ * Simple XML parser class.
+ *
+ * This class is a simplified version of XML_Parser.
+ * In most XML applications the real action is executed,
+ * when a closing tag is found.
+ *
+ * XML_Parser_Simple allows you to just implement one callback
+ * for each tag that will receive the tag with its attributes
+ * and CData
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stephan Schmidt
+ */
+
+/**
+ * built on XML_Parser
+ */
+require_once 'XML/Parser.php';
+
+/**
+ * Simple XML parser class.
+ *
+ * This class is a simplified version of XML_Parser.
+ * In most XML applications the real action is executed,
+ * when a closing tag is found.
+ *
+ * XML_Parser_Simple allows you to just implement one callback
+ * for each tag that will receive the tag with its attributes
+ * and CData.
+ *
+ *
+ * require_once '../Parser/Simple.php';
+ *
+ * class myParser extends XML_Parser_Simple
+ * {
+ * function myParser()
+ * {
+ * $this->XML_Parser_Simple();
+ * }
+ *
+ * function handleElement($name, $attribs, $data)
+ * {
+ * printf('handle %s
', $name);
+ * }
+ * }
+ *
+ * $p = &new myParser();
+ *
+ * $result = $p->setInputFile('myDoc.xml');
+ * $result = $p->parse();
+ *
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author Stephan Schmidt
+ */
+class XML_Parser_Simple extends XML_Parser
+{
+ /**
+ * element stack
+ *
+ * @access private
+ * @var array
+ */
+ var $_elStack = array();
+
+ /**
+ * all character data
+ *
+ * @access private
+ * @var array
+ */
+ var $_data = array();
+
+ /**
+ * element depth
+ *
+ * @access private
+ * @var integer
+ */
+ var $_depth = 0;
+
+ /**
+ * Mapping from expat handler function to class method.
+ *
+ * @var array
+ */
+ var $handler = array(
+ 'default_handler' => 'defaultHandler',
+ 'processing_instruction_handler' => 'piHandler',
+ 'unparsed_entity_decl_handler' => 'unparsedHandler',
+ 'notation_decl_handler' => 'notationHandler',
+ 'external_entity_ref_handler' => 'entityrefHandler'
+ );
+
+ /**
+ * Creates an XML parser.
+ *
+ * This is needed for PHP4 compatibility, it will
+ * call the constructor, when a new instance is created.
+ *
+ * @param string $srcenc source charset encoding, use NULL (default) to use
+ * whatever the document specifies
+ * @param string $mode how this parser object should work, "event" for
+ * handleElement(), "func" to have it call functions
+ * named after elements (handleElement_$name())
+ * @param string $tgenc a valid target encoding
+ */
+ function XML_Parser_Simple($srcenc = null, $mode = 'event', $tgtenc = null)
+ {
+ $this->XML_Parser($srcenc, $mode, $tgtenc);
+ }
+
+ /**
+ * inits the handlers
+ *
+ * @access private
+ */
+ function _initHandlers()
+ {
+ if (!is_object($this->_handlerObj)) {
+ $this->_handlerObj = &$this;
+ }
+
+ if ($this->mode != 'func' && $this->mode != 'event') {
+ return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+ }
+ xml_set_object($this->parser, $this->_handlerObj);
+
+ xml_set_element_handler($this->parser, array(&$this, 'startHandler'), array(&$this, 'endHandler'));
+ xml_set_character_data_handler($this->parser, array(&$this, 'cdataHandler'));
+
+ /**
+ * set additional handlers for character data, entities, etc.
+ */
+ foreach ($this->handler as $xml_func => $method) {
+ if (method_exists($this->_handlerObj, $method)) {
+ $xml_func = 'xml_set_' . $xml_func;
+ $xml_func($this->parser, $method);
+ }
+ }
+ }
+
+ /**
+ * Reset the parser.
+ *
+ * This allows you to use one parser instance
+ * to parse multiple XML documents.
+ *
+ * @access public
+ * @return boolean|object true on success, PEAR_Error otherwise
+ */
+ function reset()
+ {
+ $this->_elStack = array();
+ $this->_data = array();
+ $this->_depth = 0;
+
+ $result = $this->_create();
+ if ($this->isError( $result )) {
+ return $result;
+ }
+ return true;
+ }
+
+ /**
+ * start handler
+ *
+ * Pushes attributes and tagname onto a stack
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string element name
+ * @param array attributes
+ */
+ function startHandler($xp, $elem, &$attribs)
+ {
+ array_push($this->_elStack, array(
+ 'name' => $elem,
+ 'attribs' => $attribs
+ )
+ );
+ $this->_depth++;
+ $this->_data[$this->_depth] = '';
+ }
+
+ /**
+ * end handler
+ *
+ * Pulls attributes and tagname from a stack
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string element name
+ */
+ function endHandler($xp, $elem)
+ {
+ $el = array_pop($this->_elStack);
+ $data = $this->_data[$this->_depth];
+ $this->_depth--;
+
+ switch ($this->mode) {
+ case 'event':
+ $this->_handlerObj->handleElement($el['name'], $el['attribs'], $data);
+ break;
+ case 'func':
+ $func = 'handleElement_' . $elem;
+ if (strchr($func, '.')) {
+ $func = str_replace('.', '_', $func);
+ }
+ if (method_exists($this->_handlerObj, $func)) {
+ call_user_func(array(&$this->_handlerObj, $func), $el['name'], $el['attribs'], $data);
+ }
+ break;
+ }
+ }
+
+ /**
+ * handle character data
+ *
+ * @access private
+ * @final
+ * @param resource xml parser resource
+ * @param string data
+ */
+ function cdataHandler($xp, $data)
+ {
+ $this->_data[$this->_depth] .= $data;
+ }
+
+ /**
+ * handle a tag
+ *
+ * Implement this in your parser
+ *
+ * @access public
+ * @abstract
+ * @param string element name
+ * @param array attributes
+ * @param string character data
+ */
+ function handleElement($name, $attribs, $data)
+ {
+ }
+
+ /**
+ * get the current tag depth
+ *
+ * The root tag is in depth 0.
+ *
+ * @access public
+ * @return integer
+ */
+ function getCurrentDepth()
+ {
+ return $this->_depth;
+ }
+
+ /**
+ * add some string to the current ddata.
+ *
+ * This is commonly needed, when a document is parsed recursively.
+ *
+ * @access public
+ * @param string data to add
+ * @return void
+ */
+ function addToData( $data )
+ {
+ $this->_data[$this->_depth] .= $data;
+ }
+}
+?>
diff --git a/vendor/spikephpcoverage/cli/driver.php b/vendor/spikephpcoverage/cli/driver.php
new file mode 100644
index 000000000..2339c4b25
--- /dev/null
+++ b/vendor/spikephpcoverage/cli/driver.php
@@ -0,0 +1,267 @@
+
+
+ */
+
+ if(!defined("__PHPCOVERAGE_HOME")) {
+ define("__PHPCOVERAGE_HOME", dirname(dirname(__FILE__)));
+ }
+ require_once __PHPCOVERAGE_HOME . "/conf/phpcoverage.conf.php";
+ require_once __PHPCOVERAGE_HOME . "/util/Utility.php";
+
+ // ######################################################################
+ // ######################################################################
+
+ function usage() {
+ global $util;
+ echo "Usage: " . $_SERVER['argv'][0] . " \n";
+ echo "\n";
+ echo " Options: \n";
+ echo " --phpcoverage-home OR -p Path to PHPCoverage home (defaults to PHPCOVERAGE_HOME environment property)\n";
+ echo " --init Initialize PHPCoverage Reporting\n";
+ echo " --report Generate PHPCoverage Reports\n";
+ echo " --cleanup Remove existing PHPCoverage data\n";
+ echo " init options \n";
+ echo " --cov-url Specify application default url\n";
+ echo " --tmp-dir Specify tmp directory location (Defaults to '" . $util->getTmpDir() . "')\n";
+ echo " --cov-file-name Specify coverage data file name (Defaults to 'phpcoverage.data.xml')\n";
+ echo " report options \n";
+ echo " --cov-data-files Coverage data file path [use this instead of --cov-url for a local file path]\n";
+ echo " --report-name Report name\n";
+ echo " --report-dir Report directory path (Defaults to 'report')\n";
+ echo " --appbase-path Application base path (Defaults to PHPCOVERAGE_APPBASE_PATH if specified on the command line)\n";
+ echo " --include-paths Comma-separated paths to include in code coverage report. (Includes appbase-path by default)\n";
+ echo " --exclude-paths Comma-separated paths to exclude from code coverage report.\n";
+ echo " --print-summary Print coverage report summary to console.\n";
+ echo " other options \n";
+ echo " --verbose OR -v Print verbose information\n";
+ echo " --help OR -h Display this usage information\n";
+ exit;
+ }
+
+ //
+ // Setup command line argument processing
+ //
+
+ $OPTION["p"] = false;
+ $OPTION['verbose'] = false;
+ $OPTION['init'] = false;
+ $OPTION['report'] = false;
+ $OPTION['cleanup'] = false;
+ $OPTION['cov-url'] = false;
+ $OPTION['report-name'] = false;
+ $OPTION['report-dir'] = false;
+ $OPTION['tmp-dir'] = false;
+ $OPTION['cov-file-name'] = false;
+ $OPTION['cov-data-files'] = false;
+ $OPTION['appbase-path'] = false;
+
+ //
+ // loop through our arguments and see what the user selected
+ //
+
+ for ($i = 1; $i < $_SERVER["argc"]; $i++) {
+ switch($_SERVER["argv"][$i]) {
+ case "--phpcoverage-home":
+ case "-p":
+ $OPTION['p'] = $_SERVER['argv'][++$i];
+ break;
+ case "-v":
+ case "--verbose":
+ $OPTION['verbose'] = true;
+ break;
+ case "--init":
+ $OPTION['init'] = true;
+ break;
+ case "--report":
+ $OPTION['report'] = true;
+ break;
+ case "--cleanup":
+ $OPTION['cleanup'] = true;
+ break;
+ case "--cov-url":
+ $OPTION['cov-url'] = $_SERVER['argv'][++$i] . "/" . "phpcoverage.remote.top.inc.php";
+ break;
+ case "--tmp-dir":
+ $OPTION['tmp-dir'] = $_SERVER['argv'][++$i];
+ break;
+ case "--cov-file-name":
+ $OPTION['cov-file-name'] = $_SERVER['argv'][++$i];
+ break;
+ case "--cov-data-files":
+ $OPTION['cov-data-files'] = $_SERVER['argv'][++$i];
+ break;
+ case "--report-name":
+ $OPTION['report-name'] = $_SERVER['argv'][++$i];
+ break;
+ case "--report-dir":
+ $OPTION['report-dir'] = $_SERVER['argv'][++$i];
+ break;
+ case "--appbase-path":
+ $OPTION['appbase-path'] = $_SERVER['argv'][++$i];
+ break;
+ case "--include-paths":
+ $OPTION['include-paths'] = $_SERVER['argv'][++$i];
+ break;
+ case "--exclude-paths":
+ $OPTION['exclude-paths'] = $_SERVER['argv'][++$i];
+ break;
+ case "--print-summary":
+ $OPTION['print-summary'] = true;
+ break;
+ case "--help":
+ case "-h":
+ usage();
+ break;
+ }
+ }
+
+ if($OPTION['p'] == false) {
+ $OPTION['p'] = __PHPCOVERAGE_HOME;
+ if(empty($OPTION['p']) || !is_dir($OPTION['p'])) {
+ die("PHPCOVERAGE_HOME does not exist. [" . $OPTION['p'] . "]");
+ }
+ }
+
+ putenv("PHPCOVERAGE_HOME=" . $OPTION['p']);
+
+ require_once $OPTION['p'] . "/phpcoverage.inc.php";
+ require_once PHPCOVERAGE_HOME . "/remote/RemoteCoverageRecorder.php";
+ require_once PHPCOVERAGE_HOME . "/reporter/HtmlCoverageReporter.php";
+
+ // Initializations
+ $includePaths = array();
+ $excludePaths = array();
+
+ if (!$OPTION['cov-url']){
+ if(!$OPTION['report'] && !$OPTION['cov-data-files']) {
+ echo "ERROR: No --cov-url option specified.\n";
+ exit(1);
+ }
+ }
+
+ if($OPTION['init']) {
+ if(!$OPTION['tmp-dir']) {
+ $OPTION['tmp-dir'] = $util->getTmpDir();
+ }
+ if(!$OPTION['cov-file-name']) {
+ $OPTION['cov-file-name'] = "phpcoverage.data.xml";
+ }
+ }
+
+ if($OPTION['report']) {
+ if (!$OPTION['report-name']){
+ echo "ERROR: No --report-name option specified.\n";
+ exit(1);
+ }
+
+ if(!$OPTION['report-dir']) {
+ if(!empty($PHPCOVERAGE_REPORT_DIR)) {
+ $OPTION["report-dir"] = $PHPCOVERAGE_REPORT_DIR;
+ }
+ else {
+ $OPTION["report-dir"] = "report";
+ }
+ }
+
+ if(empty($OPTION['appbase-path']) && !empty($PHPCOVERAGE_APPBASE_PATH)) {
+ $OPTION['appbase-path'] = realpath($PHPCOVERAGE_APPBASE_PATH);
+ }
+
+ if(isset($OPTION['include-paths'])) {
+ $includePaths = explode(",", $OPTION['include-paths']);
+ }
+ if(isset($OPTION['appbase-path']) && !empty($OPTION["appbase-path"])) {
+ $includePaths[] = $OPTION['appbase-path'];
+ }
+
+ if(isset($OPTION['exclude-paths'])) {
+ $excludePaths = explode(",", $OPTION['exclude-paths']);
+ }
+ }
+
+ if ($OPTION['verbose']){
+ echo "Options: " . print_r($OPTION, true) . "\n";
+ echo "include-paths: " . print_r($includePaths, true) . "\n";
+ echo "exclude-paths: " . print_r($excludePaths, true) . "\n";
+ }
+
+ //
+ //
+ //
+
+ if ($OPTION['init']){
+ echo "PHPCoverage: init " . $OPTION['cov-url'] . "?phpcoverage-action=init&cov-file-name=". urlencode($OPTION["cov-file-name"]) . "&tmp-dir=" . urlencode($OPTION['tmp-dir']) . "\n";
+
+ //
+ // Initialize the PHPCoverage reporting framework
+ //
+
+ file_get_contents($OPTION['cov-url'] . "?phpcoverage-action=init&cov-file-name=". urlencode($OPTION["cov-file-name"]) . "&tmp-dir=" . urlencode($OPTION['tmp-dir']));
+
+ }
+ else if ($OPTION['report']){
+
+
+ //
+ // Retrieve coverage data (xml) from the PHPCoverage reporting framework
+ //
+
+ if($OPTION['cov-data-files']) {
+ $OPTION['cov-data-fileset'] = explode(",", $OPTION['cov-data-files']);
+ foreach($OPTION['cov-data-fileset'] as $covDataFile) {
+ if(!is_readable($covDataFile)) {
+ echo "Error: Cannot read cov-data-file: " . $covDataFile . "\n";
+ exit(1);
+ }
+ $xmlUrl[] = $covDataFile;
+ }
+ }
+ else {
+ echo "PHPCoverage: report " . $OPTION['cov-url'] . "?phpcoverage-action=get-coverage-xml" . "\n";
+ $xmlUrl = $OPTION['cov-url'] . "?phpcoverage-action=get-coverage-xml";
+ }
+
+ //
+ // Configure reporter, and generate the PHPCoverage report
+ //
+
+ $covReporter = new HtmlCoverageReporter($OPTION['report-name'], "", $OPTION["report-dir"]);
+
+ //
+ // Notice the coverage recorder is of type RemoteCoverageRecorder
+ //
+
+ $cov = new RemoteCoverageRecorder($includePaths, $excludePaths, $covReporter);
+ $cov->generateReport($xmlUrl, true);
+ $covReporter->printTextSummary($OPTION["report-dir"] . "/report.txt");
+ // Should the summary be printed to console ?
+ if(isset($OPTION['print-summary']) && $OPTION['print-summary']) {
+ $covReporter->printTextSummary();
+ }
+
+ }
+ else if ($OPTION['cleanup']){
+
+ echo "PHPCoverage: cleanup " . $OPTION['cov-url'] . "?phpcoverage-action=cleanup";
+ file_get_contents($OPTION['cov-url'] . "?phpcoverage-action=cleanup");
+
+ }
+
+?>
+
diff --git a/vendor/spikephpcoverage/cli/instrument.php b/vendor/spikephpcoverage/cli/instrument.php
new file mode 100644
index 000000000..da3b6693e
--- /dev/null
+++ b/vendor/spikephpcoverage/cli/instrument.php
@@ -0,0 +1,305 @@
+
+ [-p ] [-r] [-u] [-e ]"
+ . "[-v] [-h] [path1 [path2 [...]]]\n";
+ echo "\n";
+ echo " Options: \n";
+ echo " -b Application directory accessible via HTTP "
+ . "where PHPCoverage files should be copied.\n";
+ echo " -p Path to PHPCoverage Home.\n";
+ echo " -r Recursively instrument PHP files.\n";
+ echo " -u Undo instrumentation.\n";
+ echo " -e Execlude files in the file list.\n";
+ echo " -v Be verbose.\n";
+ echo " -h Print this help and exit.\n";
+ echo "\n";
+ exit(0);
+ }
+
+ /**
+ * Print error message and exit
+ *
+ * @param $msg Message to write to console.
+ * @access public
+ */
+ function error($msg) {
+ echo basename(__FILE__) . ": [ERROR] " . $msg . "\n";
+ exit(1);
+ }
+
+ /**
+ * Write a information message
+ *
+ * @param $msg Message to write to console.
+ * @access public
+ */
+ function writeMsg($msg) {
+ global $VERBOSE;
+ if($VERBOSE) {
+ echo basename(__FILE__) . ": [INFO] " . $msg . "\n";
+ }
+ }
+
+ /**
+ * Instrument the PHP file.
+ *
+ * @param $file File path
+ * @access public
+ */
+ function instrument($file) {
+ global $LOCAL_PHPCOVERAGE_LOCATION, $top, $bottom;
+ $tmpfile = "$file.tmp";
+ $contents = file_get_contents($file);
+ $len = strlen($contents);
+ if(strpos($contents, $top) === 0 && strrpos($contents, $bottom) === ($len - strlen($bottom))) {
+ writeMsg("Skipping $file.");
+ return;
+ }
+
+ $fp = fopen($tmpfile, "w");
+ if(!$fp) {
+ error("Cannot write to file: $tmpfile");
+ }
+ fputs($fp, $top);
+ fwrite($fp, $contents);
+ fputs($fp, $bottom);
+ fclose($fp);
+ // Delete if already exists - 'rename()' on Windows will return false otherwise
+ if(file_exists($file)) {
+ unlink($file);
+ }
+ $ret = rename($tmpfile, $file);
+ if(!$ret) {
+ error("Cannot save file: $file");
+ }
+ writeMsg("Instrumented: $file.");
+ }
+
+ /**
+ * Uninstrument the PHP file
+ *
+ * @param $file File path
+ * @access public
+ */
+ function uninstrument($file) {
+ global $LOCAL_PHPCOVERAGE_LOCATION, $top, $bottom;
+ $tmpfile = "$file.tmp";
+
+ $contents = file_get_contents($file);
+ $len = strlen($contents);
+ if(strpos($contents, $top) !== 0 && strrpos($contents, $bottom) !== ($len - strlen($bottom))) {
+ writeMsg("Skipping $file.");
+ return;
+ }
+
+ $fr = fopen($file, "r");
+ $fw = fopen($tmpfile, "w");
+ if(!$fr) {
+ error("Cannot read file: $file");
+ }
+ if(!$fr) {
+ error("Cannot write to file: $tmpfile");
+ }
+ while(!feof($fr)) {
+ $line = fgets($fr);
+ if(strpos($line, $top) === false && strpos($line, $bottom) === false) {
+ fputs($fw, $line);
+ }
+ }
+ fclose($fr);
+ fclose($fw);
+
+ // Delete if already exists - 'rename()' on Windows will return false otherwise
+ if(file_exists($file)) {
+ unlink($file);
+ }
+ $ret = rename($tmpfile, $file);
+ if(!$ret) {
+ error("Cannot save file: $file");
+ }
+ writeMsg("Uninstrumented: $file");
+ }
+
+ /**
+ * Retrive a list of all PHP files in the given directory
+ *
+ * @param $dir Directory to scan
+ * @param $recursive True is directory is scanned recursively
+ * @return Array List of PHP files
+ * @access public
+ */
+ function get_all_php_files($dir, &$excludeFiles, $recursive) {
+ global $spc_config;
+ $phpExtensions = $spc_config["extensions"];
+ $dirs[] = $dir;
+ while(count($dirs) > 0) {
+ $currDir = realpath(array_pop($dirs));
+ if(!is_readable($currDir)) {
+ continue;
+ }
+ $currFiles = scandir($currDir);
+ for($j = 0; $j < count($currFiles); $j++) {
+ if($currFiles[$j] == "." || $currFiles[$j] == "..") {
+ continue;
+ }
+ $currFiles[$j] = $currDir . "/" . $currFiles[$j];
+ if(is_file($currFiles[$j])) {
+ $pathParts = pathinfo($currFiles[$j]);
+ // Ignore phpcoverage bottom and top stubs
+ if(strpos($pathParts['basename'], "phpcoverage.remote.") !== false) {
+ continue;
+ }
+ // Ignore files specified in the exclude list
+ if(in_array(realpath($currFiles[$j]), $excludeFiles) !== false) {
+ continue;
+ }
+ if(isset($pathParts['extension'])
+ && in_array($pathParts['extension'], $phpExtensions)) {
+ $files[] = $currFiles[$j];
+ }
+ }
+ else if(is_dir($currFiles[$j]) && $recursive) {
+ $dirs[] = $currFiles[$j];
+ }
+ }
+ }
+ return $files;
+ }
+
+ // Initialize
+
+ $RECURSIVE = false;
+ $UNDO = false;
+
+ $top_file = "/phpcoverage.remote.top.inc.php";
+ $bottom_file = "/phpcoverage.remote.bottom.inc.php";
+
+ //print_r($argv);
+ for($i = 1; $i < $argc; $i++) {
+ switch($argv[$i]) {
+ case "-r":
+ $RECURSIVE = true;
+ break;
+
+ case "-p":
+ $PHPCOVERAGE_HOME = $argv[++$i];
+ break;
+
+ case "-b":
+ $LOCAL_PHPCOVERAGE_LOCATION = $argv[++$i];
+ break;
+
+ case "-u":
+ $UNDO = true;
+ break;
+
+ case "-e":
+ $EXCLUDE_FILES = explode(",", $argv[++$i]);
+ break;
+
+ case "-v":
+ $VERBOSE = true;
+ break;
+
+ case "-h":
+ help();
+ break;
+
+ default:
+ $paths[] = $argv[$i];
+ break;
+ }
+ }
+
+
+ if(!is_dir($LOCAL_PHPCOVERAGE_LOCATION)) {
+ error("LOCAL_PHPCOVERAGE_LOCATION [$LOCAL_PHPCOVERAGE_LOCATION] not found.");
+ }
+ if(empty($PHPCOVERAGE_HOME) || !is_dir($PHPCOVERAGE_HOME)) {
+ $PHPCOVERAGE_HOME = __PHPCOVERAGE_HOME;
+ if(empty($PHPCOVERAGE_HOME) || !is_dir($PHPCOVERAGE_HOME)) {
+ error("PHPCOVERAGE_HOME does not exist. [" . $PHPCOVERAGE_HOME . "]");
+ }
+ }
+
+ $LOCAL_PHPCOVERAGE_LOCATION = realpath($LOCAL_PHPCOVERAGE_LOCATION);
+ if(file_exists($LOCAL_PHPCOVERAGE_LOCATION . $top_file)) {
+ unlink($LOCAL_PHPCOVERAGE_LOCATION . $top_file);
+ }
+ $ret = copy($PHPCOVERAGE_HOME . $top_file, $LOCAL_PHPCOVERAGE_LOCATION . $top_file);
+ if(!$ret) {
+ error("Cannot copy to $LOCAL_PHPCOVERAGE_LOCATION");
+ }
+ if(file_exists($LOCAL_PHPCOVERAGE_LOCATION . $bottom_file)) {
+ unlink($LOCAL_PHPCOVERAGE_LOCATION . $bottom_file);
+ }
+ $ret = copy($PHPCOVERAGE_HOME . $bottom_file, $LOCAL_PHPCOVERAGE_LOCATION . $bottom_file);
+ if(!$ret) {
+ error("Cannot copy to $LOCAL_PHPCOVERAGE_LOCATION");
+ }
+ $top="\n";
+ $bottom="\n";
+
+ if(empty($paths)) {
+ $paths[] = getcwd();
+ }
+ if(!isset($EXCLUDE_FILES) || empty($EXCLUDE_FILES)) {
+ $EXCLUDE_FILES = array();
+ }
+ for($i = 0; $i < count($EXCLUDE_FILES); $i++) {
+ // Remove a file from the array if it does not exist
+ if(!file_exists($EXCLUDE_FILES[$i])) {
+ array_splice($EXCLUDE_FILES, $i, 1);
+ $i --;
+ continue;
+ }
+ $EXCLUDE_FILES[$i] = realpath($EXCLUDE_FILES[$i]);
+ }
+
+ //print_r($paths);
+ foreach($paths as $path) {
+ unset($files);
+ if(is_dir($path)) {
+ $files = get_all_php_files($path, $EXCLUDE_FILES, $RECURSIVE);
+ }
+ else if(is_file($path)) {
+ $files[] = $path;
+ }
+ else {
+ error("Unknown entity: $path");
+ }
+ //print_r($files);
+ foreach($files as $file) {
+ if($UNDO) {
+ uninstrument($file);
+ }
+ else {
+ instrument($file);
+ }
+ }
+ }
+?>
diff --git a/vendor/spikephpcoverage/conf/phpcoverage.conf.php b/vendor/spikephpcoverage/conf/phpcoverage.conf.php
new file mode 100644
index 000000000..e822109ac
--- /dev/null
+++ b/vendor/spikephpcoverage/conf/phpcoverage.conf.php
@@ -0,0 +1,26 @@
+
+
diff --git a/vendor/spikephpcoverage/parser/BasicXmlParser.php b/vendor/spikephpcoverage/parser/BasicXmlParser.php
new file mode 100644
index 000000000..1e546c931
--- /dev/null
+++ b/vendor/spikephpcoverage/parser/BasicXmlParser.php
@@ -0,0 +1,171 @@
+
+
+ * @version $Revision: $
+ * @package SpikePHPCoverage_Parser
+ */
+ class BasicXmlParser extends XML_Parser {
+ /*{{{ Members */
+
+ protected $openTags;
+ protected $docroot;
+
+ /*}}}*/
+ /*{{{ Constructor*/
+
+ /**
+ * Constructor
+ * @access public
+ */
+ public function BasicXmlParser() {
+ parent::XML_Parser();
+ }
+
+ /*}}}*/
+ /*{{{ public function handleAttrTag() */
+
+ /**
+ * Function that handles an element with attributes.
+ *
+ * @param $name Name of the element
+ * @param $attrs Attributes array (name, value pairs)
+ * @return Array An element
+ * @access public
+ */
+ public function handleAttrTag($name, $attrs) {
+ $tag = array();
+ foreach($attrs as $attr_name => $value) {
+ $tag[$attr_name] = $value;
+ }
+ return $tag;
+ }
+
+ /*}}}*/
+ /*{{{ public function startHandler() */
+
+ /**
+ * Function to handle start of an element
+ *
+ * @param $xp XMLParser handle
+ * @param $name Element name
+ * @param $attributes Attribute array
+ * @access public
+ */
+ function startHandler($xp, $name, $attributes) {
+ $this->openTags[] = $name;
+ }
+
+ /*}}}*/
+ /*{{{ public function endHandler()*/
+
+ /**
+ * Function to handle end of an element
+ *
+ * @param $xp XML_Parser handle
+ * @param $name Name of the element
+ * @access public
+ */
+ public function endHandler($xp, $name) {
+ // Handle error tags
+ $lastTag = $this->getLastOpenTag($name);
+ switch($name) {
+ case "MESSAGE":
+ if($lastTag == "ERROR") {
+ $this->docroot["ERROR"]["MESSAGE"] = $this->getCData();
+ }
+ break;
+ }
+ // Empty CData
+ $this->lastCData = "";
+
+ // Close tag
+ if($this->openTags[count($this->openTags)-1] == $name) {
+ array_pop($this->openTags);
+ }
+ }
+
+ /*}}}*/
+ /*{{{ public function cdataHandler() */
+
+ /**
+ * Function to handle character data
+ *
+ * @param $xp XMLParser handle
+ * @param $cdata Character data
+ * @access public
+ */
+ public function cdataHandler($xp, $cdata) {
+ $this->lastCData .= $cdata;
+ }
+
+ /*}}}*/
+ /*{{{ public function getCData() */
+
+ /**
+ * Returns the CData collected so far.
+ *
+ * @return String Character data collected.
+ * @access public
+ */
+ public function getCData() {
+ return $this->lastCData;
+ }
+
+ /*}}}*/
+ /*{{{ public function getLastOpenTag() */
+
+ /**
+ * Returns the name of parent tag of give tag
+ *
+ * @param $tag Name of a child tag
+ * @return String Name of the parent tag of $tag
+ * @access public
+ */
+ public function getLastOpenTag($tag) {
+ $lastTagIndex = count($this->openTags)-1;
+ if($this->openTags[$lastTagIndex] == $tag) {
+ if($lastTagIndex > 0) {
+ return $this->openTags[$lastTagIndex-1];
+ }
+ }
+ return false;
+ }
+
+ /*}}}*/
+ /*{{{ public function getDocumentArray() */
+
+ /**
+ * Return the document array gathered during parsing.
+ * Document array is a data structure that mimics the XML
+ * contents.
+ *
+ * @return Array Document array
+ * @access public
+ */
+ public function getDocumentArray() {
+ return $this->docroot;
+ }
+
+ /*}}}*/
+ }
+
+?>
diff --git a/vendor/spikephpcoverage/parser/CoverageXmlParser.php b/vendor/spikephpcoverage/parser/CoverageXmlParser.php
new file mode 100644
index 000000000..412dd3e8d
--- /dev/null
+++ b/vendor/spikephpcoverage/parser/CoverageXmlParser.php
@@ -0,0 +1,92 @@
+
+
+ *
+ *
+ *
+ *
+ *
+ * ...
+ *
+ *
+ *
+ * @author Nimish Pachapurkar
+ * @version $Revision: $
+ * @package SpikePHPCoverage_Parser
+ */
+ class CoverageXmlParser extends BasicXmlParser {
+ /*{{{ Members */
+
+ protected $_data = array();
+ protected $_lastFilePath;
+
+ /*}}}*/
+ /*{{{ public function startHandler() */
+
+ public function startHandler($xp, $name, $attrs) {
+ switch($name) {
+ case "FILE":
+ $fileAttributes = $this->handleAttrTag($name, $attrs);
+ $this->_lastFilePath = $fileAttributes["PATH"];
+ if(!isset($this->_data[$this->_lastFilePath])) {
+ $this->_data[$this->_lastFilePath] = array();
+ }
+ break;
+
+ case "LINE":
+ $lineAttributes = $this->handleAttrTag($name, $attrs);
+ $lineNumber = (int)$lineAttributes["LINE-NUMBER"];
+ if(!isset($this->_data[$this->_lastFilePath][$lineNumber])) {
+ $this->_data[$this->_lastFilePath][$lineNumber] = (int)$lineAttributes["FREQUENCY"];
+ }
+ else {
+ $this->_data[$this->_lastFilePath][$lineNumber] += (int)$lineAttributes["FREQUENCY"];
+ }
+ break;
+ }
+ }
+
+ /*}}}*/
+ /*{{{ public function getCoverageData() */
+
+ /**
+ * Returns coverage data array from the XML
+ * Format:
+ * Array
+ * (
+ * [/php/src/remote/RemoteCoverageRecorder.php] => Array
+ * (
+ * [220] => 1
+ * [221] => 1
+ * )
+ *
+ * [/opt/oss/share/apache2/htdocs/web/sample.php] => Array
+ * (
+ * [16] => 1
+ * [18] => 1
+ * )
+ * )
+ *
+ * @access public
+ */
+ public function getCoverageData() {
+ return $this->_data;
+ }
+
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/parser/PHPParser.php b/vendor/spikephpcoverage/parser/PHPParser.php
new file mode 100644
index 000000000..35316c44f
--- /dev/null
+++ b/vendor/spikephpcoverage/parser/PHPParser.php
@@ -0,0 +1,425 @@
+
+';
+ private $inComment = false;
+ private $lastLineEndTokenType = "";
+ // If one of these tokens occur as the last token of a line
+ // then the next line can be treated as a continuation line
+ // depending on how it starts.
+ public static $contTypes = array(
+ "(",
+ ",",
+ ".",
+ "=",
+ T_LOGICAL_XOR,
+ T_LOGICAL_AND,
+ T_LOGICAL_OR,
+ T_PLUS_EQUAL,
+ T_MINUS_EQUAL,
+ T_MUL_EQUAL,
+ T_DIV_EQUAL,
+ T_CONCAT_EQUAL,
+ T_MOD_EQUAL,
+ T_AND_EQUAL,
+ T_OR_EQUAL,
+ T_XOR_EQUAL,
+ T_BOOLEAN_AND,
+ T_BOOLEAN_OR,
+ T_OBJECT_OPERATOR,
+ T_DOUBLE_ARROW,
+ "[",
+ "]",
+ T_LOGICAL_OR,
+ T_LOGICAL_XOR,
+ T_LOGICAL_AND
+ );
+
+ /*}}}*/
+ /*{{{ protected function processLine() */
+
+ /**
+ * Process a line read from the file and determine if it is an
+ * executable line or not.
+ *
+ * This is the work horse function that does most of the parsing.
+ * To parse PHP, get_all_tokens() tokenizer function is used.
+ *
+ * @param $line Line to be parsed.
+ * @access protected
+ */
+ protected function processLine($line) {
+
+ // Default values
+ $this->lineType = LINE_TYPE_NOEXEC;
+ $line = trim($line);
+ $parseLine = $line;
+ $artificialStart = false;
+ $artificialEnd = false;
+
+ // If we are not inside PHP opening tag
+ if(!$this->inPHP) {
+ $pos = -1;
+
+ // Confirm that the line does not have T_OPEN_TAG_WITH_ECHO (< ? =)
+ if(strpos($line, $this->phpStarters[2]) === false) {
+ // If the line has PHP start tag of the first kind
+ if(($pos = strpos($line, $this->phpStarters[0])) !== false) {
+ $pos = $pos + strlen($this->phpStarters[0]);
+ }
+ // if the line has PHP start tag of the second kind.
+ else if(($pos = strpos($line, $this->phpStarters[1])) !== false) {
+ $pos = $pos + strlen($this->phpStarters[1]);
+ }
+ // $pos now points to the character after opening tag
+ if($pos > 0) {
+ $this->inPHP = true;
+ //echo "Going in PHP\n";
+ // Remove the part of the line till the PHP opening
+ // tag and recurse
+ return $this->processLine(trim(substr($line, $pos)));
+ }
+ }
+ }
+ // If we are already in PHP
+ else if($this->inPHP) {
+ // If we are inside a multi-line comment, that is not ending
+ // on the same line
+ if((strpos($line, "/*") !== false &&
+ strpos($line, "*/") === false) ||
+ (strpos($line, "/*") > strpos($line, "*/"))) {
+ $this->inComment = true;
+ }
+ if($this->inComment) {
+ // Do we need to append an artificial comment start?
+ // (otherwise the tokenizer might throw error.
+ if(strpos($line, "/*") === false) {
+ $line = "/*" . $line;
+ $artificialStart = true;
+ }
+ // Do we need to append an artificial comment end?
+ if(strpos($line, "*/") === false) {
+ $line = $line . "*/";
+ $artificialEnd = true;
+ }
+ }
+ // Since we are inside php, append php opening and closing tags
+ // to prevent tokenizer from mis-interpreting the line
+ $parseLine = "";
+ }
+
+ // Tokenize
+ $tokens = @token_get_all($parseLine);
+ $this->logger->debug("inPHP? " . $this->inPHP . "\nLine:" . $parseLine,
+ __FILE__, __LINE__);
+ $this->logger->debug(print_r($tokens, true), __FILE__, __LINE__);
+ $seenEnough = false;
+ $seeMore = false;
+ $tokenCnt = 0; //tokens in this line
+ $phpEnded = false;
+ if($this->isContinuation($this->lastLineEndTokenType)) {
+ $this->lineType = LINE_TYPE_CONT;
+ $this->logger->debug("Continuation !", __FILE__, __LINE__);
+ }
+ foreach($tokens as $token) {
+ $tokenCnt ++;
+ if($this->inPHP) {
+ if($tokenCnt == 2) {
+ if($this->isContinuation($token)) {
+ $this->lineType = LINE_TYPE_CONT;
+ $this->logger->debug("Continuation! Token: $token",
+ __FILE__, __LINE__);
+ break;
+ }
+ }
+ }
+
+ if(is_string($token)) {
+ // FIXME: Add more cases, if needed
+ switch($token) {
+ // Any of these things, are non-executable.
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ case ';':
+ if($this->lineType != LINE_TYPE_EXEC) {
+ $this->lineType = LINE_TYPE_NOEXEC;
+ }
+ break;
+
+ // Everything else by default is executable.
+ default:
+ $this->lineType = LINE_TYPE_EXEC;
+ break;
+ }
+ $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken: $token",
+ __FILE__, __LINE__);
+ }
+ else {
+ // The token is an array
+ list($tokenType, $text) = $token;
+ switch($tokenType) {
+
+ // If it is a comment end or start, set the correct flag
+ // If we have put the start or end artificially, ignore!
+ case T_COMMENT:
+ case T_DOC_COMMENT:
+ if(strpos($text, "/*") !== false && !$artificialStart) {
+ $this->inComment = true;
+ }
+ if(strpos($text, "*/") !== false && !$artificialEnd) {
+ $this->inComment = false;
+ }
+
+ case T_WHITESPACE: // white space
+ case T_OPEN_TAG: // < ?
+ case T_OPEN_TAG_WITH_ECHO: // < ? =
+ case T_CURLY_OPEN: //
+ case T_INLINE_HTML: //
jhsk
+ //case T_STRING: //
+ case T_EXTENDS: // extends
+ case T_STATIC: // static
+ case T_STRING_VARNAME: // string varname?
+ case T_CHARACTER: // character
+ case T_ELSE: // else
+ case T_CONSTANT_ENCAPSED_STRING: // "some str"
+ case T_START_HEREDOC:
+ // Only if decision is not already made
+ // mark this non-executable.
+ if($this->lineType != LINE_TYPE_EXEC) {
+ $this->lineType = LINE_TYPE_NOEXEC;
+ }
+ break;
+
+ case T_PRIVATE: // private
+ case T_PUBLIC: // public
+ case T_PROTECTED: // protected
+ case T_VAR: // var
+ case T_FUNCTION: // function
+ case T_CLASS: // class
+ case T_INTERFACE: // interface
+ case T_REQUIRE: // require
+ case T_REQUIRE_ONCE: // require_once
+ case T_INCLUDE: // include
+ case T_INCLUDE_ONCE: // include_once
+ case T_ARRAY: // array
+ case T_SWITCH: // switch
+ case T_CONST: // const
+ case T_TRY: // try
+ $this->lineType = LINE_TYPE_NOEXEC;
+ // No need to see any further
+ $seenEnough = true;
+ break;
+
+ case T_VARIABLE: // $foo
+ $seeMore = true;
+ $this->lineType = LINE_TYPE_EXEC;
+ break;
+
+ case T_CLOSE_TAG:
+ if($tokenCnt != count($tokens)) {
+ // Token is not last (because we inserted that)
+ $this->logger->debug("T_CLOSE_TAG for tokenCnt " . $tokenCnt . " End of PHP code.");
+ $phpEnded = true; // php end tag found within the line.
+ }
+ if($this->lineType != LINE_TYPE_EXEC) {
+ $this->lineType = LINE_TYPE_NOEXEC;
+ }
+ break;
+
+ default:
+ $seeMore = false;
+ $this->lineType = LINE_TYPE_EXEC;
+ break;
+ }
+ $this->logger->debug("Status: " . $this->getLineTypeStr($this->lineType) . "\t\tToken type: $tokenType \tText: $text",
+ __FILE__, __LINE__);
+ }
+ if(($this->lineType == LINE_TYPE_EXEC && !$seeMore)
+ || $seenEnough) {
+ $this->logger->debug("Made a decision! Exiting. Token Type: $tokenType & Text: $text",
+ __FILE__, __LINE__);
+ if($seenEnough) {
+ $this->logger->debug("Seen enough at Token Type: $tokenType & Text: $text",
+ __FILE__, __LINE__);
+ }
+ break;
+ }
+ } // end foreach
+ $this->logger->debug("Line Type: " . $this->getLineTypeStr($this->lineType),
+ __FILE__, __LINE__);
+ if($this->inPHP) {
+ $this->lastLineEndTokenType = $this->getLastTokenType($tokens);
+ }
+ $this->logger->debug("Last End Token: " . $this->lastLineEndTokenType,
+ __FILE__, __LINE__);
+
+ if($this->inPHP) {
+ // Check if PHP block ends on this line
+ if($phpEnded) {
+ $this->inPHP = false;
+ // If line is not executable so far, check for the
+ // remaining part
+ if($this->lineType != LINE_TYPE_EXEC) {
+ //return $this->processLine(trim(substr($line, $pos+2)));
+ }
+ }
+ }
+ }
+
+ /*}}}*/
+ /*{{{ public function getLineType() */
+
+ /**
+ * Returns the type of line just read
+ *
+ * @return Line type
+ * @access public
+ */
+ public function getLineType() {
+ return $this->lineType;
+ }
+ /*}}}*/
+ /*{{{ protected function isContinuation() */
+
+ /**
+ * Check if a line is a continuation of the previous line
+ *
+ * @param &$token Second token in a line (after PHP start)
+ * @return Boolean True if the line is a continuation; false otherwise
+ * @access protected
+ */
+ protected function isContinuation(&$token) {
+ if(is_string($token)) {
+ switch($token) {
+ case ".":
+ case ",";
+ case "]":
+ case "[":
+ case "(":
+ case ")":
+ case "=":
+ return true;
+ }
+ }
+ else {
+ list($tokenType, $text) = $token;
+ switch($tokenType) {
+ case T_CONSTANT_ENCAPSED_STRING:
+ case T_ARRAY:
+ case T_DOUBLE_ARROW:
+ case T_OBJECT_OPERATOR:
+ case T_LOGICAL_XOR:
+ case T_LOGICAL_AND:
+ case T_LOGICAL_OR:
+ case T_PLUS_EQUAL:
+ case T_MINUS_EQUAL:
+ case T_MUL_EQUAL:
+ case T_DIV_EQUAL:
+ case T_CONCAT_EQUAL:
+ case T_MOD_EQUAL:
+ case T_AND_EQUAL:
+ case T_OR_EQUAL:
+ case T_XOR_EQUAL:
+ case T_BOOLEAN_AND:
+ case T_BOOLEAN_OR:
+ case T_LNUMBER:
+ case T_DNUMBER:
+ return true;
+
+ case T_STRING:
+ case T_VARIABLE:
+ return in_array($this->lastLineEndTokenType, PHPParser::$contTypes);
+ }
+ }
+
+ return false;
+ }
+ /*}}}*/
+ /*{{{ protected function getTokenType() */
+
+ /**
+ * Get the token type of a token (if exists) or
+ * the token itself.
+ *
+ * @param $token Token
+ * @return Token type or token itself
+ * @access protected
+ */
+ protected function getTokenType($token) {
+ if(is_string($token)) {
+ return $token;
+ }
+ else {
+ list($tokenType, $text) = $token;
+ return $tokenType;
+ }
+ }
+ /*}}}*/
+ /*{{{*/
+
+ /**
+ * Return the type of last non-empty token in a line
+ *
+ * @param &$tokens Array of tokens for a line
+ * @return mixed Last non-empty token type (or token) if exists; false otherwise
+ * @access protected
+ */
+ protected function getLastTokenType(&$tokens) {
+ for($i = count($tokens)-2; $i > 0; $i--) {
+ if(empty($tokens[$i])) {
+ continue;
+ }
+ if(is_string($tokens[$i])) {
+ return $tokens[$i];
+ }
+ else {
+ list($tokenType, $text) = $tokens[$i];
+ if($tokenType != T_WHITESPACE && $tokenType != T_EMPTY) {
+ return $tokenType;
+ }
+ }
+ }
+ return false;
+ }
+ /*}}}*/
+
+ /*
+ // Main
+ $obj = new PHPParser();
+ $obj->parse("test.php");
+ while(($line = $obj->getLine()) !== false) {
+ echo "#########################\n";
+ echo "[" . $line . "] Type: [" . $obj->getLineTypeStr($obj->getLineType()) . "]\n";
+ echo "#########################\n";
+ }
+ */
+
+ }
+?>
diff --git a/vendor/spikephpcoverage/parser/Parser.php b/vendor/spikephpcoverage/parser/Parser.php
new file mode 100644
index 000000000..ed242b0f3
--- /dev/null
+++ b/vendor/spikephpcoverage/parser/Parser.php
@@ -0,0 +1,217 @@
+
+totalLines = 0;
+ $this->coveredLines = 0;
+ $this->uncoveredLines = 0;
+
+ $this->fileRef = false;
+ $this->line = false;
+ $this->lineType = false;
+
+ $this->logger = $util->getLogger();
+ }
+
+ /*}}}*/
+ /*{{{ public abstract function parse() */
+
+ /**
+ * Parse a given file
+ *
+ * @param $filename Full path of the file
+ * @return FALSE on error.
+ * @access public
+ */
+ public function parse($filename) {
+ $this->filename = $filename;
+ $ret = $this->openFileReadOnly();
+ if(!$ret) {
+ die("Error: Cannot open file: $this->filename \n");
+ }
+ }
+
+ /*}}}*/
+ /*{{{ protected abstract function processLine() */
+
+ /**
+ * Process the line and classify it into either
+ * covered and uncovered.
+ *
+ * @param $line
+ * @return
+ * @access protected
+ */
+ protected abstract function processLine($line);
+
+ /*}}}*/
+ /*{{{ public function getLine() */
+
+ /**
+ * Returns the next line from file.
+ *
+ * @return Next line from file
+ * @access public
+ */
+ public function getLine() {
+ if(!feof($this->fileRef)) {
+ $this->line = fgets($this->fileRef);
+ $this->processLine($this->line);
+ }
+ else {
+ fclose($this->fileRef);
+ $this->line = false;
+ }
+ return $this->line;
+ }
+
+ /*}}}*/
+ /*{{{ public abstract function getLineType() */
+
+ /**
+ * Returns the type of last line read.
+ *
+ * The type can be either
+ * * LINE_TYPE_EXEC Line that can be executed.
+ * * LINE_TYPE_NOEXEC Line that cannot be executed.
+ * This includes the variable and function definitions
+ * (without initialization), blank lines, non-PHP lines,
+ * etc.
+ *
+ * @return Type of last line
+ * @access public
+ */
+ public abstract function getLineType();
+
+ /*}}}*/
+ /*{{{ public function getLineTypeStr() */
+
+ /**
+ * Returns the string representation of LINE_TYPE
+ *
+ * @param $lineType
+ * @return Type of line
+ * @access public
+ */
+ public function getLineTypeStr($lineType) {
+ if($lineType == LINE_TYPE_EXEC) {
+ return "LINE_TYPE_EXEC";
+ }
+ else if($lineType == LINE_TYPE_NOEXEC) {
+ return "LINE_TYPE_NOEXEC";
+ }
+ else if($lineType == LINE_TYPE_CONT) {
+ return "LINE_TYPE_CONT";
+ }
+ else {
+ return "LINE_TYPE_UNKNOWN";
+ }
+ }
+
+ /*}}}*/
+ /*{{{ protected function openFileReadOnly() */
+
+ /**
+ * Opens the file to be parsed in Read-only mode
+ *
+ * @return FALSE on failure.
+ * @access protected
+ */
+ protected function openFileReadOnly() {
+ $this->fileRef = fopen($this->filename, "r");
+ return $this->fileRef !== false;
+ }
+
+ /*}}}*/
+ /*{{{ public function getTotalLines() */
+
+ /**
+ * Returns the total lines (PHP, non-PHP) from a file
+ *
+ * @return Number of lines
+ * @access public
+ */
+ public function getTotalLines() {
+ return $this->totalLines;
+ }
+
+ /*}}}*/
+ /*{{{ public function getCoveredLines() */
+
+ /**
+ * Returns the number of covered PHP lines
+ *
+ * @return Number of covered lines
+ * @access public
+ */
+ public function getCoveredLines() {
+ return $this->coveredLines;
+ }
+
+ /*}}}*/
+ /*{{{ public function getUncoveredLines() */
+
+ /**
+ * Returns the number of uncovered PHP lines
+ *
+ * Note that the sum of covered and uncovered
+ * lines may not be equal to total lines.
+ *
+ * @return Number of uncovered lines
+ * @access public
+ */
+ public function getUncoveredLines() {
+ return $this->uncoveredLines;
+ }
+
+ /*}}}*/
+ }
+
+?>
diff --git a/vendor/spikephpcoverage/phpcoverage.inc.php b/vendor/spikephpcoverage/phpcoverage.inc.php
new file mode 100644
index 000000000..bf004363b
--- /dev/null
+++ b/vendor/spikephpcoverage/phpcoverage.inc.php
@@ -0,0 +1,56 @@
+
+ PHPCOVERAGE_HOME=/path/to/coverage/home'\n";
+ die($msg);
+ }
+
+ // Fallback
+ if(!defined("PHPCOVERAGE_HOME")) {
+ $include_path = get_include_path();
+ set_include_path($PHPCOVERAGE_HOME. PATH_SEPARATOR . $include_path);
+ define('PHPCOVERAGE_HOME', $PHPCOVERAGE_HOME);
+ }
+
+ error_log("[phpcoverage.inc.php] PHPCOVERAGE_HOME=" . $PHPCOVERAGE_HOME);
+ error_log("[phpcoverage.inc.php] PHPCOVERAGE_REPORT_DIR=" . $PHPCOVERAGE_REPORT_DIR);
+ error_log("[phpcoverage.inc.php] PHPCOVERAGE_APPBASE_PATH=" . $PHPCOVERAGE_APPBASE_PATH);
+
+?>
diff --git a/vendor/spikephpcoverage/phpcoverage.remote.bottom.inc.php b/vendor/spikephpcoverage/phpcoverage.remote.bottom.inc.php
new file mode 100644
index 000000000..d894f5312
--- /dev/null
+++ b/vendor/spikephpcoverage/phpcoverage.remote.bottom.inc.php
@@ -0,0 +1,51 @@
+
+getLogger();
+
+ // Create a distinct hash (may or may not be unique)
+ $session_id = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["SERVER_NAME"]);
+ $tmpFile = $util->getTmpDir() . "/phpcoverage.session." . $session_id;
+ $logger->info("[phpcoverage.remote.bottom.inc.php] Session id: " . $session_id,
+ __FILE__, __LINE__);
+
+ if(!isset($cov)) {
+ if(file_exists($tmpFile)) {
+ $object = file_get_contents($tmpFile);
+ $cov = unserialize($object);
+ $logger->info("[phpcoverage.remote.bottom.inc.php] Coverage object found: " . $cov, __FILE__, __LINE__);
+ }
+ }
+
+ if(isset($cov)) {
+ // PHPCoverage bottom half
+ if(!isset($called_script)) {
+ $called_script = "";
+ }
+ $logger->info("[phpcoverage.remote.bottom.inc.php] END: " . $called_script,
+ __FILE__, __LINE__);
+ // Save the code coverage
+ $cov->saveCoverageXml();
+ $logger->info("[phpcoverage.remote.bottom.inc.php] Saved coverage xml",
+ __FILE__, __LINE__);
+ $cov->startInstrumentation();
+ $logger->info("[phpcoverage.remote.bottom.inc.php] Instrumentation turned on.",
+ __FILE__, __LINE__);
+ $object = serialize($cov);
+ file_put_contents($tmpFile, $object);
+ $logger->info("[phpcoverage.remote.bottom.inc.php] ################## END ###################",
+ __FILE__, __LINE__);
+ }
+ }
+
+?>
diff --git a/vendor/spikephpcoverage/phpcoverage.remote.top.inc.php b/vendor/spikephpcoverage/phpcoverage.remote.top.inc.php
new file mode 100644
index 000000000..f31fe6a00
--- /dev/null
+++ b/vendor/spikephpcoverage/phpcoverage.remote.top.inc.php
@@ -0,0 +1,151 @@
+
+ in your HTTP request.";
+ error_log("[phpcoverage.remote.top.inc.php] FATAL: " . $msg);
+ die($msg);
+ }
+ else {
+ $PHPCOVERAGE_HOME = $env_var;
+ }
+ }
+
+ if(empty($PHPCOVERAGE_HOME) || !is_dir($PHPCOVERAGE_HOME)) {
+ $msg = "ERROR: Could not locate PHPCOVERAGE_HOME [$PHPCOVERAGE_HOME]. ";
+ $msg .= "Use 'php PHPCOVERAGE_HOME=/path/to/coverage/home'\n";
+ die($msg);
+ }
+
+
+ // Fallback
+ if(!defined("PHPCOVERAGE_HOME")) {
+ $include_path = get_include_path();
+ set_include_path($PHPCOVERAGE_HOME. ":" . $include_path);
+ define('PHPCOVERAGE_HOME', $PHPCOVERAGE_HOME);
+ }
+
+ if($debug) error_log("[phpcoverage.remote.top.inc.php] PHPCOVERAGE_HOME=" . $PHPCOVERAGE_HOME);
+
+ // Register the shutdown function to get code coverage results before
+ // script exits abnormally.
+ register_shutdown_function('spikephpcoverage_before_shutdown');
+ require_once PHPCOVERAGE_HOME . "/conf/phpcoverage.conf.php";
+ require_once PHPCOVERAGE_HOME . "/util/Utility.php";
+ require_once PHPCOVERAGE_HOME . "/remote/RemoteCoverageRecorder.php";
+ require_once PHPCOVERAGE_HOME . "/reporter/HtmlCoverageReporter.php";
+
+ global $util;
+ $logger = $util->getLogger();
+
+ // Create a distinct hash (may or may not be unique)
+ $session_id = md5($_SERVER["REMOTE_ADDR"] . $_SERVER["SERVER_NAME"]);
+ $tmpFile = $util->getTmpDir() . "/phpcoverage.session." . $session_id;
+ $logger->info("[phpcoverage.remote.top.inc.php] Session id: " . $session_id . " Saved in: " . $tmpFile,
+ __FILE__, __LINE__);
+ if(file_exists($tmpFile)) {
+ $object = file_get_contents($tmpFile);
+ $cov = unserialize($object);
+ $logger->info("[phpcoverage.remote.top.inc.php] Coverage object found." ,
+ __FILE__, __LINE__);
+ }
+ else {
+ $covReporter = new HtmlCoverageReporter(
+ "PHPCoverage report",
+ "",
+ $util->getTmpDir() . "/php-coverage-report"
+ );
+ $cov = new RemoteCoverageRecorder(array(), array(), $covReporter);
+ $object = serialize($cov);
+ file_put_contents($tmpFile, $object);
+ $logger->info("[phpcoverage.remote.top.inc.php] Stored coverage object found",
+ __FILE__, __LINE__);
+ }
+
+ if(!empty($_REQUEST["phpcoverage-action"])) {
+ $logger->info("[phpcoverage.remote.top.inc.php] phpcoverage-action=" . strtolower($_REQUEST["phpcoverage-action"]),
+ __FILE__, __LINE__);
+ switch(strtolower($_REQUEST["phpcoverage-action"])) {
+ case "init":
+ if(!empty($_REQUEST["tmp-dir"])) {
+ $cov->setTmpDir($_REQUEST["tmp-dir"]);
+ }
+ $cov->setCoverageFileName($_REQUEST["cov-file-name"]);
+ if(!$cov->cleanCoverageFile()) {
+ die("Cannot delete existing coverage data.");
+ }
+ break;
+
+ case "instrument":
+ break;
+
+ case "get-coverage-xml":
+ $cov->getCoverageXml();
+ break;
+
+ case "cleanup":
+ if(file_exists($tmpFile) && is_writable($tmpFile)) {
+ unlink($tmpFile);
+ unset($cov);
+ $logger->info("[phpcoverage.remote.top.inc.php] Cleaned up!",
+ __FILE__, __LINE__);
+ return;
+ }
+ else {
+ $logger->error("[phpcoverage.remote.top.inc.php] Error deleting file: " . $tmpFile,
+ __FILE__, __LINE__);
+ }
+ break;
+ }
+ }
+
+ $cov->startInstrumentation();
+ $logger->info("[phpcoverage.remote.top.inc.php] Instrumentation turned on.",
+ __FILE__, __LINE__);
+ $object = serialize($cov);
+ file_put_contents($tmpFile, $object);
+ $logger->info("[phpcoverage.remote.top.inc.php] BEGIN: " . $called_script,
+ __FILE__, __LINE__);
+ }
+
+ function spikephpcoverage_before_shutdown() {
+ global $cov, $logger;
+ $logger->debug("[phpcoverage.remote.top.inc.php::before_shutdown()] Getting code coverage before shutdown: START",
+ __FILE__, __LINE__);
+ require dirname(__FILE__) . "/phpcoverage.remote.bottom.inc.php";
+ $logger->debug("[phpcoverage.remote.top.inc.php::before_shutdown()] Getting code coverage before shutdown: FINISH",
+ __FILE__, __LINE__);
+ }
+?>
diff --git a/vendor/spikephpcoverage/remote/RemoteCoverageRecorder.php b/vendor/spikephpcoverage/remote/RemoteCoverageRecorder.php
new file mode 100644
index 000000000..a2a26ef52
--- /dev/null
+++ b/vendor/spikephpcoverage/remote/RemoteCoverageRecorder.php
@@ -0,0 +1,309 @@
+
+
+ * @version $Revision: $
+ * @package SpikePHPCoverage_Remote
+ */
+ class RemoteCoverageRecorder extends CoverageRecorder {
+ /*{{{ Members */
+
+ protected $traceFilePath;
+ protected $xdebugTraceReader;
+ protected $tmpDir;
+ protected $tmpTraceFilename = "phpcoverage.xdebug.trace";
+ protected $coverageFileName = "phpcoverage.coverage.xml";
+
+ protected $xmlStart = "";
+ protected $xmlEnd = "";
+
+ /*}}}*/
+ /*{{{ public function __construct() */
+
+ /**
+ * Constructor
+ *
+ * @access public
+ */
+ public function __construct(
+ $includePaths=array("."),
+ $excludePaths=array(),
+ $reporter="new HtmlCoverageReporter()"
+ ) {
+ global $util;
+ parent::__construct($includePaths, $excludePaths, $reporter);
+ $this->isRemote = true;
+ $this->phpCoverageFiles[] = "phpcoverage.remote.inc.php";
+ $this->phpCoverageFiles[] = "phpcoverage.remote.top.inc.php";
+ $this->phpCoverageFiles[] = "phpcoverage.remote.bottom.inc.php";
+
+ // configuration
+ $this->tmpDir = $util->getTmpDir();
+ }
+
+ /*}}}*/
+ /*{{{ Getters and Setters */
+
+ public function getTraceFilePath() {
+ return $this->traceFilePath;
+ }
+
+ public function setTraceFilePath($traceFilePath) {
+ $this->traceFilePath = $traceFilePath;
+ }
+
+ public function getTmpDir() {
+ return $this->tmpDir;
+ }
+
+ public function setTmpDir($tmpTraceDir) {
+ $this->tmpDir = $tmpTraceDir;
+ }
+
+ public function getCoverageFileName() {
+ return $this->coverageFileName;
+ }
+
+ public function setCoverageFileName($covFileName) {
+ $this->coverageFileName = $covFileName;
+ }
+
+ /*}}}*/
+ /*{{{ public function cleanCoverageFile() */
+
+ /**
+ * Deletes a coverage data file if one exists.
+ *
+ * @return Boolean True on success, False on failure.
+ * @access public
+ */
+ public function cleanCoverageFile() {
+ $filepath = $this->tmpDir . "/" . $this->coverageFileName;
+ if(file_exists($filepath)) {
+ if(is_writable($filepath)) {
+ unlink($filepath);
+ }
+ else {
+ $this->logger->error("[RemoteCoverageRecorder::cleanCoverageFile()] "
+ . "ERROR: Cannot delete $filepath.", __FILE__, __LINE__);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ protected function prepareCoverageXml() */
+
+ /**
+ * Convert the Coverage data into an XML.
+ *
+ * @return String XML generated from Coverage data
+ * @access protected
+ */
+ protected function prepareCoverageXml() {
+ global $util;
+ $xmlString = "";
+ $xmlBody = "";
+ if(!empty($this->coverageData)) {
+ foreach($this->coverageData as $file => &$lines) {
+ $xmlBody .= "replaceBackslashes($file) . "\">";
+ foreach($lines as $linenum => &$frequency) {
+ $xmlBody .= "";
+ }
+ $xmlBody .= "\n";
+ }
+ unset($this->coverageData);
+ }
+ else {
+ $this->logger->info("[RemoteCoverageRecorder::prepareCoverageXml()] Coverage data is empty.",
+ __FILE__, __LINE__);
+ }
+ $xmlString .= $xmlBody;
+ $this->logger->debug("[RemoteCoverageRecorder::prepareCoverageXml()] Xml: " . $xmlString, __FILE__, __LINE__);
+ return $xmlString;
+ }
+
+ /*}}}*/
+ /*{{{ protected function parseCoverageXml() */
+
+ /**
+ * Parse coverage XML to regenerate the Coverage data array.
+ *
+ * @param $xml XML String or URL of the coverage data
+ * @param $stream=false Is the input a stream?
+ * @return
+ * @access protected
+ */
+ protected function parseCoverageXml(&$xml, $stream=false) {
+ // Need to handle multiple xml files.
+ if(!is_array($xml)) {
+ $xml = array($xml);
+ }
+ for($i = 0; $i < count($xml); $i++) {
+ $xmlParser = new CoverageXmlParser();
+ if($stream) {
+ $xmlParser->setInput($xml[$i]);
+ }
+ else {
+ $xmlParser->setInputString($xml[$i]);
+ }
+ $xmlParser->parse();
+ $data =& $xmlParser->getCoverageData();
+ if(empty($this->coverageData)) {
+ $this->coverageData = $data;
+ }
+ else {
+ $data2 = array_merge_recursive($this->coverageData, $data);
+ $this->coverageData = $data2;
+ }
+ $this->logger->debug("[RemoteCoverageRecorder::prepareCoverageXml()] " . "Coverage data intermediate: " . print_r($this->coverageData, true));
+ }
+ }
+
+ /*}}}*/
+ /*{{{ public function getCoverageXml() */
+
+ /**
+ * Dumps the coverage data in XML format
+ *
+ * @access public
+ */
+ public function getCoverageXml() {
+ $filepath = $this->tmpDir . "/" . $this->coverageFileName;
+ if(file_exists($filepath) && is_readable($filepath)) {
+ $fp = fopen($filepath, "r");
+ if($fp) {
+ while(!feof($fp)) {
+ $xml = fread($fp, 4096);
+ echo $xml;
+ }
+ fclose($fp);
+ return true;
+ }
+ else {
+ $this->logger->error("Could not read coverage data file.",
+ __FILE__, __LINE__);
+ }
+ }
+ else {
+ $this->logger->error("[RemoteCoverageRecorder::getCoverageXml()] "
+ . "ERROR: Cannot read file " . $filepath, __FILE__, __LINE__);
+ }
+ return false;
+ }
+
+ /*}}} */
+ /*{{{ protected function appendDataToFile() */
+
+ /**
+ * Append coverage data to xml file
+ *
+ * @param $newXml New xml recorded
+ * @return True on success; false otherwise
+ * @access protected
+ */
+ protected function appendDataToFile($newXml) {
+ $filepath = $this->tmpDir . "/" . $this->coverageFileName;
+ if(!file_exists($filepath)) {
+ // If new file, write the xml start and end tags
+ $bytes = file_put_contents($filepath, $this->xmlStart . "\n" . $this->xmlEnd);
+ if(!$bytes) {
+ $this->logger->critical("[RemoteCoverageRecorder::appendDataToFile()] Could not create file: " . $filepath, __FILE__, __LINE__);
+ return false;
+ }
+ }
+ if(file_exists($filepath) && is_readable($filepath)) {
+ $res = fopen($filepath, "r+");
+ if($res) {
+ fseek($res, -1 * strlen($this->xmlEnd), SEEK_END);
+ $ret = fwrite($res, $newXml);
+ if(!$ret) {
+ $this->logger->error("[RemoteCoverageRecorder::appendDataToFile()] Could not append data to file.",
+ __FILE__, __LINE__);
+ fclose($res);
+ return false;
+ }
+ fwrite($res, $this->xmlEnd);
+ fclose($res);
+ }
+ else {
+ $this->logger->error("[RemoteCoverageRecorder::appendDataToFile()] Error opening file for writing: " . $filepath,
+ __FILE__, __LINE__);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ public function saveCoverageXml() */
+
+ /**
+ * Append coverage xml to a xml data file.
+ *
+ * @return Boolean True on success, False on error
+ * @access public
+ */
+ public function saveCoverageXml() {
+ $filepath = $this->tmpDir . "/" . $this->coverageFileName;
+ if($this->stopInstrumentation()) {
+ $xml = $this->prepareCoverageXml();
+ $ret = $this->appendDataToFile($xml);
+ if(!$ret) {
+ $this->logger->warn("[RemoteCoverageRecorder::saveCoverageXml()] "
+ . "ERROR: Nothing was written to " . $filepath,
+ __FILE__, __LINE__);
+ return false;
+ }
+ $this->logger->info("[RemoteCoverageRecorder::saveCoverageXml()] "
+ . "Saved XML to $filepath; size: [" . filesize($filepath)
+ . "]", __FILE__, __LINE__);
+ return true;
+ }
+ return false;
+ }
+
+ /*}}}*/
+ /*{{{ public function generateReport() */
+
+ /**
+ * Generate report from the xml coverage data
+ * The preferred method for usage of this function is
+ * passing a stream of the XML data in. This is much more
+ * efficient and consumes less memory.
+ *
+ * @param $xmlUrl Url where XML data is available or string
+ * @param $stream=false Is the xml available as stream?
+ * @access public
+ */
+ public function generateReport($xmlUrl, $stream=false) {
+ $this->logger->debug("XML Url: " . $xmlUrl, __FILE__, __LINE__);
+ $this->parseCoverageXml($xmlUrl, true);
+ $this->logger->debug("Coverage Data final: " . print_r($this->coverageData, true));
+ parent::generateReport();
+ }
+
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/remote/XdebugTraceReader.php b/vendor/spikephpcoverage/remote/XdebugTraceReader.php
new file mode 100644
index 000000000..58bd54669
--- /dev/null
+++ b/vendor/spikephpcoverage/remote/XdebugTraceReader.php
@@ -0,0 +1,125 @@
+
+
+ * @version $Revision: $
+ * @package SpikePHPCoverage_Parser
+ */
+ class XdebugTraceReader {
+ /*{{{ Members */
+
+ protected $traceFilePath;
+ protected $handle;
+ protected $coverage = array();
+
+ /*}}}*/
+ /*{{{ Constructor */
+
+ /**
+ * Constructor
+ *
+ * @param $traceFilePath Path of the Xdebug trace file
+ * @access public
+ */
+ public function __construct($traceFilePath) {
+ $this->traceFilePath = $traceFilePath;
+ }
+
+ /*}}}*/
+ /*{{{ protected function openTraceFile() */
+
+ /**
+ * Opens the trace file
+ *
+ * @return Boolean True on success, false on failure.
+ * @access protected
+ */
+ protected function openTraceFile() {
+ $this->handle = fopen($this->traceFilePath, "r");
+ return !empty($this->handle);
+ }
+
+ /*}}}*/
+ /*{{{ public function parseTraceFile() */
+
+ /**
+ * Parses the trace file
+ *
+ * @return Boolean True on success, false on failure.
+ * @access public
+ */
+ public function parseTraceFile() {
+ if(!$this->openTraceFile()) {
+ error_log("[XdebugTraceReader::parseTraceFile()] Unable to read trace file.");
+ return false;
+ }
+ while(!feof($this->handle)) {
+ $line = fgets($this->handle);
+ // echo "Line: " . $line . "\n";
+ $this->processTraceLine($line);
+ }
+ fclose($this->handle);
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ protected function processTraceLine() */
+
+ /**
+ * Process a give trace line
+ *
+ * @param $line Line from a trace file
+ * @return Boolean True on success, false on failure
+ * @access protected
+ */
+ protected function processTraceLine($line) {
+ $dataparts = explode("\t", $line);
+ // print_r($dataparts);
+ $cnt = count($dataparts);
+ if($cnt < 2) {
+ return false;
+ }
+ if(!file_exists($dataparts[$cnt-2])) {
+ // echo "No file: " . $dataparts[$cnt-2] . "\n";
+ return false;
+ }
+ // Trim the entries
+ $dataparts[$cnt-2] = trim($dataparts[$cnt-2]);
+ $dataparts[$cnt-1] = trim($dataparts[$cnt-1]);
+
+ if(!isset($this->coverage[$dataparts[$cnt-2]][$dataparts[$cnt-1]])) {
+ $this->coverage[$dataparts[$cnt-2]][$dataparts[$cnt-1]] = 1;
+ }
+ else {
+ $this->coverage[$dataparts[$cnt-2]][$dataparts[$cnt-1]] ++;
+ }
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ public function getCoverageData() */
+
+ /**
+ * Returns the coverage array
+ *
+ * @return Array Array of coverage data from parsing.
+ * @access public
+ */
+ public function getCoverageData() {
+ return $this->coverage;
+ }
+
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/reporter/CoverageReporter.php b/vendor/spikephpcoverage/reporter/CoverageReporter.php
new file mode 100644
index 000000000..f537bc42a
--- /dev/null
+++ b/vendor/spikephpcoverage/reporter/CoverageReporter.php
@@ -0,0 +1,302 @@
+
+
+ * @version $Revision: 14665 $
+ * @package SpikePHPCoverage_Reporter
+ */
+ abstract class CoverageReporter {
+ // {{{ Members
+
+ protected $logger;
+
+ // Report heading - will be displayed as the title of the main page.
+ protected $heading;
+ // CSS file path to be used.
+ protected $style;
+ // Directory where the report file(s) are written.
+ protected $outputDir;
+
+ // Total number of lines in all the source files.
+ protected $grandTotalLines;
+ // Total number of lines covered in code coverage measurement.
+ protected $grandTotalCoveredLines;
+ // Total number of executable code lines that were left untouched.
+ protected $grandTotalUncoveredLines;
+ // Total number of files included
+ protected $grandTotalFiles;
+ protected $fileCoverage = array();
+ protected $recorder = false;
+
+ // }}}
+ /*{{{ public function __construct()*/
+
+ /**
+ * The constructor (PHP5 compatible)
+ *
+ * @param $heading
+ * @param $style
+ * @param $dir
+ * @access public
+ */
+ public function __construct(
+ $heading="Coverage Report",
+ $style="",
+ $dir="report"
+ ) {
+
+ global $util;
+ echo get_class($util);
+ $this->heading = $heading;
+ $this->style = $style;
+ $this->outputDir = $util->replaceBackslashes($dir);
+ // Create the directory if not there
+ $this->createReportDir();
+ $this->grandTotalFiles = 0;
+ $this->grandTotalLines = 0;
+ $this->grandTotalCoveredLines = 0;
+ $this->grandTotalUncoveredLines = 0;
+
+ // Configure
+ $this->logger = $util->getLogger();
+ }
+
+ /*}}}*/
+ /*{{{ protected function createReportDir() */
+
+ /**
+ * Create the report directory if it does not exists
+ *
+ * @access protected
+ */
+ protected function createReportDir() {
+ global $util;
+ if(!file_exists($this->outputDir)) {
+ $util->makeDirRecursive($this->outputDir, 0755);
+ }
+ if(file_exists($this->outputDir)) {
+ $this->outputDir = $util->replaceBackslashes(realpath($this->outputDir));
+ }
+ }
+
+ /*}}}*/
+ /*{{{ protected function updateGrandTotals() */
+
+ /**
+ * Update the grand totals
+ *
+ * @param &$coverageCounts Coverage counts for a file
+ * @access protected
+ */
+ protected function updateGrandTotals(&$coverageCounts) {
+ $this->grandTotalLines += $coverageCounts['total'];
+ $this->grandTotalCoveredLines += $coverageCounts['covered'];
+ $this->grandTotalUncoveredLines += $coverageCounts['uncovered'];
+
+ $this->recordFileCoverageInfo($coverageCounts);
+ }
+
+ /*}}}*/
+ /*{{{ public function getGrandCodeCoveragePercentage()*/
+
+ /**
+ * Returns Overall Code Coverage percentage
+ *
+ * @return double Code Coverage percentage rounded to two decimals
+ * @access public
+ */
+ public function getGrandCodeCoveragePercentage() {
+ if($this->grandTotalCoveredLines+$this->grandTotalUncoveredLines == 0) {
+ return round(0, 2);
+ }
+ return round(((double)$this->grandTotalCoveredLines/((double)$this->grandTotalCoveredLines + (double)$this->grandTotalUncoveredLines)) * 100.0, 2);
+ }
+
+ /*}}}*/
+ /*{{{ public function getFileCoverageInfo() */
+
+ /**
+ * Return the array containing file coverage information.
+ *
+ * The array returned contains following fields
+ * * filename: Name of the file
+ * * total: Total number of lines in that file
+ * * covered: Total number of executed lines in that file
+ * * uncovered: Total number of executable lines that were not executed.
+ *
+ * @return array Array of file coverage information
+ * @access public
+ */
+ public function getFileCoverageInfo() {
+ return $this->fileCoverage;
+ }
+
+ /*}}}*/
+ /*{{{ public function recordFileCoverageInfo() */
+
+ /**
+ * Record the file coverage information for a file.
+ *
+ * @param &$fileCoverage Coverage information for a file
+ * @access protected
+ */
+ protected function recordFileCoverageInfo(&$fileCoverage) {
+ $this->fileCoverage[] = $fileCoverage;
+ }
+
+ /*}}}*/
+ /*{{{ public function printTextSummary() */
+
+ /**
+ * Print the coverage summary to filename (if specified) or stderr
+ *
+ * @param $filename=false Filename to write the log to
+ * @access public
+ */
+ public function printTextSummary($filename=false) {
+ global $util;
+ $str = "\n";
+ $str .= "##############################################\n";
+ $str .= " Code Coverage Summary: " . $this->heading . "\n";
+ $str .= " Total Files: " . $this->grandTotalFiles . "\n";
+ $str .= " Total Lines: " . $this->grandTotalLines . "\n";
+ $str .= " Total Covered Lines of Code: " . $this->grandTotalCoveredLines . "\n";
+ $str .= " Total Missed Lines of Code: " . $this->grandTotalUncoveredLines . "\n";
+ $str .= " Total Lines of Code: " . ($this->grandTotalCoveredLines + $this->grandTotalUncoveredLines) . "\n";
+ $str .= " Code Coverage: " . $this->getGrandCodeCoveragePercentage() . "%\n";
+ $str .= "##############################################\n";
+
+ if(empty($filename)) {
+ file_put_contents("php://stdout", $str);
+ }
+ else {
+ $filename = $util->replaceBackslashes($filename);
+ if(!file_exists(dirname($filename))) {
+ $ret = $util->makeDirRecursive(dirname($filename), 0755);
+ if(!$ret) {
+ die ("Cannot create directory " . dirname($filename) . "\n");
+ }
+ }
+ file_put_contents($filename, $str);
+ }
+ }
+
+ /*}}}*/
+/*{{{ protected function makeRelative() */
+
+ /**
+ * Convert the absolute path to PHP file markup to a path relative
+ * to the report dir.
+ *
+ * @param $filepath PHP markup file path
+ * @return Relative file path
+ * @access protected
+ */
+ protected function makeRelative($filepath) {
+ $dirPath = realpath($this->outputDir);
+ $absFilePath = realpath($filepath);
+
+ if(strpos($absFilePath, $dirPath) === 0) {
+ $relPath = substr($absFilePath, strlen($dirPath)+1);
+ return $relPath;
+ }
+ return $absFilePath;
+ }
+
+/*}}}*/
+/*{{{ protected function getRelativeOutputDirPath() */
+
+
+ /**
+ * Get the relative path of report directory with respect to the given
+ * filepath
+ *
+ * @param $filepath Path of the file (relative to the report dir)
+ * @return String Relative path of report directory w.r.t. filepath
+ * @access protected
+ */
+ protected function getRelativeOutputDirPath($filepath) {
+ $relPath = "";
+ $filepath = dirname($filepath);
+ while($filepath !== false && $filepath != ".") {
+ $relPath = "../" . $relPath;
+ $filepath = dirname($filepath);
+ }
+ return $relPath;
+ }
+
+/*}}}*/
+ /*{{{ public abstract function generateReport() */
+
+ /**
+ *
+ * This function generates report using one of the concrete subclasses.
+ *
+ * @param &$data Coverage Data recorded by coverage recorder.
+ * @access public
+ */
+ public abstract function generateReport(&$data);
+
+ /*}}}*/
+ /*{{{ Getters and Setters */
+
+ public function setHeading($heading) {
+ $this->heading = $heading;
+ }
+
+ public function getHeading() {
+ return $this->heading;
+ }
+
+ public function setStyle($style) {
+ $this->style = $style;
+ }
+
+ public function getStyle() {
+ return $this->style;
+ }
+
+ public function setOutputDir($dir) {
+ $this->outputDir = $dir;
+ }
+
+ public function getOutputDir() {
+ return $this->outputDir;
+ }
+
+ public function setCoverageRecorder(&$recorder) {
+ $this->recorder = $recorder;
+ }
+
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/reporter/HtmlCoverageReporter.php b/vendor/spikephpcoverage/reporter/HtmlCoverageReporter.php
new file mode 100644
index 000000000..f520544f7
--- /dev/null
+++ b/vendor/spikephpcoverage/reporter/HtmlCoverageReporter.php
@@ -0,0 +1,616 @@
+
+
+ * @version $Revision: 14665 $
+ * @package SpikePHPCoverage_Reporter
+ */
+ class HtmlCoverageReporter extends CoverageReporter {
+
+ /*{{{ Members */
+
+ private $coverageData;
+ private $htmlFile;
+ private $body;
+ private $header = "html/header.html";
+ private $footer = "html/footer.html";
+ private $indexHeader = "html/indexheader.html";
+ private $indexFooter = "html/indexfooter.html";
+
+ /*}}}*/
+ /*{{{ public function __construct() */
+
+ /**
+ * Constructor method (PHP5 only)
+ *
+ * @param $heading Heading of the report (shown as title)
+ * @param $style Name of the stylesheet file
+ * @param $dir Directory where the report files should be dumped
+ * @access public
+ */
+ public function __construct(
+ $heading="Coverage Report",
+ $style="",
+ $dir="report"
+ ) {
+ parent::__construct($heading, $style, $dir);
+ }
+
+ /*}}}*/
+ /*{{{ public function generateReport() */
+
+ /**
+ * Implementaion of generateReport abstract function.
+ * This is the only function that will be called
+ * by the instrumentor.
+ *
+ * @param &$data Reference to Coverage Data
+ * @access public
+ */
+ public function generateReport(&$data) {
+ if(!file_exists($this->outputDir)) {
+ mkdir($this->outputDir);
+ }
+ $this->coverageData =& $data;
+ $this->grandTotalFiles = count($this->coverageData);
+ $ret = $this->writeIndexFile();
+ if($ret === FALSE) {
+ $this->logger->error("Error occured!!!", __FILE__, __LINE__);
+ }
+ $this->logger->debug(print_r($data, true), __FILE__, __LINE__);
+ }
+
+ /*}}}*/
+ /*{{{ private function writeIndexFileHeader() */
+
+ /**
+ * Write the index file header to a string
+ *
+ * @return string String containing HTML code for the index file header
+ * @access private
+ */
+ private function writeIndexFileHeader() {
+ $str = false;
+ $dir = realpath(dirname(__FILE__));
+ if($dir !== false) {
+ $str = file_get_contents($dir . "/" . $this->indexHeader);
+ if($str == false) {
+ return $str;
+ }
+ $str = str_replace("%%heading%%", $this->heading, $str);
+ $str = str_replace("%%style%%", $this->style, $str);
+ }
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ private function writeIndexFileFooter() */
+
+ /**
+ * Write the index file footer to a string
+ *
+ * @return string String containing HTML code for the index file footer.
+ * @access private
+ */
+ private function writeIndexFileFooter() {
+ $str = false;
+ $dir = realpath(dirname(__FILE__));
+ if($dir !== false) {
+ $str = file_get_contents($dir . "/" . $this->indexFooter);
+ if($str == false) {
+ return $str;
+ }
+ }
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ private function createJSDir() */
+
+ /**
+ * Create a directory for storing Javascript for the report
+ *
+ * @access private
+ */
+ private function createJSDir() {
+ $jsDir = $this->outputDir . "/js";
+ if(file_exists($this->outputDir) && !file_exists($jsDir)) {
+ mkdir($jsDir);
+ }
+ $jsSortFile = realpath(dirname(__FILE__)) . "/js/sort_spikesource.js";
+ copy($jsSortFile, $jsDir . "/" . "sort_spikesource.js");
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ private function createImagesDir() */
+
+ /**
+ * Create a directory for storing images for the report
+ *
+ * @access private
+ */
+ private function createImagesDir() {
+ $imagesDir = $this->outputDir . "/images";
+ if(file_exists($this->outputDir) && !file_exists($imagesDir)) {
+ mkdir($imagesDir);
+ }
+ $imagesSpikeDir = $imagesDir . "/spikesource";
+ if(!file_exists($imagesSpikeDir)) {
+ mkdir($imagesSpikeDir);
+ }
+ $imagesArrowUpFile = realpath(dirname(__FILE__)) . "/images/arrow_up.gif";
+ $imagesArrowDownFile = realpath(dirname(__FILE__)) . "/images/arrow_down.gif";
+ $imagesPHPCoverageLogoFile = realpath(dirname(__FILE__)) . "/images/spikesource/phpcoverage.gif";
+ $imagesSpacerFile = realpath(dirname(__FILE__)) . "/images/spacer.gif";
+ copy($imagesArrowUpFile, $imagesDir . "/" . "arrow_up.gif");
+ copy($imagesArrowDownFile, $imagesDir . "/" . "arrow_down.gif");
+ copy($imagesSpacerFile, $imagesDir . "/" . "spacer.gif");
+ copy($imagesPHPCoverageLogoFile, $imagesSpikeDir . "/" . "phpcoverage.gif");
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ private function createStyleDir() */
+
+ private function createStyleDir() {
+ if(isset($this->style)) {
+ $this->style = trim($this->style);
+ }
+ if(empty($this->style)) {
+ $this->style = "spikesource.css";
+ }
+ $styleDir = $this->outputDir . "/css";
+ if(file_exists($this->outputDir) && !file_exists($styleDir)) {
+ mkdir($styleDir);
+ }
+ $styleFile = realpath(dirname(__FILE__)) . "/css/" . $this->style;
+ copy($styleFile, $styleDir . "/" . $this->style);
+ return true;
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeIndexFileTableHead() */
+
+ /**
+ * Writes the table heading for index.html
+ *
+ * @return string Table heading row code
+ * @access protected
+ */
+ protected function writeIndexFileTableHead() {
+ $str = "";
+ $str .= 'Details
';
+ $str .= '';
+ $str .= ' | ';
+ $str .= 'Lines | ';
+ $str .= ' | ';
+ $str .= '
';
+
+ // Second row - subheadings
+ $str .= '';
+ $str .= ' | ';
+ $str .= ' | ';
+ $str .= ' | ';
+ $str .= ' | ';
+ $str .= '
';
+ $str .= '';
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeIndexFileTableRow() */
+
+ /**
+ * Writes one row in the index.html table to display filename
+ * and coverage recording.
+ *
+ * @param $fileLink link to html details file.
+ * @param $realFile path to real PHP file.
+ * @param $fileCoverage Coverage recording for that file.
+ * @return string HTML code for a single row.
+ * @access protected
+ */
+ protected function writeIndexFileTableRow($fileLink, $realFile, $fileCoverage) {
+
+ global $util;
+ $fileLink = $this->makeRelative($fileLink);
+ $realFileShort = $util->shortenFilename($realFile);
+ $str = "";
+
+ $str .= '';
+ $str .= '' . $realFileShort. '' . ' | ';
+ $str .= '' . $fileCoverage['total'] . " | ";
+ $str .= '' . $fileCoverage['covered'] . " | ";
+ $str .= '' . $fileCoverage['uncovered'] . " | ";
+ $str .= '' . ($fileCoverage['covered']+$fileCoverage['uncovered']) . " | ";
+ if($fileCoverage['uncovered'] + $fileCoverage['covered'] == 0) {
+ // If there are no executable lines, assume coverage to be 100%
+ $str .= '100% |
';
+ }
+ else {
+ $str .= ''
+ . round(($fileCoverage['covered']/($fileCoverage['uncovered']
+ + $fileCoverage['covered']))*100.0, 2)
+ . '% | ';
+ }
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeIndexFileGrandTotalPercentage() */
+
+ /**
+ * Writes the grand total for coverage recordings on the index.html
+ *
+ * @return string HTML code for grand total row
+ * @access protected
+ */
+ protected function writeIndexFileGrandTotalPercentage() {
+ $str = "";
+
+ $str .= "
" . $this->heading . "
";
+
+ $str .= ' ';
+ $str .= 'Summary';
+ $str .= '';
+ $str .= 'Overall Code Coverage | ';
+ $str .= '' . $this->getGrandCodeCoveragePercentage() . '% | ';
+
+ $str .= '';
+
+ $str .= 'Total Covered Lines of Code | ';
+ $str .= '' . $this->grandTotalCoveredLines.' | ';
+ $str .= '(' . TOTAL_COVERED_LINES_EXPLAIN . ') | ';
+
+ $str .= ' ';
+
+ $str .= 'Total Missed Lines of Code | ';
+ $str .= '' . $this->grandTotalUncoveredLines.' | ';
+ $str .= '(' . TOTAL_UNCOVERED_LINES_EXPLAIN . ') | ';
+
+ $str .= ' ';
+
+ $str .= 'Total Lines of Code | ';
+ $str .= '' . ($this->grandTotalCoveredLines + $this->grandTotalUncoveredLines) .' | ';
+ $str .= '(' .
+ TOTAL_LINES_OF_CODE_EXPLAIN . ') | ';
+
+ $str .= ' ';
+
+ $str .= 'Total Lines | ';
+ $str .= '' . $this->grandTotalLines.' | ';
+ $str .= '(' . TOTAL_LINES_EXPLAIN . ') | ';
+
+ $str .= ' ';
+
+ $str .= 'Total Files | ';
+ $str .= '' . $this->grandTotalFiles.' | ';
+ $str .= '(' . TOTAL_FILES_EXPLAIN . ') | ';
+
+ $str .= ' ';
+
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeIndexFile() */
+
+ /**
+ * Writes index.html file from all coverage recordings.
+ *
+ * @return boolean FALSE on failure
+ * @access protected
+ */
+ protected function writeIndexFile() {
+ global $util;
+ $str = "";
+ $this->createJSDir();
+ $this->createImagesDir();
+ $this->createStyleDir();
+ $this->htmlFile = $this->outputDir . "/index.html";
+ $indexFile = fopen($this->htmlFile, "w");
+ if(empty($indexFile)) {
+ $this->logger->error("Cannot open file for writing: $this->htmlFile",
+ __FILE__, __LINE__);
+ return false;
+ }
+
+ $strHead = $this->writeIndexFileHeader();
+ if($strHead == false) {
+ return false;
+ }
+ $str .= $this->writeIndexFileTableHead();
+ $str .= ' |
';
+ if(!empty($this->coverageData)) {
+ foreach($this->coverageData as $filename => &$lines) {
+ $realFile = realpath($filename);
+ $fileLink = $this->outputDir . $util->unixifyPath($realFile). ".html";
+ $fileCoverage = $this->markFile($realFile, $fileLink, $lines);
+ if(empty($fileCoverage)) {
+ return false;
+ }
+ $this->recordFileCoverageInfo($fileCoverage);
+ $this->updateGrandTotals($fileCoverage);
+
+ $str .= $this->writeIndexFileTableRow($fileLink, $realFile, $fileCoverage);
+ unset($this->coverageData[$filename]);
+ }
+ }
+ $str .= '';
+ $str .= "
";
+
+ $str .= "Report Generated On: " . $util->getTimeStamp() . " ";
+ $str .= "Generated using Spike PHPCoverage " . $this->recorder->getVersion() . " |
";
+
+ // Get the summary
+ $strSummary = $this->writeIndexFileGrandTotalPercentage();
+
+ // Merge them - with summary on top
+ $str = $strHead . $strSummary . $str;
+
+ $str .= $this->writeIndexFileFooter();
+ fwrite($indexFile, $str);
+ fclose($indexFile);
+ return TRUE;
+ }
+
+ /*}}}*/
+ /*{{{ private function writePhpFileHeader() */
+
+ /**
+ * Write the header for the source file with mark-up
+ *
+ * @param $filename Name of the php file
+ * @return string String containing the HTML for PHP file header
+ * @access private
+ */
+ private function writePhpFileHeader($filename, $fileLink) {
+ $fileLink = $this->makeRelative($fileLink);
+ $str = false;
+ $dir = realpath(dirname(__FILE__));
+ if($dir !== false) {
+ $str = file_get_contents($dir . "/" . $this->header);
+ if($str == false) {
+ return $str;
+ }
+ $str = str_replace("%%filename%%", $filename, $str);
+ // Get the path to parent CSS directory
+ $relativeCssPath = $this->getRelativeOutputDirPath($fileLink);
+ $relativeCssPath .= "/css/" . $this->style;
+ $str = str_replace("%%style%%", $relativeCssPath, $str);
+ }
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ private function writePhpFileFooter() */
+
+ /**
+ * Write the footer for the source file with mark-up
+ *
+ * @return string String containing the HTML for PHP file footer
+ * @access private
+ */
+ private function writePhpFileFooter() {
+ $str = false;
+ $dir = realpath(dirname(__FILE__));
+ if($dir !== false) {
+ $str = file_get_contents($dir . "/" . $this->footer);
+ if($str == false) {
+ return $str;
+ }
+ }
+ return $str;
+ }
+
+ /*}}}*/
+ /*{{{ protected function markFile() */
+
+ /**
+ * Mark a source code file based on the coverage data gathered
+ *
+ * @param $phpFile Name of the actual source file
+ * @param $fileLink Link to the html mark-up file for the $phpFile
+ * @param &$coverageLines Coverage recording for $phpFile
+ * @return boolean FALSE on failure
+ * @access protected
+ */
+ protected function markFile($phpFile, $fileLink, &$coverageLines) {
+ global $util;
+ $fileLink = $util->replaceBackslashes($fileLink);
+ $parentDir = $util->replaceBackslashes(dirname($fileLink));
+ if(!file_exists($parentDir)) {
+ //echo "\nCreating dir: $parentDir\n";
+ $util->makeDirRecursive($parentDir, 0755);
+ }
+ $writer = fopen($fileLink, "w");
+
+ if(empty($writer)) {
+ $this->logger->error("Could not open file for writing: $fileLink",
+ __FILE__, __LINE__);
+ return false;
+ }
+
+ // Get the header for file
+ $filestr = $this->writePhpFileHeader(basename($phpFile), $fileLink);
+
+ // Add header for table
+ $filestr .= '';
+ $filestr .= $this->writeFileTableHead();
+
+ $lineCnt = $coveredCnt = $uncoveredCnt = 0;
+ $parser = new PHPParser();
+ $parser->parse($phpFile);
+ $lastLineType = "non-exec";
+ $fileLines = array();
+ while(($line = $parser->getLine()) !== false) {
+ $line = substr($line, 0, strlen($line)-1);
+ $lineCnt++;
+ $coverageLineNumbers = array_keys($coverageLines);
+ if(in_array($lineCnt, $coverageLineNumbers)) {
+ $lineType = $parser->getLineType();
+ if($lineType == LINE_TYPE_EXEC) {
+ $coveredCnt ++;
+ $type = "covered";
+ }
+ else if($lineType == LINE_TYPE_CONT) {
+ // XDebug might return this as covered - when it is
+ // actually merely a continuation of previous line
+ if($lastLineType == "covered") {
+ unset($coverageLines[$lineCnt]);
+ $type = $lastLineType;
+ }
+ else {
+ if($lineCnt-1 >= 0 && isset($fileLines[$lineCnt-1]["type"])) {
+ if($fileLines[$lineCnt-1]["type"] == "uncovered") {
+ $uncoveredCnt --;
+ }
+ $fileLines[$lineCnt-1]["type"] = $lastLineType = "covered";
+ }
+ $coveredCnt ++;
+ $type = "covered";
+ }
+ }
+ else {
+ $type = "non-exec";
+ $coverageLines[$lineCnt] = 0;
+ }
+ }
+ else if($parser->getLineType() == LINE_TYPE_EXEC) {
+ $uncoveredCnt ++;
+ $type = "uncovered";
+ }
+ else if($parser->getLineType() == LINE_TYPE_CONT) {
+ $type = $lastLineType;
+ }
+ else {
+ $type = "non-exec";
+ }
+ // Save line type
+ $lastLineType = $type;
+ //echo $line . "\t[" . $type . "]\n";
+
+ if(!isset($coverageLines[$lineCnt])) {
+ $coverageLines[$lineCnt] = 0;
+ }
+ $fileLines[$lineCnt] = array("type" => $type, "lineCnt" => $lineCnt, "line" => $line, "coverageLines" => $coverageLines[$lineCnt]);
+ }
+ $this->logger->debug("File lines: ". print_r($fileLines, true),
+ __FILE__, __LINE__);
+ for($i = 1; $i <= count($fileLines); $i++) {
+ $filestr .= $this->writeFileTableRow($fileLines[$i]["type"],
+ $fileLines[$i]["lineCnt"],
+ $fileLines[$i]["line"],
+ $fileLines[$i]["coverageLines"]);
+ }
+ $filestr .= "
";
+ $filestr .= $this->writePhpFileFooter();
+ fwrite($writer, $filestr);
+ fclose($writer);
+ return array(
+ 'filename' => $phpFile,
+ 'covered' => $coveredCnt,
+ 'uncovered' => $uncoveredCnt,
+ 'total' => $lineCnt
+ );
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeFileTableHead() */
+
+ /**
+ * Writes table heading for file details table.
+ *
+ * @return string HTML string representing one table row.
+ * @access protected
+ */
+ protected function writeFileTableHead() {
+ $filestr = "";
+
+ $filestr .= 'Line # | ';
+ $filestr .= 'Frequency | ';
+ $filestr .= 'Source Line | ';
+ return $filestr;
+ }
+
+ /*}}}*/
+ /*{{{ protected function writeFileTableRow() */
+
+ /**
+ * Write a line for file details table.
+ *
+ * @param $color Text color
+ * @param $bgcolor Row bgcolor
+ * @param $lineCnt Line number
+ * @param $line The source code line
+ * @param $coverageLineCnt Number of time the line was executed.
+ * @return string HTML code for a table row.
+ * @access protected
+ */
+ protected function writeFileTableRow($type, $lineCnt, $line, $coverageLineCnt) {
+ $spanstr = "";
+ if($type == "covered") {
+ $spanstr .= '';
+ }
+ else if($type == "uncovered") {
+ $spanstr .= '';
+ }
+ else {
+ $spanstr .= '';
+ }
+
+ if(empty($coverageLineCnt)) {
+ $coverageLineCnt = "";
+ }
+
+ $filestr = '';
+ $filestr .= '' . $spanstr . $lineCnt . ' | ';
+ if(empty($coverageLineCnt)) {
+ $coverageLineCnt = " ";
+ }
+ $filestr .= '' . $spanstr . $coverageLineCnt . ' | ';
+ $filestr .= '' . $spanstr . $this->preserveSpacing($line) . ' | ';
+ $filestr .= "
";
+ return $filestr;
+ }
+
+ /*}}}*/
+ /*{{{ protected function preserveSpacing() */
+
+ /**
+ * Changes all tabs and spaces with HTML non-breakable spaces.
+ *
+ * @param $string String containing spaces and tabs.
+ * @return string HTML string with replacements.
+ * @access protected
+ */
+ protected function preserveSpacing($string) {
+ $string = htmlspecialchars($string);
+ $string = str_replace(" ", " ", $string);
+ $string = str_replace("\t", " ", $string);
+ return $string;
+ }
+
+ /*}}}*/
+ }
+?>
diff --git a/vendor/spikephpcoverage/reporter/css/spikesource.css b/vendor/spikephpcoverage/reporter/css/spikesource.css
new file mode 100644
index 000000000..e299698dc
--- /dev/null
+++ b/vendor/spikephpcoverage/reporter/css/spikesource.css
@@ -0,0 +1,1035 @@
+/* www.spikesource.com style */
+
+/* colors */
+
+.logoBlue{
+ background-color:#0066cc
+}
+.darkBlue{
+ background-color:#003399
+}
+
+.lightBlue {
+
+ background-color:#D2E7FC;
+}
+.faintBlue{
+ background-color:#efefef;
+}
+/* Overload html tags */
+body{
+ background-color:#ffffff;
+ font-size:11px;
+ font-family:Arial,Helvetica,sans-serif;
+ margin:0px;
+ color:#222222;
+}
+
+
+/* major page sections; containers are have "Box" suffix */
+
+#pageBox{
+ width:800px;
+ margin-left:20px;
+}
+#navBox{
+ width:800px
+}
+#contentBox{
+ width:800px;
+ margin-top:10px;
+}
+
+/* spikesource.com elements */
+
+#leftBox{
+ width:600px;
+ padding-left:2px;
+ font-size:11px;
+ color:#222222;
+ line-height:14px
+}
+#rightBox{
+ width:190px;
+ margin-bottom:5px;
+ margin-left:15px
+}
+#footerBox{
+ border-top:2px solid #0066cc;
+ margin-top:15px;
+ width:800px;
+ height:22px;
+}
+
+#leftNav {
+ width:165px;
+ margin-right:10px;
+}
+#leftRule {
+ width:175px;
+ border-right:1px solid #000000;
+}
+
+#rightContent {
+ margin-left:15px;
+ width:600px;
+}
+#userInfo {
+ height:20px;
+ width:800px;
+}
+
+
+.navCell{
+ height:31px;
+ border-left:1px solid #000000
+}
+
+.copyright{
+ font-family:arial,sans-serif;
+ color:#666666;
+ font-size:10px;
+ font-weight:bold
+}
+
+/* Convention:Do not overload html default styles - use spike[sometag] */
+h1,.spikeh1{
+ font-size:17px;
+ font-family:Arial,Helvetica,sans-serif;
+ font-weight:bold;
+ color:#000000;
+ margin-bottom:4px;
+ margin-top:0px;
+}
+h2,.spikeh2{
+ font-size:14px;
+ font-family:Arial,Helvetica,sans-serif;
+ font-weight:bold;
+ color:#003399;
+ margin-bottom:8px
+}
+h3,.spikeh3{
+ font-size:12px;
+ font-family:Arial,Helvetica,sans-serif;
+ font-weight:bold;
+ color:#444444;
+ margin-bottom:0px
+}
+.spikep{
+ margin-bottom:10px;
+ margin-top:5px;
+ padding-left:2px;
+ font-size:11px;
+ line-height:15px;
+ width:90%;
+}
+p{
+ margin-bottom:10px;
+ margin-top:5px;
+ padding-left:2px;
+ font-size:11px;
+ line-height:15px;
+}
+.emph{
+ font-style:oblique
+}
+ul,.spikeul{
+ margin-left:22px;
+ padding:0px;
+ margin-top:5px;
+}
+li,.spikeli{
+ list-style-position:outside;
+ line-height:14px;
+ font-size:11px;
+ margin-top:4px;
+ margin-left:2px;
+}
+
+
+.emphasis{
+ font-size:13px;
+ font-family:Arial,Helvetica,sans-serif;
+ font-weight:bold;
+ color:#666666
+}
+
+
+.rightHeader{
+ font-size:14px;
+ font-family:Arial,Helvetica,sans-serif;
+ color:#222222;
+ font-weight:bold;
+ padding-bottom:2px;
+}
+
+.blueRuleBox{
+ width:175px;
+ border-bottom:2px solid #0066cc;
+ margin-left:15px;
+}
+
+.rightRule{
+ border-left:1px solid #000000;
+}
+.rightIndent{
+ width:178px;
+ margin-left:15px;
+ font-size:11px;
+ color:#222222;
+ line-height:1.3em;
+ margin-top:7px;
+ text-align:left;
+ margin-bottom:5px
+}
+
+.newsHeader{
+ width:178px;
+ margin-left:15px;
+ font-size:12px;
+ color:#222222;
+ margin-bottom:2px;
+ margin-top:6px;
+
+}
+ul.newsBox{
+ margin-left:30px;
+ padding-bottom:5px
+}
+
+
+.content_small{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:10px;
+ color:#444444
+}
+.content{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#444444
+}
+.content_gray{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#000000;
+ background-color:#e9e9e9
+}
+.content_error{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#0066cc;
+ font-weight:bold
+}
+.contentError{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#990000;
+ font-weight:bold;
+}
+.content_required{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#0066cc
+}
+.content_bold {
+ font-size:11px;
+ font-weight:bold;
+}
+/* Tables */
+.spikeDataTable {
+ border-top:1px solid #5d6c7b;
+ border-right:1px solid #5d6c7b;
+ border-left:1px solid #5d6c7b;
+}
+
+/* DataTable headings and cell styles */
+.spikeDataTableHeadRight {
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ text-align:right;
+ padding-right:5px;
+}
+.spikeDataTableHeadLeft {
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ text-align:left;
+ padding-left:5px;
+}
+.spikeDataTableHeadCenter {
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.spikeDataTableSubHeadRight {
+ font-size:11px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ border-top:2px solid #ffffff;
+ text-align:right;
+ padding-right:5px;
+}
+.spikeDataTableSubHeadLeft {
+ font-size:11px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ border-top:2px solid #ffffff;
+ text-align:left;
+ padding-left:5px;
+}
+
+.spikeDataTableSubHeadCenter {
+ font-size:11px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-right:2px solid #ffffff;
+ border-top:2px solid #ffffff;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+.spikeDataTableSubHeadLeftLast{
+ font-size:11px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-top:2px solid #ffffff;
+ text-align:left;
+ padding-left:5px;
+}
+.spikeDataTableSubHeadCenterLast {
+ font-size:11px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-top:2px solid #ffffff;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+}
+.spikeDataTableHeadRightLast {
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ text-align:right;
+ padding-right:5px;
+}
+
+.spikeDataTableHeadLeftLast {
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ text-align:left;
+ padding-left:5px;
+}
+.spikeDataTableHeadCenterLast {
+ background-color:#d2e7fc;
+ font-size:12px;
+ font-weight:bold;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+}
+
+
+
+.spikeDataTableCellRight {
+ font-size:11px;
+ text-align:right;
+ border-bottom:1px solid #5d6c7b;
+ padding-right:5px;
+}
+.spikeDataTableCellRightBorder {
+ font-size:11px;
+ text-align:right;
+ border-bottom:1px solid #5d6c7b;
+ border-right:1px solid #cccccc;
+ padding-right:5px;
+}
+.spikeDataTableCellLeft {
+ font-size:11px;
+ text-align:left;
+ border-bottom:1px solid #5d6c7b;
+ padding-left:5px;
+}
+.spikeDataTableCellLeftBorder {
+ font-size:11px;
+ text-align:left;
+ border-bottom:1px solid #5d6c7b;
+ border-right:1px solid #cccccc;
+ padding-left:5px;
+}
+.spikeDataTableCellCenter {
+ font-size:11px;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+ border-bottom:1px solid #5d6c7b;
+}
+.spikeDataTableCellCenterBorder {
+ font-size:11px;
+ text-align:center;
+ margin-left:auto;
+ margin-right:auto;
+ border-bottom:1px solid #5d6c7b;
+ border-right:1px solid #cccccc;
+}
+/* vertical table */
+.spikeVerticalTable {
+ border:1px solid #5d6c7b;
+}
+.spikeVerticalTableHead {
+
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ border-bottom:2px solid #ffffff;
+ text-align:right;
+ padding-right:5px;
+ vertical-align:top;
+}
+
+.spikeVerticalTableHeadLast {
+
+ font-size:12px;
+ font-weight:bold;
+ background-color:#d2e7fc;
+ text-align:right;
+ padding-left:5px;
+ vertical-align:top;
+}
+
+.spikeVerticalTableCell {
+ font-size:11px;
+ text-align:left;
+ border-bottom:1px solid #5d6c7b;
+ padding-left:5px;
+ vertical-align:top;
+}
+.spikeVerticalTableCellBold {
+ font-weight:bold;
+ font-size:11px;
+ text-align:left;
+ border-bottom:1px solid #5d6c7b;
+ padding-left:5px;
+ vertical-align:top;
+}
+.spikeVerticalTableCellLast {
+ font-size:11px;
+ text-align:left;
+ padding-left:5px;
+ vertical-align:top;
+}
+
+
+/* Top navigation */
+#navbar {
+ height:80px;
+ border-bottom:2px solid #0066cc;
+ width:642px;
+}
+.navText{
+ padding-left:5px;
+ font-size:13px;
+ color:#000000;
+ font-weight:bold;
+ letter-spacing:1px;
+ text-decoration:none
+}
+
+.navText:link,.navText:visited{
+ color:#0066cc
+}
+.navText:hover{
+ color:#000000
+}
+.navTextOn {
+ color:#000000;
+ padding-left:5px;
+ font-size:13px;
+ font-weight:bold;
+ letter-spacing:1px;
+ text-decoration:none
+}
+/* left side navigation */
+.parent {
+ margin-left:15px;
+ width:160px;
+ font-weight: bold;
+ font-size: 11px;
+ font-family: Arial, Helvetica, sans-serif;
+ text-decoration:none;
+ padding-bottom:5px;
+ color:#0066cc;
+ vertical-align:top;
+}
+
+.parentChild {
+ margin-left:0px;
+ width:175px;
+ font-weight: bold;
+ font-size: 11px;
+ font-family: Arial, Helvetica, sans-serif;
+ text-decoration:none;
+ padding-bottom:5px;
+ color:#0066cc;
+ vertical-align:top;
+}
+.submenu {
+ color:#000000;
+ width:145px;
+ text-decoration: none;
+ padding-top:1px;
+ padding-bottom:1px;
+ padding-left:25px;
+
+}
+
+
+.submenu a {
+ white-space:normal;
+}
+
+
+.child {
+ padding-left:0px;
+ width:175px;
+ font-weight: bold;
+ font-size: 11px;
+ text-decoration:none;
+ display:none;
+ padding-bottom:2px;
+}
+
+
+.menuImage {
+ margin-right:6px;
+}
+.submenu a:visited {color: #0066cc; text-decoration:none; border:0px;}
+.submenu a:link {color: #0066cc; text-decoration: none; border:0px;}
+.submenu a:hover { color: #000000; text-decoration: none ; border:0px;}
+
+.parentChild a:visited {
+ color: #0066cc;
+ text-decoration:none;
+}
+.parentChild a:link {
+ color: #0066cc;
+ text-decoration:none;
+}
+.parentChild a:hover {
+ color: #000000;
+ text-decoration:none
+}
+.parent a:visited {
+ color: #0066cc;
+ text-decoration:none;
+}
+.parent a:link {
+ color: #0066cc;
+ text-decoration:none;
+}
+.parent a:hover {
+ color: #000000;
+ text-decoration:none
+}
+
+
+/* links */
+.footerlink:link, .footerlink:visited{
+ color:#003399;
+ font-size:10px;
+ text-decoration:none
+}
+
+.footerlink:hover{
+ color:#000000;
+ text-decoration:underline
+}
+.contentlink{
+ font-family:arial,sans-serif;
+ font-size:11px;
+}
+.contentlink:visited, .contentlink:link {
+ color:#0055bb;
+ text-decoration:none;
+}
+.contentlink:hover{
+ color:#000000;
+ text-decoration:underline
+}
+.contentlinkBold{
+ font-family:arial,sans-serif;
+ font-size:11px;
+ font-weight:bold;
+}
+.contentlinkBold:visited, .contentlinkBold:link {
+ color:#0055bb;
+ text-decoration:none;
+}
+.contentlinkBold:hover{
+ color:#000000;
+ text-decoration:underline
+}
+a.headerlink{
+ color:#000000;
+ text-decoration:underline;
+
+}
+a.headerlink:hover {
+ text-decoration:underline;
+}
+a.externalLink, a.tablelink {
+ color:#000000;
+ text-decoration:none;
+}
+a.externalLink:link,a.tablelink:link {
+ color:#0055bb;
+}
+a.externalLink:visited,a.tablelink:visited {
+ color:#0055bb;
+}
+a.externalLink:hover,a.tablelink:hover {
+ color:#000000;
+ text-decoration:underline;
+}
+
+a.tablelinkBold:link {
+ font-weight:bold;
+ text-decoration:none;
+ color:#0055bb;
+}
+a.tablelinkBold:visited {
+ font-weight:bold;
+ text-decoration:none;
+ color:#0066cc;
+}
+a.tablelinkBold:hover {
+ font-weight:bold;
+ color:#000000;
+ text-decoration:underline;
+}
+.blueHeader,.greyHeader{
+ font-size:12px;
+ font-family:Arial,Helvetica,sans-serif;
+ color:#0066cc;
+ font-weight:bold
+}
+
+/* form layout */
+.spikeForm{
+ vertical-align:top;
+ margin:0px;
+ padding:0px;
+ border:1px solid #5d6c7b;
+}
+.spikeButton {
+ padding-right:10px;
+}
+.required {
+ margin-bottom:0px;
+ text-align:right;
+}
+.formRule{
+ border-left:1px solid #000000;
+ padding-top:0px;
+ margin-left:0px;
+}
+
+
+.formHeader{
+ padding-left:20px;
+ height: 22px;
+ background-color:#D2E7FC;
+ color: #000000;
+ font-size:12px;
+ font-weight:bold;
+}
+.formLabel{
+ height:14px;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+ color:#000000;
+ text-align:right;
+ font-weight:bold;
+ white-space:nowrap;
+}
+.formLabelL {
+ font-size:11px;
+ font-weight:bold;
+ color:#000000;
+ white-space:nowrap;
+}
+.formDirection {
+ padding-left:20px;
+ height: 20px;
+ background-color:#ededed;
+ color: #000000;
+ font-size:11px;
+}
+.formNote {
+ color:#333333;
+ font-size:10px;
+ padding-left:2px;
+}
+.label,.element{
+ font-family:Arial,Helvetica,sans-serif;
+ color:#000000;
+ font-size:11px;
+}
+.checkLabel{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:10px;
+ color:#000000;
+ font-weight:bold;
+ margin-right:2px;
+ vertical-align:middle;
+}
+.button{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:10px;
+ color:#000000;
+ background-color:#e9e9e9;
+ border:1px solid #666666
+}
+
+.asterickR{
+ margin-right:2px
+}
+.asterickL{
+ margin-left:2px
+}
+
+.blueButton{
+ font-family:arial,tahoma,sans-serif;
+ font-size:11px;
+ font-weight:bold;
+ color:#ffffff;
+ background-color:#0066cc;
+ border-style:none;
+ border:none;
+ margin:0px;
+ padding:1px;
+}
+
+/* Defines input boxes,textareas,and input checkboxes */
+.textfield{
+ margin-left:2px;
+ border:1px solid #3d86ce;
+ background-color:#E7F1FA;
+ font-size:11px;
+ color:#333333
+}
+input{
+ margin:0px;
+ padding:0px
+}
+.checkbox {
+ margin-right:2px;
+ margin-left:0px;
+}
+.selectMenu{
+ height:16px;
+ margin-left:4px;
+ background-color:#ffffff;
+ font-size:11px;
+ color:#333333
+}
+.optionElem{
+ font-size:11px;
+ color:#333333;
+ padding-bottom:2px
+}
+.multiBox{
+ height:50px;
+ font-size:11px
+}
+
+/* Required by HTML_QuickForm module */
+.errors{
+ font-family:Arial,Helvetica,sans-serif;
+ color:#990000;
+ font-weight:bold
+}
+.note{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:9px;
+ color:#000000
+}
+/* Job Page styles */
+
+.jobTitle {
+ font-family: Arial, Helvetica, sans-serif;
+ font-size:11px;
+ text-decoration:none;
+ line-height:12px;
+ font-weight:bold;
+ color:#0066cc;
+}
+
+a.jobTitle:link {
+ color:#0066cc;
+
+}
+
+a.jobTitle:visited {
+ color:#0066cc;
+
+}
+
+a.jobTitle:hover {
+ color:#000000;
+ text-decoration:underline;
+
+}
+
+.jobTable {
+ margin-bottom:20px;
+}
+
+.jobTable td {
+ padding-left:4px;
+}
+
+.jobTable .jobTitle {
+ font-family:Verdana, Arial, Helvetica, sans-serif;
+ font-weight:bold;
+ color:#ffffff;
+ background-color:#0066cc;
+ /*background-color:#D2E7FC; */
+ font-size:12px;
+ height:20px;
+
+}
+p.jobDescription {
+ margin-bottom:5px;
+ margin-top:2px;
+}
+
+
+
+/* old stuff */
+
+.loginTable{
+ width:250px;
+ padding-bottom:2px;
+ padding-top:10px
+}
+.category_sublink:hover,.boxtitlelink:hover,.category_mainlink:hover{
+ color:#990000;
+ text-decoration:underline
+}
+.spacer,#betaTable{
+ background-color:#ffffff
+}
+.borderedbg{
+ background-color:#ffffff;
+ font-family:arial;
+ font-size:10px;
+ color:#333333
+}
+table.bordered{
+ background-color:#999999
+}
+td.boxoff{
+ background-color:#ffffff;
+ color:#000000;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif
+}
+td.boxon{
+ background-color:#eeeeee;
+ color:#000000;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif
+}
+.pagetitle{
+ font-family:arial;
+ font-weight:bold;
+ font-size:18pt;
+ color:#333333;
+ border-bottom:1px solid #999999
+}
+.boxtitlelink{
+ color:#336699;
+ font-size:9pt;
+ font-family:tahoma,verdana,arial;
+ text-decoration:none
+}
+.sectiontitle{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:10pt;
+ color:#990000;
+ font-weight:bold
+}
+.subtitle{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11pt;
+ font-weight:bold;
+ color:#333333;
+ border-bottom:1px dashed #000000
+}
+.subcategory{
+ background-color:#eeeeee;
+ color:#000000;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif;
+ border-left:5px solid #cccccc
+}
+.subcategory2{
+ background-color:#d9d6c5;
+ color:#000000;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif;
+ border-left:5px solid #cccccc
+}
+.row_title{
+ background-color:#999999;
+ color:#ffffff;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt;
+ font-weight:bold
+}
+.row_category{
+ background-color:#1D97C3;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:10pt;
+ font-weight:bold
+}
+.row1{
+ color:#000000;
+ background-color:#dadada;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt
+}
+.row2{
+ color:#000000;
+ background-color:#e9e9e9;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt
+}
+.row3{
+ color:#990000;
+ background-color:#e9e9e9;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt
+}
+.row4{
+ color:#990000;
+ background-color:#ffffff;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt
+}
+.rowsection{
+ color:#000000;
+ background-color:#f9f9f9;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:11px;
+}
+.category{
+ background-color:#ffffff;
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:9pt
+}
+.category_mainlink{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:9pt;
+ color:#336699;
+ font-family:Arial,Helvetica,sans-serif;
+ text-decoration:underline;
+ font-weight:bold
+}
+.category_sublink{
+ font-family:Arial,Helvetica,sans-serif;
+ font-size:8pt;
+ color:#336699;
+ font-family:Arial,Helvetica,sans-serif;
+ text-decoration:underline
+}
+.boxed_sectionheader{
+ font-family:trebuchet MS,verdana,arial;
+ font-size:11pt;
+ font-weight:bold;
+ color:#333366;
+ background-color:#e9e9e9
+}
+.dotted_sectionheader{
+ font-family:trebuchet MS,verdana,arial;
+ font-size:11pt;
+ font-weight:bold;
+ color:#333366;
+ background-color:#ffffff
+}
+.tab_on{
+ background-color:#ffffff;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif;
+ border-left:1px solid #666666;
+ border-right:1px solid #666666;
+ border-top:1px solid #666666
+}
+.tab_off{
+ background-color:#e9e9e9;
+ font-size:8pt;
+ font-family:Arial,Helvetica,sans-serif;
+ border-left:1px solid #666666;
+ border-right:1px solid #666666;
+ border-bottom:1px solid #666666;
+ border-top:1px solid #666666
+}
+.tab_spacer{
+ background-color:#ffffff;
+ border-bottom:1px solid #666666
+}
+.tab_content{
+ background-color:#ffffff;
+ border-left:1px solid #666666;
+ border-right:1px solid #666666;
+ border-bottom:1px solid #666666
+}
+
+/* PHPCoverage Specific */
+
+.emphasis {
+ font-size:12px;
+ font-weight:bold;
+ color:#222222;
+}
+.note {
+ vertical-align:text-bottom
+}
+.coverageDetailsHead {
+ border-right:2px solid #eeeeee;
+ background-color:#C0CEDC;
+ color:#000000;
+ font-size:12px;
+ font-weight:bold;
+ white-space:nowrap;
+}
+.coverageDetails {
+ color:#666666;
+ font-size:13px;
+ border-right:1px solid #A2AFBC
+}
+.coverageDetailsCode {
+ font-weight:normal;
+ color:#666666;
+ font-size:13px;
+}
+.codeExecuted {
+ color:#003399;
+ font-weight:bold;
+}
+.codeMissed {
+ color:#990000;
+ font-weight:bold;
+}
diff --git a/vendor/spikephpcoverage/reporter/html/footer.html b/vendor/spikephpcoverage/reporter/html/footer.html
new file mode 100644
index 000000000..9b66de5e1
--- /dev/null
+++ b/vendor/spikephpcoverage/reporter/html/footer.html
@@ -0,0 +1,6 @@
+
+
+
+
+