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:

+

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 .= ''; + $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 .= '"; + $str .= '"; + $str .= '"; + $str .= '"; + if($fileCoverage['uncovered'] + $fileCoverage['covered'] == 0) { + // If there are no executable lines, assume coverage to be 100% + $str .= ''; + } + else { + $str .= ''; + } + 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 .= '
File Name LinesCode Coverage
Total Covered Missed Executable
'; + $str .= '' . $realFileShort. '' . '' . $fileCoverage['total'] . "' . $fileCoverage['covered'] . "' . $fileCoverage['uncovered'] . "' . ($fileCoverage['covered']+$fileCoverage['uncovered']) . "100%
' + . round(($fileCoverage['covered']/($fileCoverage['uncovered'] + + $fileCoverage['covered']))*100.0, 2) + . '%
'; + $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 .= "

Summary

'; + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= ''; + + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= ''; + + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= ''; + + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= ''; + + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= ''; + + $str .= ''; + $str .= ''; + $str .= ''; + + $str .= '
Overall Code Coverage ' . $this->getGrandCodeCoveragePercentage() . '%
Total Covered Lines of Code ' . $this->grandTotalCoveredLines.'(' . TOTAL_COVERED_LINES_EXPLAIN . ')
Total Missed Lines of Code ' . $this->grandTotalUncoveredLines.'(' . TOTAL_UNCOVERED_LINES_EXPLAIN . ')
Total Lines of Code ' . ($this->grandTotalCoveredLines + $this->grandTotalUncoveredLines) .'(' . + TOTAL_LINES_OF_CODE_EXPLAIN . ')
Total Lines ' . $this->grandTotalLines.'(' . TOTAL_LINES_EXPLAIN . ')
Total Files ' . $this->grandTotalFiles.'(' . TOTAL_FILES_EXPLAIN . ')
'; + + 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 .= '
"; + + $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 @@ + + + + + + diff --git a/vendor/spikephpcoverage/reporter/html/header.html b/vendor/spikephpcoverage/reporter/html/header.html new file mode 100644 index 000000000..41cdf4e17 --- /dev/null +++ b/vendor/spikephpcoverage/reporter/html/header.html @@ -0,0 +1,17 @@ + + + + Spike PHPCoverage Details: %%filename%% + + + +
+ + + + + + +

Spike PHPCoverage Details: %%filename%%

