Update copyright
[friendica.git/.git] / src / Database / View.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Database;
23
24 use Exception;
25 use Friendica\Core\Hook;
26 use Friendica\DI;
27
28 class View
29 {
30         /**
31          * view definition loaded from static/dbview.config.php
32          *
33          * @var array
34          */
35         private static $definition = [];
36
37         /**
38          * Loads the database structure definition from the static/dbview.config.php file.
39          * On first pass, defines DB_UPDATE_VERSION constant.
40          *
41          * @see static/dbview.config.php
42          * @param boolean $with_addons_structure Whether to tack on addons additional tables
43          * @param string  $basePath              The base path of this application
44          * @return array
45          * @throws Exception
46          */
47         public static function definition($basePath = '', $with_addons_structure = true)
48         {
49                 if (!self::$definition) {
50                         if (empty($basePath)) {
51                                 $basePath = DI::app()->getBasePath();
52                         }
53
54                         $filename = $basePath . '/static/dbview.config.php';
55
56                         if (!is_readable($filename)) {
57                                 throw new Exception('Missing database view config file static/dbview.config.php');
58                         }
59
60                         $definition = require $filename;
61
62                         if (!$definition) {
63                                 throw new Exception('Corrupted database view config file static/dbview.config.php');
64                         }
65
66                         self::$definition = $definition;
67                 } else {
68                         $definition = self::$definition;
69                 }
70
71                 if ($with_addons_structure) {
72                         Hook::callAll('dbview_definition', $definition);
73                 }
74
75                 return $definition;
76         }
77
78         public static function create(bool $verbose, bool $action)
79         {
80                 // Delete previously used views that aren't used anymore
81                 foreach(['post-view', 'post-thread-view'] as $view) {
82                         if (self::isView($view)) {
83                                 $sql = sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($view));
84                                 if (!empty($sql) && $verbose) {
85                                         echo $sql . ";\n";
86                                 }
87                 
88                                 if (!empty($sql) && $action) {
89                                         DBA::e($sql);
90                                 }
91                         }
92                 }
93
94                 $definition = self::definition();
95
96                 foreach ($definition as $name => $structure) {
97                         self::createview($name, $structure, $verbose, $action);
98                 }
99         }
100
101         public static function printStructure($basePath)
102         {
103                 $database = self::definition($basePath, false);
104
105                 foreach ($database AS $name => $structure) {
106                         echo "--\n";
107                         echo "-- VIEW $name\n";
108                         echo "--\n";
109                         self::createView($name, $structure, true, false);
110
111                         echo "\n";
112                 }
113         }
114
115         private static function createview($name, $structure, $verbose, $action)
116         {
117                 $r = true;
118
119                 $sql_rows = [];
120                 foreach ($structure["fields"] AS $fieldname => $origin) {
121                         if (is_string($origin)) {
122                                 $sql_rows[] = $origin . " AS `" . DBA::escape($fieldname) . "`";
123                         } elseif (is_array($origin) && (sizeof($origin) == 2)) {
124                                 $sql_rows[] = "`" . DBA::escape($origin[0]) . "`.`" . DBA::escape($origin[1]) . "` AS `" . DBA::escape($fieldname) . "`";
125                         }
126                 }
127
128                 if (self::isView($name)) {
129                         $sql = sprintf("DROP VIEW IF EXISTS `%s`", DBA::escape($name));
130                 } elseif (self::isTable($name)) {
131                         $sql = sprintf("DROP TABLE IF EXISTS `%s`", DBA::escape($name));
132                 }
133
134                 if (!empty($sql) && $verbose) {
135                         echo $sql . ";\n";
136                 }
137
138                 if (!empty($sql) && $action) {
139                         DBA::e($sql);
140                 }
141
142                 $sql = sprintf("CREATE VIEW `%s` AS SELECT \n\t", DBA::escape($name)) .
143                         implode(",\n\t", $sql_rows) . "\n\t" . $structure['query'];
144         
145                 if ($verbose) {
146                         echo $sql . ";\n";
147                 }
148
149                 if ($action) {
150                         $r = DBA::e($sql);
151                 }
152
153                 return $r;
154         }
155
156         /**
157          * Check if the given table/view is a view
158          *
159          * @param string $view
160          * @return boolean "true" if it's a view
161          */
162         private static function isView(string $view)
163         {
164                 $status = DBA::selectFirst(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_TYPE'],
165                         ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $view]);
166
167                 if (empty($status['TABLE_TYPE'])) {
168                         return false;
169                 }
170
171                 return $status['TABLE_TYPE'] == 'VIEW';
172         }
173
174         /**
175          * Check if the given table/view is a table
176          *
177          * @param string $table
178          * @return boolean "true" if it's a table
179          */
180         private static function isTable(string $table)
181         {
182                 $status = DBA::selectFirst(['INFORMATION_SCHEMA' => 'TABLES'], ['TABLE_TYPE'],
183                         ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_NAME' => $table]);
184
185                 if (empty($status['TABLE_TYPE'])) {
186                         return false;
187                 }
188
189                 return $status['TABLE_TYPE'] == 'BASE TABLE';
190         }
191 }