1<?php
2/*
3 * This file is part of the php-code-coverage package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\CodeCoverage\Report\Html;
12
13use SebastianBergmann\CodeCoverage\CodeCoverage;
14use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
15use SebastianBergmann\CodeCoverage\RuntimeException;
16
17/**
18 * Generates an HTML report from a code coverage object.
19 */
20class Facade
21{
22    /**
23     * @var string
24     */
25    private $templatePath;
26
27    /**
28     * @var string
29     */
30    private $generator;
31
32    /**
33     * @var int
34     */
35    private $lowUpperBound;
36
37    /**
38     * @var int
39     */
40    private $highLowerBound;
41
42    /**
43     * Constructor.
44     *
45     * @param int    $lowUpperBound
46     * @param int    $highLowerBound
47     * @param string $generator
48     */
49    public function __construct($lowUpperBound = 50, $highLowerBound = 90, $generator = '')
50    {
51        $this->generator      = $generator;
52        $this->highLowerBound = $highLowerBound;
53        $this->lowUpperBound  = $lowUpperBound;
54        $this->templatePath   = __DIR__ . '/Renderer/Template/';
55    }
56
57    /**
58     * @param CodeCoverage $coverage
59     * @param string       $target
60     */
61    public function process(CodeCoverage $coverage, $target)
62    {
63        $target = $this->getDirectory($target);
64        $report = $coverage->getReport();
65        unset($coverage);
66
67        if (!isset($_SERVER['REQUEST_TIME'])) {
68            $_SERVER['REQUEST_TIME'] = time();
69        }
70
71        $date = date('D M j G:i:s T Y', $_SERVER['REQUEST_TIME']);
72
73        $dashboard = new Dashboard(
74            $this->templatePath,
75            $this->generator,
76            $date,
77            $this->lowUpperBound,
78            $this->highLowerBound
79        );
80
81        $directory = new Directory(
82            $this->templatePath,
83            $this->generator,
84            $date,
85            $this->lowUpperBound,
86            $this->highLowerBound
87        );
88
89        $file = new File(
90            $this->templatePath,
91            $this->generator,
92            $date,
93            $this->lowUpperBound,
94            $this->highLowerBound
95        );
96
97        $directory->render($report, $target . 'index.html');
98        $dashboard->render($report, $target . 'dashboard.html');
99
100        foreach ($report as $node) {
101            $id = $node->getId();
102
103            if ($node instanceof DirectoryNode) {
104                if (!file_exists($target . $id)) {
105                    mkdir($target . $id, 0777, true);
106                }
107
108                $directory->render($node, $target . $id . '/index.html');
109                $dashboard->render($node, $target . $id . '/dashboard.html');
110            } else {
111                $dir = dirname($target . $id);
112
113                if (!file_exists($dir)) {
114                    mkdir($dir, 0777, true);
115                }
116
117                $file->render($node, $target . $id . '.html');
118            }
119        }
120
121        $this->copyFiles($target);
122    }
123
124    /**
125     * @param string $target
126     */
127    private function copyFiles($target)
128    {
129        $dir = $this->getDirectory($target . 'css');
130        copy($this->templatePath . 'css/bootstrap.min.css', $dir . 'bootstrap.min.css');
131        copy($this->templatePath . 'css/nv.d3.min.css', $dir . 'nv.d3.min.css');
132        copy($this->templatePath . 'css/style.css', $dir . 'style.css');
133
134        $dir = $this->getDirectory($target . 'fonts');
135        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.eot', $dir . 'glyphicons-halflings-regular.eot');
136        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.svg', $dir . 'glyphicons-halflings-regular.svg');
137        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.ttf', $dir . 'glyphicons-halflings-regular.ttf');
138        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff', $dir . 'glyphicons-halflings-regular.woff');
139        copy($this->templatePath . 'fonts/glyphicons-halflings-regular.woff2', $dir . 'glyphicons-halflings-regular.woff2');
140
141        $dir = $this->getDirectory($target . 'js');
142        copy($this->templatePath . 'js/bootstrap.min.js', $dir . 'bootstrap.min.js');
143        copy($this->templatePath . 'js/d3.min.js', $dir . 'd3.min.js');
144        copy($this->templatePath . 'js/holder.min.js', $dir . 'holder.min.js');
145        copy($this->templatePath . 'js/html5shiv.min.js', $dir . 'html5shiv.min.js');
146        copy($this->templatePath . 'js/jquery.min.js', $dir . 'jquery.min.js');
147        copy($this->templatePath . 'js/nv.d3.min.js', $dir . 'nv.d3.min.js');
148        copy($this->templatePath . 'js/respond.min.js', $dir . 'respond.min.js');
149    }
150
151    /**
152     * @param string $directory
153     *
154     * @return string
155     *
156     * @throws RuntimeException
157     */
158    private function getDirectory($directory)
159    {
160        if (substr($directory, -1, 1) != DIRECTORY_SEPARATOR) {
161            $directory .= DIRECTORY_SEPARATOR;
162        }
163
164        if (is_dir($directory)) {
165            return $directory;
166        }
167
168        if (@mkdir($directory, 0777, true)) {
169            return $directory;
170        }
171
172        throw new RuntimeException(
173            sprintf(
174                'Directory "%s" does not exist.',
175                $directory
176            )
177        );
178    }
179}
180