+
+ diff --git a/vendor/spikephpcoverage/reporter/html/indexfooter.html b/vendor/spikephpcoverage/reporter/html/indexfooter.html new file mode 100644 index 000000000..6d247c293 --- /dev/null +++ b/vendor/spikephpcoverage/reporter/html/indexfooter.html @@ -0,0 +1,18 @@ +
+ + + + + +
+ spacer
+ + + + diff --git a/vendor/spikephpcoverage/reporter/html/indexheader.html b/vendor/spikephpcoverage/reporter/html/indexheader.html new file mode 100644 index 000000000..cb0702083 --- /dev/null +++ b/vendor/spikephpcoverage/reporter/html/indexheader.html @@ -0,0 +1,41 @@ + + + + %%heading%% + + + + + +
+ + + + + + + diff --git a/vendor/spikephpcoverage/reporter/images/arrow_down.gif b/vendor/spikephpcoverage/reporter/images/arrow_down.gif new file mode 100644 index 000000000..582d6f578 Binary files /dev/null and b/vendor/spikephpcoverage/reporter/images/arrow_down.gif differ diff --git a/vendor/spikephpcoverage/reporter/images/arrow_up.gif b/vendor/spikephpcoverage/reporter/images/arrow_up.gif new file mode 100644 index 000000000..fb3980459 Binary files /dev/null and b/vendor/spikephpcoverage/reporter/images/arrow_up.gif differ diff --git a/vendor/spikephpcoverage/reporter/images/spacer.gif b/vendor/spikephpcoverage/reporter/images/spacer.gif new file mode 100644 index 000000000..fc2560981 Binary files /dev/null and b/vendor/spikephpcoverage/reporter/images/spacer.gif differ diff --git a/vendor/spikephpcoverage/reporter/images/spikesource/phpcoverage.gif b/vendor/spikephpcoverage/reporter/images/spikesource/phpcoverage.gif new file mode 100644 index 000000000..bd580a931 Binary files /dev/null and b/vendor/spikephpcoverage/reporter/images/spikesource/phpcoverage.gif differ diff --git a/vendor/spikephpcoverage/reporter/js/sort_spikesource.js b/vendor/spikephpcoverage/reporter/js/sort_spikesource.js new file mode 100644 index 000000000..45222db7a --- /dev/null +++ b/vendor/spikephpcoverage/reporter/js/sort_spikesource.js @@ -0,0 +1,310 @@ +/*---------------------------------------------------\ +| Table Sorter | +|----------------------------------------------------| +| Author: Vinay Srinivasaiah (vsrini@spikesource.com)| +| SpikeSource (http://www.spikesource.com) | +| - DOM 1 based script that makes the table sortable.| +| - Copyright (c) 2004 SpikeSource Inc. | +|---------------------------------------------------*/ +//http://www.w3.org/TR/REC-DOM-Level-1/java-language-binding.html + +var tableBody; +var table2sort; +var imgUp; +var imgDown; + +function TableSorter(table) { + this.table2sort = table; + this.tableBody = this.table2sort.getElementsByTagName("tbody")[0]; + + this.imgUp = document.createElement("img"); + this.imgUp.src = "images/arrow_up.gif"; + this.imgDown = document.createElement("img"); + this.imgDown.src = "images/arrow_down.gif"; +} + +var lastSortCol = -1; +var lastSortOrderAsc = true; +var origChildRows; + +function createImgLink(row, imageSrc) { + var cell = row.cells[0]; + var id = _getInnerText(cell) + "_" + imageSrc; + + imgExpand = document.createElement("img"); + imgExpand.src = "images" + imageSrc + ".gif"; + imgExpand.border="0"; + + imgBlank = document.createElement("img"); + imgBlank.src = "results/images/transdot.gif"; + imgBlank.border="0"; + imgBlank2 = imgBlank.cloneNode(false); + imgBlank3 = imgBlank.cloneNode(false); + + anchorTag = document.createElement("a"); + anchorTag.href="javascript:toggleShowChildren('" + id + "');" + anchorTag.appendChild(imgExpand); + anchorTag.appendChild(imgBlank); + anchorTag.appendChild(imgBlank2); + anchorTag.appendChild(imgBlank3); + anchorTag.id = id; + + cell.id = id + "_cell"; + row.id = id + "_row"; + + cell.insertBefore(anchorTag, cell.firstChild); +} + +TableSorter.prototype.initTable = function () { + this.populateChildRowsMap(); + for (i = 0; i < origChildRows.length; i++) { + if (origChildRows[i].id != "indented_row") { + createImgLink(origChildRows[i], "minus"); + } + } +} + +TableSorter.prototype.collapseAllChildren = function () { + for (i = 0; i < origChildRows.length; i++) { + if (origChildRows[i].id != "indented_row") { + id = _getInnerText(origChildRows[i].cells[0]) + "_" + "minus"; + var anchorTag = document.getElementById(id); + if (anchorTag != null) { + this.togglechildren(id); + } + } + } +} + +TableSorter.prototype.expandAllChildren = function () { + for (i = 0; i < origChildRows.length; i++) { + if (origChildRows[i].id != "indented_row") { + id = _getInnerText(origChildRows[i].cells[0]) + "_" + "plus"; + var anchorTag = document.getElementById(id); + if (anchorTag != null) { + this.togglechildren(id); + } + } + } +} + +TableSorter.prototype.togglechildren = function (id) { + anchorTag = document.getElementById(id); + anchorParent = document.getElementById((id + "_cell")); + anchorParent.removeChild(anchorTag); + row = document.getElementById((id + "_row")); + nextRow = row.nextSibling; + + var addChildren = false; + if (anchorTag.firstChild.src.indexOf("plus") != -1) { + addChildren = true; + createImgLink(row, "minus"); + } else if (anchorTag.firstChild.src.indexOf("minus") != -1) { + addChildren = false; + createImgLink(row, "plus"); + } + for (i = 0; i < origChildRows.length; i++) { + //alert("comparing " + _getInnerText(origChildRows[i].cells[0]) + // + " and " + _getInnerText(row.cells[0])); + if (_getInnerText(origChildRows[i].cells[0]) == _getInnerText(row.cells[0])) { + for (j = i + 1; j < origChildRows.length; j++) { + if (origChildRows[j].id == "indented_row") { + if (addChildren) { + this.tableBody.insertBefore(origChildRows[j], nextRow); + } else { + this.tableBody.removeChild(origChildRows[j]); + } + } else { + // done; + break; + } + } + break; + } + } +} + +TableSorter.prototype.populateChildRowsMap = function () { + var rows = this.tableBody.rows; + origChildRows = new Array(); + var count = 0; + var newRowsCount = 0; + for (i = 0; i < rows.length; i ++) { + if (rows[i].id == "indented_row") { + if (parentRow != null) { + origChildRows[count++] = parentRow; + parentRow = null; + } + origChildRows[count++] = rows[i]; + } else { + parentRow = rows[i]; + } + } +} + +TableSorter.prototype.sort = function (col, type) { + if (lastSortCol != -1) { + sortCell = document.getElementById("sortCell" + lastSortCol); + if (sortCell != null) { + if (lastSortOrderAsc == true) { + sortCell.removeChild(this.imgUp); + } else { + sortCell.removeChild(this.imgDown); + } + } + sortLink = document.getElementById("sortCellLink" + lastSortCol); + if(sortLink != null) { + sortLink.title = "Sort Ascending"; + } + } + + if (lastSortCol == col) { + lastSortOrderAsc = !lastSortOrderAsc; + } else { + lastSortCol = col; + lastSortOrderAsc = true; + } + + var rows = this.tableBody.rows; + var newRows = new Array(); + var parentRow; + + var childRows = new Array(); + var count = 0; + var newRowsCount = 0; + for (i = 0; i < rows.length; i ++) { + if (rows[i].id == "indented_row") { + if (parentRow != null) { + childRows[count++] = parentRow; + parentRow = null; + } + childRows[count++] = rows[i]; + } else { + newRows[newRowsCount++] = rows[i]; + parentRow = rows[i]; + } + } + + // default + sortFunction = sort_caseInsensitive; + if (type == "string") sortFunction = sort_caseSensitive; + if (type == "percentage") sortFunction = sort_numericPercentage; + if (type == "number") sortFunction = sort_numeric; + + newRows.sort(sortFunction); + + if (lastSortOrderAsc == false) { + newRows.reverse(); + } + + for (i = 0; i < newRows.length; i ++) { + this.table2sort.tBodies[0].appendChild(newRows[i]); + var parentRowText = _getInnerText(newRows[i].cells[0]); + var match = -1; + for (j = 0; j < childRows.length; j++) { + var childRowText = _getInnerText(childRows[j].cells[0]); + if (childRowText == parentRowText) { + match = j; + break; + } + } + if (match != -1) { + for (j = match + 1; j < childRows.length; j++) { + if (childRows[j].id == "indented_row") { + this.table2sort.tBodies[0].appendChild(childRows[j]); + } else { + break; + } + } + } + } + + sortCell = document.getElementById("sortCell" + col); + if (sortCell == null) { + } else { + if (lastSortOrderAsc == true) { + sortCell.appendChild(this.imgUp); + } else { + sortCell.appendChild(this.imgDown); + } + } + + sortLink = document.getElementById("sortCellLink" + col); + if (sortLink == null) { + } else { + if (lastSortOrderAsc == true) { + sortLink.title = "Sort Descending"; + } else { + sortLink.title = "Sort Ascending"; + } + } +} + +function sort_caseSensitive(a, b) { + aa = _getInnerText(a.cells[lastSortCol]); + bb = _getInnerText(b.cells[lastSortCol]); + return compareString(aa, bb); +} + +function sort_caseInsensitive(a,b) { + aa = _getInnerText(a.cells[lastSortCol]).toLowerCase(); + bb = _getInnerText(b.cells[lastSortCol]).toLowerCase(); + return compareString(aa, bb); +} + +function sort_numeric(a,b) { + aa = _getInnerText(a.cells[lastSortCol]); + bb = _getInnerText(b.cells[lastSortCol]); + return compareNumber(aa, bb); +} + +function sort_numericPercentage(a,b) { + aa = _getInnerText(a.cells[lastSortCol]); + bb = _getInnerText(b.cells[lastSortCol]); + + var aaindex = aa.indexOf("%"); + var bbindex = bb.indexOf("%"); + + if (aaindex != -1 && bbindex != -1) { + aa = aa.substring(0, aaindex); + bb = bb.substring(0, bbindex); + return compareNumber(aa, bb); + } + + return compareString(aa, bb); +} + +function compareString(a, b) { + if (a == b) return 0; + if (a < b) return -1; + return 1; +} + +function compareNumber(a, b) { + aa = parseFloat(a); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b); + if (isNaN(bb)) bb = 0; + return aa-bb; +} + +function _getInnerText(el) { + if (typeof el == "string") return el; + if (typeof el == "undefined") { return el }; + if (el.innerText) return el.innerText; + var str = ""; + + var cs = el.childNodes; + var l = cs.length; + for (var i = 0; i < l; i++) { + switch (cs[i].nodeType) { + case 1: //ELEMENT_NODE + str += _getInnerText(cs[i]); + break; + case 3: //TEXT_NODE + str += cs[i].nodeValue; + break; + } + } + return str; +} diff --git a/vendor/spikephpcoverage/util/CoverageLogger.php b/vendor/spikephpcoverage/util/CoverageLogger.php new file mode 100644 index 000000000..71119b149 --- /dev/null +++ b/vendor/spikephpcoverage/util/CoverageLogger.php @@ -0,0 +1,103 @@ + +logLevels); $i++) { + if(strcasecmp($this->logLevels[$i], $level) === 0) { + $level = $i; + break; + } + } + } + $this->level = $level; + } + + public function critical($str, $file="", $line="") { + if($this->level >= 0) { + error_log("[CRITICAL] [" . $file . ":" . $line . "] " . $str); + } + } + + public function error($str, $file="", $line="") { + if($this->level >= 1) { + error_log("[ERROR] [" . $file . ":" . $line . "] " . $str); + } + } + + public function warn($str, $file="", $line="") { + if($this->level >= 2) { + error_log("[WARNING] [" . $file . ":" . $line . "] " . $str); + } + } + + public function notice($str, $file="", $line="") { + if($this->level >= 3) { + error_log("[NOTICE] [" . $file . ":" . $line . "] " . $str); + } + } + + public function info($str, $file="", $line="") { + if($this->level >= 4) { + error_log("[INFO] [" . $file . ":" . $line . "] " . $str); + } + } + + public function debug($str, $file="", $line="") { + if($this->level >= 5) { + error_log("[DEBUG] [" . $file . ":" . $line . "] " . $str); + } + } + + public function getLevelName($level) { + return $this->logLevels[$level]; + } + } + + // testing + if(isset($_SERVER["argv"][1]) && $_SERVER["argv"][1] == "__main__") { + $logger = new CoverageLogger(); + for($i = 0; $i < 6; $i++) { + $logger->setLevel($i); + error_log("############## Level now: " . $i); + $logger->debug(""); + $logger->info(""); + $logger->notice(""); + $logger->warn(""); + $logger->error(""); + $logger->critical(""); + } + + error_log("############# With Level Names"); + for($i = 0; $i < 6; $i++) { + $logger->setLevel($logger->getLevelName($i)); + error_log("############## Level now: " . $logger->getLevelName($i)); + $logger->debug(""); + $logger->info("", __FILE__, __LINE__); + $logger->notice(""); + $logger->warn(""); + $logger->error(""); + $logger->critical(""); + } + } +?> diff --git a/vendor/spikephpcoverage/util/Utility.php b/vendor/spikephpcoverage/util/Utility.php new file mode 100644 index 000000000..52fc41f0d --- /dev/null +++ b/vendor/spikephpcoverage/util/Utility.php @@ -0,0 +1,224 @@ + + + * @version $Revision: $ + * @package SpikePHPCoverage_Util + */ + class Utility { + + public static $logger; + + /*{{{ public function getTimeStamp() */ + + /** + * Return the current timestamp in human readable format. + * Thursday March 17, 2005 19:10:47 + * + * @return Readable timestamp + * @access public + */ + public function getTimeStamp() { + $ts = getdate(); + return $ts["weekday"] . " " . $ts["month"] . " " . $ts["mday"] + . ", " . $ts["year"] . " " . sprintf("%02d:%02d:%02d", $ts["hours"], $ts["minutes"], $ts["seconds"]); + } + + /*}}}*/ + /*{{{ public function shortenFilename() */ + + /** + * Shorten the filename to some maximum characters + * + * @param $filename Complete file path + * @param $maxlength=150 Maximum allowable length of the shortened + * filepath + * @return Shortened file path + * @access public + */ + public function shortenFilename($filename, $maxlength=80) { + $length = strlen($filename); + if($length < $maxlength) { + return $filename; + } + + // trim the first few characters + $filename = substr($filename, $length-$maxlength); + // If there is a path separator slash in first n characters, + // trim upto that point. + $n = 20; + $firstSlash = strpos($filename, "/"); + if($firstSlash === false || $firstSlash > $n) { + $firstSlash = strpos($filename, "\\"); + if($firstSlash === false || $firstSlash > $n) { + return "..." . $filename; + } + return "..." . substr($filename, $firstSlash); + } + return "..." . substr($filename, $firstSlash); + } + + /*}}}*/ + /*{{{ public function writeError() */ + + /** + * Write error log if debug is on + * + * @param $str Error string + * @access public + */ + public function writeError($str) { + if(__PHPCOVERAGE_DEBUG) { + error_log($str); + } + } + /*}}}*/ + /*{{{ public function unixifyPath() */ + + /** + * Convert Windows paths to Unix paths + * + * @param $path File path + * @return String Unixified file path + * @access public + */ + public function unixifyPath($path) { + // Remove the drive-letter: + if(strpos($path, ":") == 1) { + $path = substr($path, 2); + } + $path = $this->replaceBackslashes($path); + return $path; + } + + /*}}}*/ + /*{{{ public function replaceBackslashes() */ + + /** + * Convert the back slash path separators with forward slashes. + * + * @param $path Windows path with backslash path separators + * @return String Path with back slashes replaced with forward slashes. + * @access public + */ + public function replaceBackslashes($path) { + $path = str_replace("\\", "/", $path); + return $this->capitalizeDriveLetter($path); + } + /*}}}*/ + /*{{{ public function capitalizeDriveLetter() */ + + /** + * Convert the drive letter to upper case + * + * @param $path Windows path with "c:" + * @return String Path with driver letter capitalized. + * @access public + */ + public function capitalizeDriveLetter($path) { + if(strpos($path, ":") === 1) { + $path = strtoupper(substr($path, 0, 1)) . substr($path, 1); + } + return $path; + } + + /*}}}*/ + /*{{{ public function makeDirRecursive() */ + /** + * Make directory recursively. + * (Taken from: http://aidan.dotgeek.org/lib/?file=function.mkdirr.php) + * + * @param $dir Directory path to create + * @param $mode=0755 + * @return True on success, False on failure + * @access public + */ + public function makeDirRecursive($dir, $mode=0755) { + // Check if directory already exists + if (is_dir($dir) || empty($dir)) { + return true; + } + + // Ensure a file does not already exist with the same name + if (is_file($dir)) { + $this->getLogger()->debug("File already exists: " . $dir, + __FILE__, __LINE__); + return false; + } + + $dir = $this->replaceBackslashes($dir); + + // Crawl up the directory tree + $next_pathname = substr($dir, 0, strrpos($dir, "/")); + if ($this->makeDirRecursive($next_pathname, $mode)) { + if (!file_exists($dir)) { + return mkdir($dir, $mode); + } + } + + return false; + } + /*}}}*/ + /*{{{ public function getOS() */ + /** + * Returns the current OS code + * WIN - Windows, LIN -Linux, etc. + * + * @return String 3 letter code for current OS + * @access public + * @since 0.6.6 + */ + public function getOS() { + return strtoupper(substr(PHP_OS, 0, 3)); + } + /*}}}*/ + /*{{{ public function getTmpDir() */ + + public function getTmpDir() { + global $spc_config; + $OS = $this->getOS(); + switch($OS) { + case "WIN": + return $spc_config['windows_tmpdir']; + default: + return $spc_config['tmpdir']; + } + } + + /*}}}*/ + /*{{{ public function getLogger() */ + + public function getLogger($package=false) { + global $spc_config; + if(!isset($this->logger) || $this->logger == NULL) { + $this->logger =& new CoverageLogger(); + $this->logger->setLevel($spc_config["log_level"]); + } + return $this->logger; + } + + /*}}}*/ + } + $util = new Utility(); + global $util; +?>