3 # Copyright (C) 2006-2011 Tobias Leupold <tobias.leupold@web.de>
5 # This file is part of the b8 package
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU Lesser General Public License as published by
9 # the Free Software Foundation in version 2.1 of the License.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 # License for more details.
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21 * The MySQL abstraction layer for communicating with the database.
22 * Copyright (C) 2009 Oliver Lillie (aka buggedcom)
23 * Copyright (C) 2010-2011 Tobias Leupold <tobias.leupold@web.de>
28 * @author Oliver Lillie (aka buggedcom) (original PHP 5 port and optimizations)
29 * @author Tobias Leupold
32 class b8_storage_mysql extends b8_storage_base
35 public $config = array(
36 'database' => 'b8_wordlist',
37 'table_name' => 'b8_wordlist',
38 'host' => 'localhost',
44 public $b8_config = array(
45 'degenerator' => NULL,
49 private $_connection = NULL;
50 private $_deletes = array();
51 private $_puts = array();
52 private $_updates = array();
54 const DATABASE_CONNECTION_FAIL = 'DATABASE_CONNECTION_FAIL';
55 const DATABASE_CONNECTION_ERROR = 'DATABASE_CONNECTION_ERROR';
56 const DATABASE_CONNECTION_BAD_RESOURCE = 'DATABASE_CONNECTION_BAD_RESOURCE';
57 const DATABASE_SELECT_ERROR = 'DATABASE_SELECT_ERROR';
58 const DATABASE_TABLE_ACCESS_FAIL = 'DATABASE_TABLE_ACCESS_FAIL';
59 const DATABASE_WRONG_VERSION = 'DATABASE_WRONG_VERSION';
62 * Constructs the database layer.
65 * @param string $config
68 function __construct($config, $degenerator, $today)
71 # Pass some variables of the main b8 config to this class
72 $this->b8_config['degenerator'] = $degenerator;
73 $this->b8_config['today'] = $today;
75 # Validate the config items
77 if(count($config) > 0) {
79 foreach ($config as $name => $value) {
88 $this->config[$name] = (string) $value;
95 if(is_resource($value) === TRUE) {
96 $resource_type = get_resource_type($value);
97 $this->config['connection'] = $resource_type !== 'mysql link' && $resource_type !== 'mysql link persistent' ? FALSE : $value;
101 $this->config['connection'] = FALSE;
116 * Closes the database connection.
122 function __destruct()
125 if($this->_connection === NULL)
128 # Commit any changes before closing
131 # Just close the connection if no link-resource was passed and b8 created it's own connection
132 if($this->config['connection'] === NULL)
133 mysql_close($this->_connection);
135 $this->connected = FALSE;
140 * Connect to the database and do some checks.
143 * @return mixed Returns TRUE on a successful database connection, otherwise returns a constant from b8.
146 public function connect()
149 # Are we already connected?
150 if($this->connected === TRUE)
153 # Are we using an existing passed resource?
154 if($this->config['connection'] === FALSE) {
155 # ... yes we are, but the connection is not a resource, so return an error
156 $this->connected = FALSE;
157 return self::DATABASE_CONNECTION_BAD_RESOURCE;
160 elseif($this->config['connection'] === NULL) {
162 # ... no we aren't so we have to connect.
164 if($this->_connection = mysql_connect($this->config['host'], $this->config['user'], $this->config['pass'])) {
165 if(mysql_select_db($this->config['database'], $this->_connection) === FALSE) {
166 $this->connected = FALSE;
167 return self::DATABASE_SELECT_ERROR . ": " . mysql_error();
171 $this->connected = FALSE;
172 return self::DATABASE_CONNECTION_ERROR;
179 $this->_connection = $this->config['connection'];
183 if($this->_connection === NULL) {
184 $this->connected = FALSE;
185 return self::DATABASE_CONNECTION_FAIL;
188 # Check to see if the wordlist table exists
189 if(mysql_query('DESCRIBE ' . $this->config['table_name'], $this->_connection) === FALSE) {
190 $this->connected = FALSE;
191 return self::DATABASE_TABLE_ACCESS_FAIL . ": " . mysql_error();
194 # Everything is okay and connected
195 $this->connected = TRUE;
197 # Let's see if this is a b8 database and the version is okay
198 return $this->check_database();
203 * Does the actual interaction with the database when fetching data.
206 * @param array $tokens
207 * @return mixed Returns an array of the returned data in the format array(token => data) or an empty array if there was no data.
210 protected function _get_query($tokens)
213 # Construct the query ...
215 if(count($tokens) > 0) {
219 foreach ($tokens as $token) {
220 $token = mysql_real_escape_string($token, $this->_connection);
221 array_push($where, $token);
224 $where = 'token IN ("' . implode('", "', $where) . '")';
228 $token = mysql_real_escape_string($token, $this->_connection);
229 $where = 'token = "' . $token . '"';
232 # ... and fetch the data
234 $result = mysql_query('
236 FROM ' . $this->config['table_name'] . '
237 WHERE ' . $where . ';
238 ', $this->_connection);
242 while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
243 $data[$row['token']] = $row['count'];
245 mysql_free_result($result);
252 * Store a token to the database.
255 * @param string $token
256 * @param string $count
260 protected function _put($token, $count) {
261 $token = mysql_real_escape_string($token, $this->_connection);
262 $count = mysql_real_escape_string($count, $this->_connection);;
263 array_push($this->_puts, '("' . $token . '", "' . $count . '")');
267 * Update an existing token.
270 * @param string $token
271 * @param string $count
275 protected function _update($token, $count)
277 $token = mysql_real_escape_string($token, $this->_connection);
278 $count = mysql_real_escape_string($count, $this->_connection);
279 array_push($this->_updates, '("' . $token . '", "' . $count . '")');
283 * Remove a token from the database.
286 * @param string $token
290 protected function _del($token)
292 $token = mysql_real_escape_string($token, $this->_connection);
293 array_push($this->_deletes, $token);
297 * Commits any modification queries.
303 protected function _commit()
306 if(count($this->_deletes) > 0) {
308 $result = mysql_query('
309 DELETE FROM ' . $this->config['table_name'] . '
310 WHERE token IN ("' . implode('", "', $this->_deletes) . '");
311 ', $this->_connection);
313 if(is_resource($result) === TRUE)
314 mysql_free_result($result);
316 $this->_deletes = array();
320 if(count($this->_puts) > 0) {
322 $result = mysql_query('
323 INSERT INTO ' . $this->config['table_name'] . '(token, count)
324 VALUES ' . implode(', ', $this->_puts) . ';', $this->_connection);
326 if(is_resource($result) === TRUE)
327 mysql_free_result($result);
329 $this->_puts = array();
333 if(count($this->_updates) > 0) {
335 $result = mysql_query('
336 INSERT INTO ' . $this->config['table_name'] . '(token, count)
337 VALUES ' . implode(', ', $this->_updates) . '
338 ON DUPLICATE KEY UPDATE ' . $this->config['table_name'] . '.count = VALUES(count);', $this->_connection);
340 if(is_resource($result) === TRUE)
341 mysql_free_result($result);
343 $this->_updates = array();