Mysqli & the Fuel backup module

edited April 2015 in Share
I've just realised that CI's dbutil class isn't compatible with the mysqli driver, which in turn makes the Backup module ineffective.

Is there a workaround? I thought of using parallel db configs where everything but the backup module use mysqli but even so I get deprecation messages from PHP about using the mysql driver. Even if this worked, I'm not sure if it's a valid solution.

Comments

  • edited 5:46PM
    What errors are you seeing from the CI dbutil class and what version of PHP are you running?
  • edited April 2015
    I first noticed this trying to use a backup cron, but if you use the "Yes, back it up" button to download a zip, the same problem occurs.

    I've got live and dev environments and both use mysqli. However, the live DB, I've only just noticed, is Percona 5.6.17-66.0 (a MySQL drop-in), but the dev server uses vanilla MySQL 5.6.17 with WAMP

    #1 Live (Linux, PHP 5.4.37) error is:

    A Database Error Occurred

    Unsupported feature of the database platform you are using.

    Filename: /.../fuel/modules/backup/libraries/Fuel_backup.php

    Line Number: 512
    #2 Dev (WAMP, PHP 5.5.12) error:

    A Database Error Occurred

    Unsupported feature of the database platform you are using.

    Filename: D:\...\fuel\codeigniter\database\drivers\mysqli\mysqli_utility.php

    Line Number: 82
    ..and I've just tried it on a Mac dev environment with MySQL 5.6.20 (mysqli), PHP 5.5.20 and it gives the same error as #1

    I've noticed the backup mod didn't work on another (LAMP) site using MySQL 5.5.42, PHP 5.4.32, again using mysqli.

    However, using the mysql connector it's OK.
  • edited 5:46PM
    There seems to be a few stackoverflows out there about this issue (I hadn't heard of it before):
    http://stackoverflow.com/questions/24197844/database-utility-backups-not-working-with-mysqli-codeignitor
    http://stackoverflow.com/questions/27116299/how-to-extend-the-codeigniter-database-utility-class

    The accepted solution in the last link may be worth trying by perhaps extending the DB_utility class as a fuel/application/libraries/MY_DB_utility and replacing the following method:
    public function dbutil() { /* snip for brevity */ $class = config_item('subclass_prefix').'DB_'.$db->dbdriver.'_utility'; $filename = APPPATH.'libraries/'.$class.EXT; require_once($filename); /* snip for brevity */ }
    I haven't tested this yet and hope that you can simply extend it that way like most of the other CI classes.
  • edited 5:46PM
    I followed some of what went on in those articles, overriding dbutil() in MY_Loader, including the require of MY_DB_mysqli_utility (in application/libraries), and then I ripped the _backup() method from CI 3's mysqli_utility.php, made a few nip & tucks (replacing escape_identifiers() with _escape_identifiers() for example) and... amazingly I can export a zip of the sql from the backup module.
  • edited April 2015
    MY_DB_mysqli_utility.php added to /application/libraries:

    class MY_DB_mysqli_utility extends CI_DB_utility { function __construct() { parent::__construct(); log_message('debug', 'Extended DB mysqli utility class instantiated!'); } /** * List databases * * @access private * @return bool */ function _list_databases() { return "SHOW DATABASES"; } // -------------------------------------------------------------------- /** * Optimize table query * * Generates a platform-specific query so that a table can be optimized * * @access private * @param string the table name * @return object */ function _optimize_table($table) { return "OPTIMIZE TABLE " . $this->db->_escape_identifiers($table); } // -------------------------------------------------------------------- /** * Repair table query * * Generates a platform-specific query so that a table can be repaired * * @access private * @param string the table name * @return object */ function _repair_table($table) { return "REPAIR TABLE " . $this->db->_escape_identifiers($table); } /** * Backup * * @param array $params Preferences * @return mixed */ function _backup($params = array()) { if (count($params) === 0) { return FALSE; } // Extract the prefs for simplicity extract($params); // Build the output $output = ''; foreach ((array) $tables as $table) { // Is the table in the "ignore" list? if (in_array($table, (array) $ignore, TRUE)) { continue; } // Get the table schema $query = $this->db->query('SHOW CREATE TABLE ' . $this->db->_escape_identifiers($this->db->database . '.' . $table)); // No result means the table name was invalid if ($query === FALSE) { continue; } // Write out the table schema $output .= '#' . $newline . '# TABLE STRUCTURE FOR: ' . $table . $newline . '#' . $newline . $newline; if ($add_drop === TRUE) { $output .= 'DROP TABLE IF EXISTS ' . $this->db->_protect_identifiers($table) . ';' . $newline . $newline; } $i = 0; $result = $query->result_array(); foreach ($result[0] as $val) { if ($i++ % 2) { $output .= $val . ';' . $newline . $newline; } } // If inserts are not needed we're done... if ($add_insert === FALSE) { continue; } // Grab all the data from the current table $query = $this->db->query('SELECT * FROM ' . $this->db->_protect_identifiers($table)); if ($query->num_rows() === 0) { continue; } // Fetch the field names and determine if the field is an // integer type. We use this info to decide whether to // surround the data with quotes or not $i = 0; $field_str = ''; $is_int = array(); while ($field = $query->result_id->fetch_field()) { // Most versions of MySQL store timestamp as a string $is_int[$i] = in_array(strtolower($field->type), array('tinyint', 'smallint', 'mediumint', 'int', 'bigint'), //, 'timestamp'), TRUE); // Create a string of field names $field_str .= $this->db->_escape_identifiers($field->name) . ', '; $i++; } // Trim off the end comma $field_str = preg_replace('/, $/', '', $field_str); // Build the insert string foreach ($query->result_array() as $row) { $val_str = ''; $i = 0; foreach ($row as $v) { // Is the value NULL? if ($v === NULL) { $val_str .= 'NULL'; } else { // Escape the data if it's not an integer $val_str .= ($is_int[$i] === FALSE) ? $this->db->escape($v) : $v; } // Append a comma $val_str .= ', '; $i++; } // Remove the comma at the end of the string $val_str = preg_replace('/, $/', '', $val_str); // Build the INSERT string $output .= 'INSERT INTO ' . $this->db->_protect_identifiers($table) . ' (' . $field_str . ') VALUES (' . $val_str . ');' . $newline; } $output .= $newline . $newline; } return $output; } }
  • edited 5:46PM
    MY_Loader amended to load the above class:

    /* load the MX_Loader class */ require FUEL_PATH."core/Loader.php"; class MY_Loader extends Fuel_Loader { public function dbutil() { if (! class_exists('CI_DB')) { $this->database(); } $CI =& get_instance(); // for backwards compatibility, load dbforge so we can extend dbutils off it // this use is deprecated and strongly discouraged $CI->load->dbforge(); require_once(BASEPATH . 'database/DB_utility.php'); // START custom >> // path of default db utility file $default_utility = BASEPATH . 'database/drivers/' . $CI->db->dbdriver . '/' . $CI->db->dbdriver . '_utility.php'; // path of my custom db utility file $my_utility = APPPATH . 'libraries/MY_DB_' . $CI->db->dbdriver . '_utility.php'; // set custom db utility file if it exists if (file_exists($my_utility)) { $utility = $my_utility; $extend = 'MY_DB_'; } else { $utility = $default_utility; $extend = 'CI_DB_'; } // load db utility file require_once($utility); // set the class $class = $extend . $CI->db->dbdriver . '_utility'; // << END custom $CI->dbutil = new $class(); } }
  • edited 5:46PM
    Awesome... I've tested it out as well and seems to do the trick. I'll post a change for the next release.
Sign In or Register to comment.