Completed
Push — master ( 6cab57...287bd7 )
by Sam
02:34
created

src/DB/Database.php (1 issue)

Check for implicit conversion of array to boolean.

Best Practice Bug Minor

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file contains only a single class.
4
 *
5
 * @file
6
 * @package Tabulate
7
 */
8
9
namespace WordPress\Tabulate\DB;
10
11
/**
12
 * The database class represents the entire MySQL database that WordPress uses.
13
 */
14
class Database {
15
16
	/**
17
	 * The global wpdb object.
18
	 *
19
	 * @var \wpdb
20
	 */
21
	protected $wpdb;
22
23
	/**
24
	 * A list of all table names.
25
	 *
26
	 * @var string[]
27
	 */
28
	protected $table_names;
29
30
	/**
31
	 * The filesystem.
32
	 *
33
	 * @var \WP_Filesystem_Base
34
	 */
35
	protected $filesystem;
36
37
	/**
38
	 * The list of all tables that the user can read.
39
	 *
40
	 * @var Table[]
41
	 */
42
	protected $tables;
43
44
	/**
45
	 * Create a new Database object based on the given wpdb object.
46
	 *
47
	 * @param \wpdb $wpdb The global wpdb object.
48
	 */
49
	public function __construct( $wpdb ) {
50
		$this->wpdb = $wpdb;
51
	}
52
53
	/**
54
	 * Set the filesystem.
55
	 *
56
	 * @param \WP_Filesystem_Base $filesystem The filesystem object.
57
	 */
58
	public function set_filesystem( \WP_Filesystem_Base $filesystem ) {
59
		$this->filesystem = $filesystem;
60
	}
61
62
	/**
63
	 * Get the filesystem.
64
	 *
65
	 * @return \WP_Filesystem_Base
66
	 */
67
	public function get_filesystem() {
68
		return $this->filesystem;
69
	}
70
71
	/**
72
	 * Get the global wpdb object.
73
	 *
74
	 * @return \wpdb
75
	 */
76
	public function get_wpdb() {
77
		return $this->wpdb;
78
	}
79
80
	/**
81
	 * Get a list of tables and views that the current user can read.
82
	 *
83
	 * @return string[] The table names.
84
	 */
85
	public function get_table_names() {
86
		if ( ! $this->table_names ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->table_names of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
87
			$this->table_names = array();
88
			foreach ( $this->wpdb->get_col( 'SHOW TABLES' ) as $table_name ) {
89
				if ( Grants::current_user_can( Grants::READ, $table_name ) ) {
90
					$this->table_names[ $table_name ] = $table_name;
91
				}
92
			}
93
		}
94
		return $this->table_names;
95
	}
96
97
	/**
98
	 * Get a table from the database.
99
	 *
100
	 * @param string $name The name of the desired table.
101
	 * @return \WordPress\Tabulate\DB\Table|false The table, or false if it's not available.
102
	 */
103
	public function get_table( $name ) {
104
		if ( ! in_array( $name, $this->get_table_names(), true ) ) {
105
			return false;
106
		}
107
		if ( ! isset( $this->tables[ $name ] ) ) {
108
			$this->tables[ $name ] = new Table( $this, $name );
109
		}
110
		return $this->tables[ $name ];
111
	}
112
113
	/**
114
	 * Forget all table information, forcing it to be re-read from the database
115
	 * when next required. Used after schema changes.
116
	 */
117
	public function reset() {
118
		$this->table_names = false;
119
		$this->tables = false;
120
	}
121
122
	/**
123
	 * Get all tables in this database.
124
	 *
125
	 * @param boolean $exclude_views Whether to exclude database views from the returned list.
126
	 * @return Table[] An array of all Tables.
127
	 */
128
	public function get_tables( $exclude_views = true ) {
129
		$out = array();
130
		foreach ( $this->get_table_names() as $name ) {
131
			$table = $this->get_table( $name );
132
			// If this table is not available, skip it.
133
			if ( ! $table ) {
134
				continue;
135
			}
136
			if ( $exclude_views && $table->is_view() ) {
137
				continue;
138
			}
139
			$out[ $table->get_name() ] = $table;
140
		}
141
		return $out;
142
	}
143
144
	/**
145
	 * Get all views in this database.
146
	 *
147
	 * @return Table|array An array of all Tables that are views.
148
	 */
149
	public function get_views() {
150
		$out = array();
151
		foreach ( $this->get_tables( false ) as $table ) {
152
			if ( $table->is_view() ) {
153
				$out[ $table->get_name() ] = $table;
154
			}
155
		}
156
		return $out;
157
	}
158
159
	/**
160
	 * Create a new table.
161
	 *
162
	 * @param string $name The name of the new table.
163
	 * @param string $comment The table comment.
164
	 * @throws Exception If the current user cannot 'promote_users'.
165
	 */
166
	public function create_table( $name, $comment = '' ) {
167
		if ( ! current_user_can( 'promote_users' ) ) {
168
			throw new Exception( 'Only administrators are allowed to create tables' );
169
		}
170
		$sql = "CREATE TABLE IF NOT EXISTS `$name` ( "
171
			. " `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY "
172
			. ") ENGINE=InnoDB, COMMENT='$comment';";
173
		$this->query( $sql );
174
		$this->reset();
175
		return $this->get_table( $name );
176
	}
177
178
	/**
179
	 * A wrapper around wpdb::prepare() and wpdb::query()
180
	 * that also checks wpdb::$last_error and throws up on occasion of badness.
181
	 *
182
	 * @param string   $sql The SQL statement to execute.
183
	 * @param string[] $params Parameters to pass to wpdb::prepare().
184
	 * @param string   $error_message What to tell the user if this query fails.
185
	 * @throws Exception The exception message is taken from wpdb::$last_error and if WP_DEBUG is set will also include the erroneous SQL.
186
	 */
187
	public function query( $sql, $params = null, $error_message = null ) {
188
		if ( $params ) {
189
			$sql = $this->get_wpdb()->prepare( $sql, $params );
190
		}
191
		$this->get_wpdb()->query( $sql );
192
		if ( ! empty( $this->get_wpdb()->last_error ) ) {
193
			$msg = $error_message . ': ' . $this->get_wpdb()->last_error;
194
			if ( WP_DEBUG ) {
195
				$msg .= " <code>$sql</code>";
196
			}
197
			throw new Exception( $msg );
198
		}
199
	}
200
201
	/**
202
	 * Get the name of the directory to which MySQL will write temporary export files.
203
	 * This is either the value of the 'secure_file_priv' server variable,
204
	 * or WordPress's normal temporary directory as returned by get_temp_dir().
205
	 * Always has a trailing slash.
206
	 *
207
	 * @return string Full path of the directory.
208
	 * @throws Exception If the directory is not writable.
209
	 */
210
	public function get_tmp_dir() {
211
		$query = "SHOW VARIABLES LIKE 'secure_file_priv';";
212
		$db_dir = $this->get_wpdb()->get_var( $query, 1 );
213
		$dir = empty( $db_dir ) ? get_temp_dir() : $db_dir;
214
		$out = rtrim( $dir, '/' ) . '/';
215
		if ( ! $this->get_filesystem()->is_writable( $out ) ) {
216
			throw new Exception( "Unable to write to temporary directory $out" );
217
		}
218
		return $out;
219
	}
220
}
221