These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Aura SQL wrapper for YOURLS that creates the allmighty YDB object. |
||
5 | * |
||
6 | * A fine example of a "class that knows too much" (see https://en.wikipedia.org/wiki/God_object) |
||
7 | * |
||
8 | * Note to plugin authors: you most likely SHOULD NOT use directly methods and properties of this class. Use instead |
||
9 | * function wrappers (eg don't use $ydb->option, or $ydb->set_option(), use yourls_*_options() functions instead). |
||
10 | * |
||
11 | * @since 1.7.3 |
||
12 | */ |
||
13 | |||
14 | namespace YOURLS\Database; |
||
15 | |||
16 | use YOURLS\Admin\Logger; |
||
17 | use Aura\Sql\ExtendedPdo; |
||
18 | use PDO; |
||
19 | |||
20 | class YDB extends ExtendedPdo { |
||
21 | |||
22 | /** |
||
23 | * Debug mode, default false |
||
24 | * @var bool |
||
25 | */ |
||
26 | protected $debug = false; |
||
27 | |||
28 | /** |
||
29 | * Page context (ie "infos", "bookmark", "plugins"...) |
||
30 | * @var string |
||
31 | */ |
||
32 | protected $context = ''; |
||
33 | |||
34 | /** |
||
35 | * Information related to a short URL keyword (eg timestamp, long URL, ...) |
||
36 | * |
||
37 | * @var array |
||
38 | * |
||
39 | */ |
||
40 | protected $infos = array(); |
||
41 | |||
42 | /** |
||
43 | * Is YOURLS installed and ready to run? |
||
44 | * @var bool |
||
45 | */ |
||
46 | protected $installed = false; |
||
47 | |||
48 | /** |
||
49 | * Options |
||
50 | * @var array |
||
51 | */ |
||
52 | protected $option = array(); |
||
53 | |||
54 | /** |
||
55 | * Plugin admin pages informations |
||
56 | * @var array |
||
57 | */ |
||
58 | protected $plugin_pages = array(); |
||
59 | |||
60 | /** |
||
61 | * Plugin informations |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $plugins = array(); |
||
65 | |||
66 | /** |
||
67 | * Are we emulating prepare statements ? |
||
68 | * @var bool |
||
69 | */ |
||
70 | protected $is_emulate_prepare; |
||
71 | |||
72 | /** |
||
73 | * @since 1.7.3 |
||
74 | * @param string $dsn The data source name |
||
75 | * @param string $user The username |
||
76 | * @param string $pass The password |
||
77 | * @param array $options Driver-specific options |
||
78 | * @param array $attributes Attributes to set after a connection |
||
79 | */ |
||
80 | public function __construct($dsn, $user, $pass, $options, $attributes) { |
||
81 | parent::__construct($dsn, $user, $pass, $options, $attributes); |
||
82 | } |
||
83 | |||
84 | /** |
||
85 | * Init everything needed |
||
86 | * |
||
87 | * Everything we need to set up is done here in init(), not in the constructor, so even |
||
88 | * when the connection fails (eg config error or DB dead), the constructor has worked |
||
89 | * and we have a $ydb object properly instantiated (and for instance yourls_die() can |
||
90 | * correctly die, even if using $ydb methods) |
||
91 | * |
||
92 | * @since 1.7.3 |
||
93 | * @return void |
||
94 | */ |
||
95 | public function init() { |
||
96 | $this->connect_to_DB(); |
||
97 | |||
98 | $this->set_emulate_state(); |
||
99 | |||
100 | $this->start_profiler(); |
||
101 | } |
||
102 | |||
103 | /** |
||
104 | * Check if we emulate prepare statements, and set bool flag accordingly |
||
105 | * |
||
106 | * Check if current driver can PDO::getAttribute(PDO::ATTR_EMULATE_PREPARES) |
||
107 | * Some combinations of PHP/MySQL don't support this function. See |
||
108 | * https://travis-ci.org/YOURLS/YOURLS/jobs/271423782#L481 |
||
109 | * |
||
110 | * @since 1.7.3 |
||
111 | * @return void |
||
112 | */ |
||
113 | public function set_emulate_state() { |
||
114 | try { |
||
115 | $this->is_emulate_prepare = $this->getAttribute(PDO::ATTR_EMULATE_PREPARES); |
||
116 | } catch (\PDOException $e) { |
||
117 | $this->is_emulate_prepare = false; |
||
118 | } |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Get emulate status |
||
123 | * |
||
124 | * @since 1.7.3 |
||
125 | * @return bool |
||
126 | */ |
||
127 | public function get_emulate_state() { |
||
128 | return $this->is_emulate_prepare; |
||
129 | } |
||
130 | |||
131 | /** |
||
132 | * Initiate real connection to DB server |
||
133 | * |
||
134 | * This is to check that the server is running and/or the config is OK |
||
135 | * |
||
136 | * @since 1.7.3 |
||
137 | * @return void |
||
138 | * @throws \PDOException |
||
139 | */ |
||
140 | public function connect_to_DB() { |
||
141 | try { |
||
142 | $this->connect(); |
||
143 | } catch ( \Exception $e ) { |
||
144 | $this->dead_or_error($e); |
||
145 | } |
||
146 | } |
||
147 | |||
148 | /** |
||
149 | * Die with an error message |
||
150 | * |
||
151 | * @since 1.7.3 |
||
152 | * |
||
153 | * @param \Exception $exception |
||
154 | * |
||
155 | * @return void |
||
156 | */ |
||
157 | public function dead_or_error(\Exception $exception) { |
||
158 | // Use any /user/db_error.php file |
||
159 | if( file_exists( YOURLS_USERDIR . '/db_error.php' ) ) { |
||
160 | include_once( YOURLS_USERDIR . '/db_error.php' ); |
||
161 | die(); |
||
162 | } |
||
163 | |||
164 | $message = yourls__( 'Incorrect DB config, or could not connect to DB' ); |
||
165 | $message .= '<br/>' . get_class($exception) .': ' . $exception->getMessage(); |
||
166 | |||
167 | yourls_die( yourls__( $message ), yourls__( 'Fatal error' ), 503 ); |
||
168 | die(); |
||
169 | } |
||
170 | |||
171 | /** |
||
172 | * Start a Message Logger |
||
173 | * |
||
174 | * @since 1.7.3 |
||
175 | * @see \YOURLS\Admin\Logger |
||
176 | * @see \Aura\Sql\Profiler |
||
177 | * @return void |
||
178 | */ |
||
179 | public function start_profiler() { |
||
180 | $this->profiler = new Logger($this); |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * @param string $context |
||
185 | */ |
||
186 | public function set_html_context($context) { |
||
187 | $this->context = $context; |
||
188 | } |
||
189 | |||
190 | /** |
||
191 | * @return string |
||
192 | */ |
||
193 | public function get_html_context() { |
||
194 | return $this->context; |
||
195 | } |
||
196 | |||
197 | // Options low level functions, see \YOURLS\Database\Options |
||
198 | |||
199 | /** |
||
200 | * @param string $name |
||
201 | * @param mixed $value |
||
202 | */ |
||
203 | public function set_option($name, $value) { |
||
204 | $this->option[$name] = $value; |
||
205 | } |
||
206 | |||
207 | /** |
||
208 | * @param string $name |
||
209 | * @return bool |
||
210 | */ |
||
211 | public function has_option($name) { |
||
212 | return array_key_exists($name, $this->option); |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * @param string $name |
||
217 | * @return string |
||
218 | */ |
||
219 | public function get_option($name) { |
||
220 | return $this->option[$name]; |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * @param string $name |
||
225 | */ |
||
226 | public function delete_option($name) { |
||
227 | unset($this->option[$name]); |
||
228 | } |
||
229 | |||
230 | |||
231 | // Infos (related to keyword) low level functions |
||
232 | |||
233 | /** |
||
234 | * @param string $keyword |
||
235 | * @param mixed $infos |
||
236 | */ |
||
237 | public function set_infos($keyword, $infos) { |
||
238 | $this->infos[$keyword] = $infos; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * @param string $keyword |
||
243 | * @return bool |
||
244 | */ |
||
245 | public function has_infos($keyword) { |
||
246 | return array_key_exists($keyword, $this->infos); |
||
247 | } |
||
248 | |||
249 | /** |
||
250 | * @param string $keyword |
||
251 | * @return array |
||
252 | */ |
||
253 | public function get_infos($keyword) { |
||
254 | return $this->infos[$keyword]; |
||
255 | } |
||
256 | |||
257 | /** |
||
258 | * @param string $keyword |
||
259 | */ |
||
260 | public function delete_infos($keyword) { |
||
261 | unset($this->infos[$keyword]); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @todo: infos & options are working the same way here. Abstract this. |
||
266 | */ |
||
267 | |||
268 | |||
269 | // Plugin low level functions, see functions-plugins.php |
||
270 | |||
271 | /** |
||
272 | * @return array |
||
273 | */ |
||
274 | public function get_plugins() { |
||
275 | return $this->plugins; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @param array $plugins |
||
280 | */ |
||
281 | public function set_plugins(array $plugins) { |
||
282 | $this->plugins = $plugins; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * @param string $plugin plugin filename |
||
287 | */ |
||
288 | public function add_plugin($plugin) { |
||
289 | $this->plugins[] = $plugin; |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * @param string $plugin plugin filename |
||
294 | */ |
||
295 | public function remove_plugin($plugin) { |
||
296 | unset($this->plugins[$plugin]); |
||
297 | } |
||
298 | |||
299 | |||
300 | // Plugin Pages low level functions, see functions-plugins.php |
||
301 | |||
302 | /** |
||
303 | * @return array |
||
304 | */ |
||
305 | public function get_plugin_pages() { |
||
306 | return $this->plugin_pages; |
||
307 | } |
||
308 | |||
309 | /** |
||
310 | * @param array $pages |
||
311 | */ |
||
312 | public function set_plugin_pages(array $pages) { |
||
313 | $this->plugin_pages = $pages; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * @param string $slug |
||
318 | * @param string $title |
||
319 | * @param callable $function |
||
320 | */ |
||
321 | public function add_plugin_page($slug, $title, $function) { |
||
322 | $this->plugin_pages[$slug] = array( |
||
323 | 'slug' => $slug, |
||
324 | 'title' => $title, |
||
325 | 'function' => $function, |
||
326 | ); |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * @param string $slug |
||
331 | */ |
||
332 | public function remove_plugin_page($slug) { |
||
333 | unset($this->plugin_pages[$slug]); |
||
334 | } |
||
335 | |||
336 | |||
337 | /** |
||
338 | * Return count of SQL queries performed |
||
339 | * |
||
340 | * @since 1.7.3 |
||
341 | * @return int |
||
342 | */ |
||
343 | public function get_num_queries() { |
||
344 | return count( (array) $this->get_queries() ); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * Return SQL queries performed |
||
349 | * |
||
350 | * Aura\Sql\Profiler logs every PDO command issued. But depending on PDO::ATTR_EMULATE_PREPARES, some are |
||
351 | * actually sent to the mysql server or not : |
||
352 | * - if PDO::ATTR_EMULATE_PREPARES is true, prepare() statements are not sent to the server and are performed |
||
353 | * internally, so they are removed from the logger |
||
354 | * - if PDO::ATTR_EMULATE_PREPARES is false, prepare() statements are actually performed by the mysql server, |
||
355 | * and count as an actual query |
||
356 | * |
||
357 | * Resulting array is something like: |
||
358 | * array ( |
||
359 | * 0 => array ( |
||
360 | * 'duration' => 1.0010569095611572265625, |
||
361 | * 'function' => 'connect', |
||
362 | * 'statement' => NULL, |
||
363 | * 'bind_values' => array (), |
||
364 | * 'trace' => ...back trace..., |
||
365 | * ), |
||
366 | * // key index might not be sequential if 'prepare' function are filtered out |
||
367 | * 2 => array ( |
||
368 | * 'duration' => 0.000999927520751953125, |
||
369 | * 'function' => 'perform', |
||
370 | * 'statement' => 'SELECT option_value FROM yourls_options WHERE option_name = :option_name LIMIT 1', |
||
371 | * 'bind_values' => array ( 'option_name' => 'test_option' ), |
||
372 | * 'trace' => ...back trace..., |
||
373 | * ), |
||
374 | * ); |
||
375 | * |
||
376 | * @since 1.7.3 |
||
377 | * @return array |
||
378 | */ |
||
379 | public function get_queries() { |
||
380 | $queries = $this->getProfiler()->getProfiles(); |
||
381 | |||
382 | if ($this->get_emulate_state()) { |
||
383 | // keep queries if $query['function'] != 'prepare' |
||
384 | $queries = array_filter($queries, function($query) {return $query['function'] !== 'prepare';}); |
||
385 | } |
||
386 | |||
387 | return $queries; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Set YOURLS installed state |
||
392 | * |
||
393 | * @since 1.7.3 |
||
394 | * @param bool $bool |
||
395 | * @return void |
||
396 | */ |
||
397 | public function set_installed($bool) { |
||
398 | $this->installed = $bool; |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * Get YOURLS installed state |
||
403 | * |
||
404 | * @since 1.7.3 |
||
405 | * @return bool |
||
406 | */ |
||
407 | public function is_installed() { |
||
408 | return $this->installed; |
||
409 | } |
||
410 | |||
411 | /** |
||
412 | * Return standardized DB version |
||
413 | * |
||
414 | * The regex removes everything that's not a number at the start of the string, or remove anything that's not a number and what |
||
415 | * follows after that. |
||
416 | * 'omgmysql-5.5-ubuntu-4.20' => '5.5' |
||
417 | * 'mysql5.5-ubuntu-4.20' => '5.5' |
||
418 | * '5.5-ubuntu-4.20' => '5.5' |
||
419 | * '5.5-beta2' => '5.5' |
||
420 | * '5.5' => '5.5' |
||
421 | * |
||
422 | * @since 1.7.3 |
||
423 | * @return string |
||
424 | */ |
||
425 | public function mysql_version() { |
||
426 | $version = $this->pdo->getAttribute(PDO::ATTR_SERVER_VERSION); |
||
427 | return $version; |
||
428 | } |
||
429 | |||
430 | /** |
||
431 | * Deprecated properties since 1.7.3, unused in 3rd party plugins as far as I know |
||
432 | * |
||
433 | * $ydb->DB_driver |
||
434 | * $ydb->captured_errors |
||
435 | * $ydb->dbh |
||
436 | * $ydb->result |
||
437 | * $ydb->rows_affected |
||
438 | * $ydb->show_errors |
||
439 | */ |
||
440 | |||
441 | /** |
||
442 | * Deprecated functions since 1.7.3 |
||
443 | */ |
||
444 | |||
445 | // @codeCoverageIgnoreStart |
||
446 | |||
447 | public function escape($string) { |
||
448 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
449 | // This will escape using PDO->quote(), but then remove the enclosing quotes |
||
450 | return substr($this->quote($string), 1, -1); |
||
451 | } |
||
452 | |||
453 | public function get_col($query) { |
||
454 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
455 | yourls_debug_log('LEGACY SQL: '.$query); |
||
456 | return $this->fetchCol($query); |
||
457 | } |
||
458 | |||
459 | public function get_results($query) { |
||
460 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
461 | yourls_debug_log('LEGACY SQL: '.$query); |
||
462 | $stm = parent::query($query); |
||
0 ignored issues
–
show
|
|||
463 | return($stm->fetchAll(PDO::FETCH_OBJ)); |
||
464 | } |
||
465 | |||
466 | public function get_row($query) { |
||
467 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
468 | yourls_debug_log('LEGACY SQL: '.$query); |
||
469 | $row = $this->fetchObjects($query); |
||
470 | return isset($row[0]) ? $row[0] : null; |
||
471 | } |
||
472 | |||
473 | public function get_var($query) { |
||
474 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
475 | yourls_debug_log('LEGACY SQL: '.$query); |
||
476 | return $this->fetchValue($query); |
||
477 | } |
||
478 | |||
479 | public function query($query) { |
||
480 | yourls_deprecated_function( '$ydb->'.__FUNCTION__, '1.7.3', 'PDO' ); |
||
481 | yourls_debug_log('LEGACY SQL: '.$query); |
||
482 | return $this->fetchAffected($query); |
||
0 ignored issues
–
show
The return type of
return $this->fetchAffected($query); (integer ) is incompatible with the return type declared by the interface Aura\Sql\PdoInterface::query of type PDOStatement .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function
Loading history...
|
|||
483 | } |
||
484 | // @codeCoverageIgnoreEnd |
||
485 | } |
||
486 |
This check looks for a call to a parent method whose name is different than the method from which it is called.
Consider the following code:
The
getFirstName()
method in theSon
calls the wrong method in the parent class.