commands = array();
$this->error = false;
}
//Adds a command to the transaction queue.
public function add( $command ) {
$this->commands[] = func_get_args();
}
//Executes each command that is in the transaction queue.
public function execute() {
$this->error = false;
foreach ( $this->commands as $key => $transaction ) {
$done = false;
$log_line = '';
if ( count( $transaction ) < 1 ) {
$done = false;
} elseif ( ! is_callable( $transaction[0] ) ) {
$done = false;
} else {
$func = array_shift( $transaction );
if ( is_array( $func ) ) {
if ( is_object( $func[0] ) ) {
$log_line = get_class( $func[0] ) . '->';
} elseif ( is_scalar( $func[0] ) ) {
$log_line = $func[0] . '::';
}
if ( is_scalar( $func[1] ) ) {
$log_line .= $func[1];
}
} else {
$log_line = $func;
}
$log_line .= '(' . json_encode( $transaction ) . ')';
try {
call_user_func_array( $func, $transaction );
$done = true;
} catch( Exception $ex ) {
$this->set_error( $ex, $func );
return false;
}
}
if ( $done ) {
$this->log_action( $log_line );
unset( $this->commands[$key] );
}
}
return true;
}
//Saves error details if a command fails during execution.
protected function set_error( $exception, $command ) {
$this->error = $exception;
$this->error->command = $command;
}
// Returns the last error and resets the error-flag.
public function last_error() {
$error = $this->error;
$this->error = false;
return $error;
}
// Debug function that will display the contents of the current queue.
public function debug() {
self::$core->debug->dump( $this->commands );
}
//sets plugin name
public function plugin( $name ) {
$this->plugin = sanitize_html_class( $name );
}
//Writes data to a file in the uploads directory.
private function write_to_file( $file, $ext, $data, $silent_fail = false ) {
// Find the uploads-folder.
$upload = wp_upload_dir();
if ( false !== $upload['error'] ) {
return $this->write_file_failed(
$silent_fail,
1,
$upload['error']
);
}
// Create the Snapshot sub-folder.
if ( empty( $this->plugin ) ) {
$this->plugin( 'plugin' );
}
$target = trailingslashit( $upload['basedir'] ) . $this->plugin . '/';
if ( ! is_dir( $target ) ) {
mkdir( $target );
}
if ( ! is_dir( $target ) ) {
return $this->write_file_failed(
$silent_fail,
2,
'Could not create sub-directory ' . $target
);
}
// Create the empty snapshot file.
$filename = sanitize_html_class( $file );
$filename .= '-' . date( 'Ymd-His' );
$ext = '.' . $ext;
$i = '';
$sep = '';
while ( file_exists( $target . $filename . $sep . $i . $ext ) ) {
if ( empty( $i ) ) { $i = 1; }
else { $i += 1; }
$sep = '-';
}
$filename = $target . $filename . $sep . $i . $ext;
file_put_contents( $filename, '' );
if ( ! file_exists( $filename ) ) {
return $this->write_file_failed(
$silent_fail,
3,
'Could not create file ' . $filename
);
}
// Write data to file.
file_put_contents( $filename, $data );
return $filename;
}
//reads data from file
private function read_from_file( $file ) {
// Find the uploads-folder.
$upload = wp_upload_dir();
if ( false !== $upload['error'] ) {
return '';
}
// Build the full file name.
if ( empty( $this->plugin ) ) {
$this->plugin( 'plugin' );
}
$target = trailingslashit( $upload['basedir'] ) . $this->plugin . '/';
if ( ! is_dir( $target ) ) {
return '';
}
$filename = $target . $file;
if ( ! is_file( $filename ) ) {
return '';
}
$data = file_get_contents( $filename );
return $data;
}
//returns backup files
public function list_files( $ext = '' ) {
$res = array();
// Find the uploads-folder.
$upload = wp_upload_dir();
if ( false !== $upload['error'] ) {
return $res;
}
// Build the full file name.
if ( empty( $this->plugin ) ) {
$this->plugin( 'plugin' );
}
$target = trailingslashit( $upload['basedir'] ) . $this->plugin . '/';
if ( ! is_dir( $target ) ) {
return $res;
}
if ( empty( $ext ) ) {
$ext = '*';
}
$pattern = $target . '*.' . $ext;
$res = glob( $pattern );
foreach ( $res as $key => $path ) {
$res[$key] = str_replace( $target, '', $path );
}
return $res;
}
//saves actions
public function log_action( $data, $silent_fail = false ) {
static $Logfile = null;
$data .= "\n-----\n";
if ( null === $Logfile ) {
$Logfile = $this->write_to_file( 'update_log', 'log', $data, $silent_fail );
} else {
file_put_contents( $Logfile, $data, FILE_APPEND );
}
}
//saves certain database values
public function snapshot( $name, $data_list, $silent_fail = false ) {
// Collect data from the DB that was specified by the user.
$data = $this->snapshot_collect( $data_list );
$data = json_encode( $data );
$this->write_to_file( $name, 'json', $data, $silent_fail );
}
private function write_file_failed( $silent_fail, $err_code, $error = '' ) {
if ( $silent_fail ) { return false; }
if ( empty( $this->plugin ) ) {
$this->plugin( 'plugin' );
}
$msg = sprintf(
'Abborting update of %s! '.
'Could not create a restore-point [%s]
%s',
ucwords( $this->plugin ),
$err_code,
$error
);
wp_die( $msg );
}
private function snapshot_collect( $data_list ) {
$dump = (object) array();
// Options.
$dump->options = array();
if ( isset( $data_list->options )
&& is_array( $data_list->options )
) {
foreach ( $data_list->options as $option ) {
$dump->options[$option] = get_option( $option );
}
}
// Posts and Post-Meta
$dump->posts = array();
$dump->postmeta = array();
if ( isset( $data_list->posts )
&& is_array( $data_list->posts )
) {
foreach ( $data_list->posts as $id ) {
$post = get_post( $id );
$meta = get_post_meta( $id );
// Flatten the meta values.
foreach ( $meta as $key => $values ) {
if ( is_array( $values ) && isset( $values[0] ) ) {
$meta[ $key ] = $values[0];
}
}
// Append the data to the dump.
if ( ! isset( $dump->posts[$post->post_type] ) ) {
$dump->posts[$post->post_type] = array();
$dump->postmeta[$post->post_type] = array();
}
$dump->posts[$post->post_type][$post->ID] = $post;
$dump->postmeta[$post->post_type][$post->ID] = $meta;
}
}
return $dump;
}
//Restores a saved snapshot.
public function restore( $snapshot ) {
global $wpdb;
// Get the contents of the snapshot file.
$data = $this->read_from_file( $snapshot );
if ( empty( $data ) ) {
return false;
}
// Decode the snapshot data to an PHP object.
$data = json_decode( $data, true );
if ( empty( $data ) ) {
return false;
}
// The restore-process is handled as execution transaction.
$this->clear();
// Options
if ( ! empty( $data['options'] ) && is_array( $data['options'] ) ) {
$sql_delete = "DELETE FROM {$wpdb->options} WHERE option_name IN ";
$sql_idlist = array();
$sql_insert = "INSERT INTO {$wpdb->options} (option_name, option_value) VALUES ";
$sql_values = array();
foreach ( $data['options'] as $key => $value ) {
$sql_idlist[] = $wpdb->prepare( '%s', $key );
$sql_values[] = $wpdb->prepare( '(%s,%s)', $key, maybe_serialize( $value ) );
}
if ( ! empty( $sql_values ) ) {
$this->add( $sql_delete . '(' . implode( ',', $sql_idlist ) . ')' );
$this->add( $sql_insert . implode( ",\n", $sql_values ) );
}
}
// Posts
if ( ! empty( $data['posts'] ) && is_array( $data['posts'] ) ) {
foreach ( $data['posts'] as $posttype => $items ) {
$sql_delete_post = "DELETE FROM {$wpdb->posts} WHERE ID IN ";
$sql_delete_meta = "DELETE FROM {$wpdb->postmeta} WHERE post_id IN ";
$sql_idlist = array();
$sql_insert = "INSERT INTO {$wpdb->posts} (ID, post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt, post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered, post_parent, guid, menu_order, post_type, post_mime_type, comment_count) VALUES ";
$sql_values = array();
foreach ( $items as $id => $post ) {
self::$core->array->equip(
$post,
'post_author',
'post_date',
'post_date_gmt',
'post_content',
'post_title',
'post_excerpt',
'post_status',
'comment_status',
'ping_status',
'post_password',
'post_name',
'to_ping',
'pinged',
'post_modified',
'post_modified_gmt',
'post_content_filtered',
'post_parent',
'guid',
'menu_order',
'post_type',
'post_mime_type',
'comment_count'
);
$sql_idlist[] = $wpdb->prepare( '%s', $id );
$sql_values[] = $wpdb->prepare(
'(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)',
$id,
$post['post_author'],
$post['post_date'],
$post['post_date_gmt'],
$post['post_content'],
$post['post_title'],
$post['post_excerpt'],
$post['post_status'],
$post['comment_status'],
$post['ping_status'],
$post['post_password'],
$post['post_name'],
$post['to_ping'],
$post['pinged'],
$post['post_modified'],
$post['post_modified_gmt'],
$post['post_content_filtered'],
$post['post_parent'],
$post['guid'],
$post['menu_order'],
$post['post_type'],
$post['post_mime_type'],
$post['comment_count']
);
}
while ( ! empty( $sql_idlist ) ) {
$values = array();
for ( $i = 0; $i < 100; $i += 1 ) {
if ( empty( $sql_idlist ) ) { break; }
$values[] = array_shift( $sql_idlist );
}
$this->add( $sql_delete_post . '(' . implode( ',', $values ) . ')' );
$this->add( $sql_delete_meta . '(' . implode( ',', $values ) . ')' );
}
while ( ! empty( $sql_values ) ) {
$values = array();
for ( $i = 0; $i < 100; $i += 1 ) {
if ( empty( $sql_values ) ) { break; }
$values[] = array_shift( $sql_values );
}
$this->add( $sql_insert . implode( ",\n", $values ) );
}
}
}
// Postmeta
if ( ! empty( $data['postmeta'] ) && is_array( $data['postmeta'] ) ) {
foreach ( $data['postmeta'] as $posttype => $items ) {
foreach ( $items as $id => $entries ) {
$sql_meta = "INSERT INTO {$wpdb->postmeta} (post_id,meta_key,meta_value) VALUES ";
$sql_values = array();
foreach ( $entries as $key => $value ) {
$sql_values[] = $wpdb->prepare( '(%s,%s,%s)', $id, $key, $value );
}
if ( ! empty( $sql_values ) ) {
$this->add( $sql_meta . implode( ",\n", $sql_values ) );
}
}
}
}
// Run all scheduled queries
foreach ( $this->commands as $key => $params ) {
if ( ! isset( $params[0] ) ) { continue; }
$query = $params[0];
$res = $wpdb->query( $query );
}
return true;
}
}