• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPOffice / PhpProject / 28034263091

23 Jun 2026 02:41PM UTC coverage: 96.915% (-2.3%) from 99.211%
28034263091

Pull #56

github

web-flow
Merge 7c42a59d1 into 81e4472af
Pull Request #56: Add HTML writer with frappe-gantt chart (#15)

0 of 27 new or added lines in 1 file covered. (0.0%)

1131 of 1167 relevant lines covered (96.92%)

2.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/PhpProject/Writer/HTML.php
1
<?php
2

3
/**
4
 * This file is part of PHPProject - A pure PHP library for reading and writing
5
 * project management files.
6
 *
7
 * PHPProject is free software distributed under the terms of the GNU Lesser
8
 * General Public License version 3 as published by the Free Software Foundation.
9
 *
10
 * For the full copyright and license information, please read the LICENSE
11
 * file that was distributed with this source code. For the full list of
12
 * contributors, visit https://github.com/PHPOffice/PHPProject/contributors.
13
 *
14
 * @link        https://github.com/PHPOffice/PHPProject
15
 * @copyright   2009-2014 PHPProject contributors
16
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
17
 */
18

19
declare(strict_types=1);
20

21
namespace PhpOffice\PhpProject\Writer;
22

23
use PhpOffice\PhpProject\PhpProject;
24
use PhpOffice\PhpProject\Task;
25

26
/**
27
 * HTML writer (Gantt chart rendered with frappe-gantt)
28
 */
29
class HTML implements WriterInterface
30
{
31
    /**
32
     * PHPProject object
33
     *
34
     * @var PhpProject
35
     */
36
    protected $phpProject;
37

38
    /**
39
     * Create a new HTML writer
40
     *
41
     * @param PhpProject $phpProject
42
     */
43
    public function __construct(PhpProject $phpProject)
44
    {
NEW
45
        $this->phpProject = $phpProject;
×
46
    }
47

48
    /**
49
     * @param string $pFilename
50
     * @throws \Exception
51
     */
52
    public function save(string $pFilename): void
53
    {
54
        // Tasks (frappe-gantt JS objects)
NEW
55
        $tasks = '';
×
NEW
56
        foreach ($this->phpProject->getAllTasks() as $task) {
×
NEW
57
            $tasks .= $this->writeTask($task);
×
58
        }
59

60
        // Full HTML page : header + tasks + footer
NEW
61
        $content = $this->writeHeader() . $tasks . $this->writeFooter();
×
62

63
        // Writing the HTML page in file
NEW
64
        if (file_exists($pFilename) && !is_writable($pFilename)) {
×
NEW
65
            throw new \Exception("Could not open file $pFilename for writing.");
×
66
        }
NEW
67
        $fileHandle = fopen($pFilename, 'wb+');
×
NEW
68
        fwrite($fileHandle, $content);
×
NEW
69
        fclose($fileHandle);
×
70
    }
71

72
    /**
73
     * Generate the HTML header : page skeleton, frappe-gantt includes and the
74
     * opening of the tasks array.
75
     *
76
     * @return string
77
     */
78
    protected function writeHeader(): string
79
    {
NEW
80
        return <<<'HTML'
×
81
<!DOCTYPE html>
82
<html lang="en">
83
<head>
84
    <meta charset="UTF-8">
85
    <title>PhpProject</title>
86
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.css">
87
</head>
88
<body>
89
    <div id="gantt"></div>
90
    <script src="https://cdn.jsdelivr.net/npm/frappe-gantt/dist/frappe-gantt.umd.js"></script>
91
    <script>
92
        let tasks = [
93

NEW
94
HTML;
×
95
    }
96

97
    /**
98
     * Generate the JS object for one task (frappe-gantt format).
99
     *
100
     * @param Task $task
101
     * @return string
102
     */
103
    protected function writeTask(Task $task): string
104
    {
NEW
105
        $data = array(
×
NEW
106
            'id' => (string) $task->getIndex(),
×
NEW
107
            'name' => $task->getName(),
×
NEW
108
        );
×
NEW
109
        if ($task->getStartDate() !== null) {
×
NEW
110
            $data['start'] = date('Y-m-d', $task->getStartDate());
×
111
        }
NEW
112
        if ($task->getEndDate() !== null) {
×
NEW
113
            $data['end'] = date('Y-m-d', $task->getEndDate());
×
114
        }
NEW
115
        $data['progress'] = (int) (($task->getProgress() ?? 0) * 100);
×
116

NEW
117
        $line = '            ' . (string) json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . ",\n";
×
118

119
        // Sub-tasks (recursive)
NEW
120
        foreach ($task->getTasks() as $taskChild) {
×
NEW
121
            $line .= $this->writeTask($taskChild);
×
122
        }
123

NEW
124
        return $line;
×
125
    }
126

127
    /**
128
     * Generate the HTML footer : close the tasks array, instantiate the Gantt
129
     * chart and close the page.
130
     *
131
     * @return string
132
     */
133
    protected function writeFooter(): string
134
    {
NEW
135
        return <<<'HTML'
×
136
        ];
137
        let gantt = new Gantt("#gantt", tasks);
138
    </script>
139
</body>
140
</html>
NEW
141
HTML;
×
142
    }
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc