1<?php
2/**
3 * Contains PhpMyAdmin\Plugins\Schema\Svg\RelationStatsSvg class
4 */
5
6declare(strict_types=1);
7
8namespace PhpMyAdmin\Plugins\Schema\Svg;
9
10use PhpMyAdmin\Plugins\Schema\Dia\TableStatsDia;
11use PhpMyAdmin\Plugins\Schema\Eps\TableStatsEps;
12use PhpMyAdmin\Plugins\Schema\ExportRelationSchema;
13use PhpMyAdmin\Plugins\Schema\Pdf\TableStatsPdf;
14use function in_array;
15use function max;
16use function min;
17use function sprintf;
18
19/**
20 * RelationStatsSvg Relation Schema Class
21 *
22 * Purpose of this class is to generate the SVG XML Document because
23 * SVG defines the graphics in XML format which is used for representing
24 * the database diagrams as vector image. This class actually helps
25 *  in preparing SVG XML format.
26 *
27 * SVG XML is generated by using XMLWriter php extension and this class
28 * inherits ExportRelationSchema class has common functionality added
29 * to this class
30 *
31 * @name Svg_Relation_Schema
32 */
33class SvgRelationSchema extends ExportRelationSchema
34{
35    /** @var TableStatsDia[]|TableStatsEps[]|TableStatsPdf[]|TableStatsSvg[] */
36    private $tables = [];
37
38    /** @var RelationStatsSvg[] Relations */
39    private $relations = [];
40
41    /** @var int|float */
42    private $xMax = 0;
43
44    /** @var int|float */
45    private $yMax = 0;
46
47    /** @var int|float */
48    private $xMin = 100000;
49
50    /** @var int|float */
51    private $yMin = 100000;
52
53    /** @var int */
54    private $tablewidth;
55
56    /**
57     * Upon instantiation This starts writing the SVG XML document
58     * user will be prompted for download as .svg extension
59     *
60     * @see PMA_SVG
61     *
62     * @param string $db database name
63     */
64    public function __construct($db)
65    {
66        parent::__construct($db, new Svg());
67
68        $this->setShowColor(isset($_REQUEST['svg_show_color']));
69        $this->setShowKeys(isset($_REQUEST['svg_show_keys']));
70        $this->setTableDimension(isset($_REQUEST['svg_show_table_dimension']));
71        $this->setAllTablesSameWidth(isset($_REQUEST['svg_all_tables_same_width']));
72
73        $this->diagram->setTitle(
74            sprintf(
75                __('Schema of the %s database - Page %s'),
76                $this->db,
77                $this->pageNumber
78            )
79        );
80        $this->diagram->SetAuthor('phpMyAdmin ' . PMA_VERSION);
81        $this->diagram->setFont('Arial');
82        $this->diagram->setFontSize(16);
83
84        $alltables = $this->getTablesFromRequest();
85
86        foreach ($alltables as $table) {
87            if (! isset($this->tables[$table])) {
88                $this->tables[$table] = new TableStatsSvg(
89                    $this->diagram,
90                    $this->db,
91                    $table,
92                    $this->diagram->getFont(),
93                    $this->diagram->getFontSize(),
94                    $this->pageNumber,
95                    $this->tablewidth,
96                    $this->showKeys,
97                    $this->tableDimension,
98                    $this->offline
99                );
100            }
101
102            if ($this->sameWide) {
103                $this->tables[$table]->width = &$this->tablewidth;
104            }
105            $this->setMinMax($this->tables[$table]);
106        }
107
108        $border = 15;
109        $this->diagram->startSvgDoc(
110            $this->xMax + $border,
111            $this->yMax + $border,
112            $this->xMin - $border,
113            $this->yMin - $border
114        );
115
116        $seen_a_relation = false;
117        foreach ($alltables as $one_table) {
118            $exist_rel = $this->relation->getForeigners($this->db, $one_table, '', 'both');
119            if (! $exist_rel) {
120                continue;
121            }
122
123            $seen_a_relation = true;
124            foreach ($exist_rel as $master_field => $rel) {
125                /* put the foreign table on the schema only if selected
126                * by the user
127                * (do not use array_search() because we would have to
128                * to do a === false and this is not PHP3 compatible)
129                */
130                if ($master_field !== 'foreign_keys_data') {
131                    if (in_array($rel['foreign_table'], $alltables)) {
132                        $this->addRelation(
133                            $one_table,
134                            $this->diagram->getFont(),
135                            $this->diagram->getFontSize(),
136                            $master_field,
137                            $rel['foreign_table'],
138                            $rel['foreign_field'],
139                            $this->tableDimension
140                        );
141                    }
142                    continue;
143                }
144
145                foreach ($rel as $one_key) {
146                    if (! in_array($one_key['ref_table_name'], $alltables)) {
147                        continue;
148                    }
149
150                    foreach ($one_key['index_list'] as $index => $one_field) {
151                        $this->addRelation(
152                            $one_table,
153                            $this->diagram->getFont(),
154                            $this->diagram->getFontSize(),
155                            $one_field,
156                            $one_key['ref_table_name'],
157                            $one_key['ref_index_list'][$index],
158                            $this->tableDimension
159                        );
160                    }
161                }
162            }
163        }
164        if ($seen_a_relation) {
165            $this->drawRelations();
166        }
167
168        $this->drawTables();
169        $this->diagram->endSvgDoc();
170    }
171
172    /**
173     * Output RelationStatsSvg Document for download
174     *
175     * @return void
176     */
177    public function showOutput()
178    {
179        $this->diagram->showOutput($this->getFileName('.svg'));
180    }
181
182    /**
183     * Sets X and Y minimum and maximum for a table cell
184     *
185     * @param TableStatsSvg $table The table
186     *
187     * @return void
188     */
189    private function setMinMax($table)
190    {
191        $this->xMax = max($this->xMax, $table->x + $table->width);
192        $this->yMax = max($this->yMax, $table->y + $table->height);
193        $this->xMin = min($this->xMin, $table->x);
194        $this->yMin = min($this->yMin, $table->y);
195    }
196
197    /**
198     * Defines relation objects
199     *
200     * @see setMinMax,TableStatsSvg::__construct(),
201     *       PhpMyAdmin\Plugins\Schema\Svg\RelationStatsSvg::__construct()
202     *
203     * @param string $masterTable    The master table name
204     * @param string $font           The font face
205     * @param int    $fontSize       Font size
206     * @param string $masterField    The relation field in the master table
207     * @param string $foreignTable   The foreign table name
208     * @param string $foreignField   The relation field in the foreign table
209     * @param bool   $tableDimension Whether to display table position or not
210     *
211     * @return void
212     */
213    private function addRelation(
214        $masterTable,
215        $font,
216        $fontSize,
217        $masterField,
218        $foreignTable,
219        $foreignField,
220        $tableDimension
221    ) {
222        if (! isset($this->tables[$masterTable])) {
223            $this->tables[$masterTable] = new TableStatsSvg(
224                $this->diagram,
225                $this->db,
226                $masterTable,
227                $font,
228                $fontSize,
229                $this->pageNumber,
230                $this->tablewidth,
231                false,
232                $tableDimension
233            );
234            $this->setMinMax($this->tables[$masterTable]);
235        }
236        if (! isset($this->tables[$foreignTable])) {
237            $this->tables[$foreignTable] = new TableStatsSvg(
238                $this->diagram,
239                $this->db,
240                $foreignTable,
241                $font,
242                $fontSize,
243                $this->pageNumber,
244                $this->tablewidth,
245                false,
246                $tableDimension
247            );
248            $this->setMinMax($this->tables[$foreignTable]);
249        }
250        $this->relations[] = new RelationStatsSvg(
251            $this->diagram,
252            $this->tables[$masterTable],
253            $masterField,
254            $this->tables[$foreignTable],
255            $foreignField
256        );
257    }
258
259    /**
260     * Draws relation arrows and lines
261     * connects master table's master field to
262     * foreign table's foreign field
263     *
264     * @see Relation_Stats_Svg::relationDraw()
265     *
266     * @return void
267     */
268    private function drawRelations()
269    {
270        foreach ($this->relations as $relation) {
271            $relation->relationDraw($this->showColor);
272        }
273    }
274
275    /**
276     * Draws tables
277     *
278     * @see TableStatsSvg::Table_Stats_tableDraw()
279     *
280     * @return void
281     */
282    private function drawTables()
283    {
284        foreach ($this->tables as $table) {
285            $table->tableDraw($this->showColor);
286        }
287    }
288}
289