5 * Copyright (c) 2009-2011, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 * @package CodeCoverage
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://github.com/sebastianbergmann/php-code-coverage
43 * @since File available since Release 1.1.0
46 if (!defined('T_NAMESPACE')) {
47 define('T_NAMESPACE', 1000);
50 if (!defined('T_TRAIT')) {
51 define('T_TRAIT', 1001);
55 * Renders a PHP_CodeCoverage_Report_Node_File node.
58 * @package CodeCoverage
59 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
60 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
61 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
62 * @version Release: 1.1.1
63 * @link http://github.com/sebastianbergmann/php-code-coverage
64 * @since Class available since Release 1.1.0
66 class PHP_CodeCoverage_Report_HTML_Renderer_File extends PHP_CodeCoverage_Report_HTML_Renderer
81 * @param string $templatePath
82 * @param string $charset
83 * @param string $generator
85 * @param integer $lowUpperBound
86 * @param integer $highLowerBound
87 * @param boolean $highlight
90 public function __construct($templatePath, $charset, $generator, $date, $lowUpperBound, $highLowerBound, $highlight, $yui)
101 $this->highlight = $highlight;
106 * @param PHP_CodeCoverage_Report_Node_File $node
107 * @param string $file
108 * @param string $title
110 public function render(PHP_CodeCoverage_Report_Node_File $node, $file, $title = NULL)
112 if ($title === NULL) {
113 $title = $node->getName();
117 $template = new Text_Template($this->templatePath . 'file.html');
119 $template = new Text_Template(
120 $this->templatePath . 'file_no_yui.html'
124 list($source, $yuiTemplate) = $this->renderSource($node);
128 'items' => $this->renderItems($node),
130 'yuiPanelJS' => $yuiTemplate
134 $this->setCommonTemplateVariables($template, $title, $node);
136 $template->renderTo($file);
140 * @param PHP_CodeCoverage_Report_Node_File $node
143 protected function renderItems(PHP_CodeCoverage_Report_Node_File $node)
145 $template = new Text_Template($this->templatePath . 'file_item.html');
147 $methodItemTemplate = new Text_Template(
148 $this->templatePath . 'method_item.html'
151 $items = $this->renderItemTemplate(
154 'itemClass' => 'coverDirectory',
156 'numClasses' => $node->getNumClasses() + $node->getNumTraits(),
157 'numTestedClasses' => $node->getNumTestedClasses() + $node->getNumTestedTraits(),
158 'numMethods' => $node->getNumMethods(),
159 'numTestedMethods' => $node->getNumTestedMethods(),
160 'linesExecutedPercent' => $node->getLineExecutedPercent(FALSE),
161 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
162 'numExecutedLines' => $node->getNumExecutedLines(),
163 'numExecutableLines' => $node->getNumExecutableLines(),
164 'testedMethodsPercent' => $node->getTestedMethodsPercent(FALSE),
165 'testedMethodsPercentAsString' => $node->getTestedMethodsPercent(),
166 'testedClassesPercent' => $node->getTestedClassesPercent(FALSE),
167 'testedClassesPercentAsString' => $node->getTestedClassesPercent(),
168 'crap' => '<acronym title="Change Risk Anti-Patterns (CRAP) Index">CRAP</acronym>'
172 $items .= $this->renderFunctionItems(
173 $node->getFunctions(), $methodItemTemplate
176 $items .= $this->renderTraitOrClassItems(
177 $node->getTraits(), $template, $methodItemTemplate
180 $items .= $this->renderTraitOrClassItems(
181 $node->getClasses(), $template, $methodItemTemplate
188 * @param array $items
189 * @param Text_Template $template
192 protected function renderTraitOrClassItems(array $items, Text_Template $template, Text_Template $methodItemTemplate)
200 foreach ($items as $name => $item) {
201 $numMethods = count($item['methods']);
202 $numTestedMethods = 0;
204 foreach ($item['methods'] as $method) {
205 if ($method['executedLines'] == $method['executableLines']) {
210 $buffer .= $this->renderItemTemplate(
213 'itemClass' => 'coverDirectory',
216 'numTestedClasses' => $numTestedMethods == $numMethods ? 1 : 0,
217 'numMethods' => $numMethods,
218 'numTestedMethods' => $numTestedMethods,
219 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent(
220 $item['executedLines'],
221 $item['executableLines'],
224 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent(
225 $item['executedLines'],
226 $item['executableLines'],
229 'numExecutedLines' => $item['executedLines'],
230 'numExecutableLines' => $item['executableLines'],
231 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent(
236 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent(
241 'testedClassesPercent' => PHP_CodeCoverage_Util::percent(
242 $numTestedMethods == $numMethods ? 1 : 0,
246 'testedClassesPercentAsString' => PHP_CodeCoverage_Util::percent(
247 $numTestedMethods == $numMethods ? 1 : 0,
251 'crap' => $item['crap']
255 foreach ($item['methods'] as $method) {
256 $buffer .= $this->renderFunctionOrMethodItem(
257 $methodItemTemplate, $method, ' '
266 * @param array $functions
267 * @param Text_Template $template
270 protected function renderFunctionItems(array $functions, Text_Template $template)
272 if (empty($functions)) {
278 foreach ($functions as $function) {
279 $buffer .= $this->renderFunctionOrMethodItem(
288 * @param Text_Template $template
291 protected function renderFunctionOrMethodItem(Text_Template $template, array $item, $indent = '')
293 $numTestedItems = $item['executedLines'] == $item['executableLines'] ? 1 : 0;
295 return $this->renderItemTemplate(
299 '%s<a href="#%d">%s</a>',
302 htmlspecialchars($item['signature'])
305 'numTestedMethods' => $numTestedItems,
306 'linesExecutedPercent' => PHP_CodeCoverage_Util::percent(
307 $item['executedLines'],
308 $item['executableLines'],
311 'linesExecutedPercentAsString' => PHP_CodeCoverage_Util::percent(
312 $item['executedLines'],
313 $item['executableLines'],
316 'numExecutedLines' => $item['executedLines'],
317 'numExecutableLines' => $item['executableLines'],
318 'testedMethodsPercent' => PHP_CodeCoverage_Util::percent(
323 'testedMethodsPercentAsString' => PHP_CodeCoverage_Util::percent(
328 'crap' => $item['crap']
334 * @param PHP_CodeCoverage_Report_Node_File $node
337 protected function renderSource(PHP_CodeCoverage_Report_Node_File $node)
340 $yuiTemplate = new Text_Template(
341 $this->templatePath . 'yui_item.js'
345 $coverageData = $node->getCoverageData();
346 $ignoredLines = $node->getIgnoredLines();
347 $testData = $node->getTestData();
348 list($codeLines, $fillup) = $this->loadFile($node->getPath());
353 foreach ($codeLines as $line) {
356 if (!isset($ignoredLines[$i]) && isset($coverageData[$i])) {
358 $numTests = count($coverageData[$i]);
360 if ($coverageData[$i] === NULL) {
361 $color = 'lineDeadCode';
365 else if ($numTests == 0) {
366 $color = 'lineNoCov';
367 $count = sprintf('%8d', 0);
372 $count = sprintf('%8d', $numTests);
378 foreach ($coverageData[$i] as $test) {
379 switch ($testData[$test]) {
381 $testCSS = ' class=\"testPassed\"';
387 $testCSS = ' class=\"testIncomplete\"';
392 $testCSS = ' class=\"testFailure\"';
397 $testCSS = ' class=\"testError\"';
410 addslashes(htmlspecialchars($test))
415 $header = $numTests . ' tests cover';
417 $header = '1 test covers';
420 $header .= ' line ' . $i;
422 $yuiTemplate->setVar(
431 $yuiPanelJS .= $yuiTemplate->render();
436 '<span class="%s"> %s : ',
443 $_fillup = array_shift($fillup);
446 $line .= str_repeat(' ', $_fillup);
450 '<span class="lineNum" id="container%d"><a name="%d"></a>'.
451 '<a href="#%d" id="line%d">%8d</a> </span>%s%s%s' . "\n",
458 !empty($css) ? $css : ' : ',
459 !$this->highlight ? htmlspecialchars($line) : $line,
460 !empty($css) ? '</span>' : ''
466 return array($lines, $yuiPanelJS);
470 * @param string $file
473 protected function loadFile($file)
475 $buffer = file_get_contents($file);
476 $lines = explode("\n", str_replace("\t", ' ', $buffer));
480 if (count($lines) == 0) {
484 $lines = array_map('rtrim', $lines);
485 $linesLength = array_map('strlen', $lines);
486 $width = max($linesLength);
488 foreach ($linesLength as $line => $length) {
489 $fillup[$line] = $width - $length;
492 if (!$this->highlight) {
493 unset($lines[count($lines)-1]);
494 return array($lines, $fillup);
497 $tokens = token_get_all($buffer);
502 foreach ($tokens as $j => $token) {
503 if (is_string($token)) {
504 if ($token === '"' && $tokens[$j - 1] !== '\\') {
505 $result[$i] .= sprintf(
506 '<span class="string">%s</span>',
508 htmlspecialchars($token)
511 $stringFlag = !$stringFlag;
513 $result[$i] .= sprintf(
514 '<span class="keyword">%s</span>',
516 htmlspecialchars($token)
523 list ($token, $value) = $token;
525 $value = str_replace(
527 array(' ', ' '),
528 htmlspecialchars($value)
531 if ($value === "\n") {
534 $lines = explode("\n", $value);
536 foreach ($lines as $jj => $line) {
544 case T_INLINE_HTML: {
550 case T_DOC_COMMENT: {
615 $result[$i] .= sprintf(
616 '<span class="%s">%s</span>',
623 if (isset($lines[$jj + 1])) {
630 unset($result[count($result)-1]);
632 return array($result, $fillup);