<?php

require_once __DIR__ . '/wp-config.php';

$old_site_url = "myopt.dev/";
$new_site_url = "myopt.webskisolution.com.au/";


// You can you ALL for all tables.
//$tables = "ALL";


// Important: Table names without $table_prefix from wp-config.php ( wp_ or other )
$tables = array( 
    'posts' => array(
        'post_content',
        'post_excerpt',
        'guid',
    ), 
    'postmeta' => array(
        'meta_value'
    ),
    'options' => array(
        'option_value'
    ) 
);

class Migrate extends Database{
    
    private $old_site_url = '';
    private $new_site_url = '';
    
    private $table_prefix = 'wp_';
    private $tables;
    
    private static $instance = false;
    
    public function __construct($old_url, $new_url, $tables = array( 'posts', 'options' => array('option_value'), 'postmeta' => array('meta_value'))) {
        global $table_prefix;
        parent::__construct(DB_USER, DB_PASSWORD, DB_HOST, DB_NAME);
        $this->old_site_url = $old_url;
        $this->new_site_url = $new_url;
        
        $this->table_prefix = $table_prefix;
        $this->tables = $tables;
        return $this;
    }

    public static function getInstance($old_url, $new_url, $tables){
        return self::$instance ? self::$instance : new Migrate($old_url, $new_url, $tables);
    }
    
    public function run(){
        $result = true;
        $rows_affected = array();
        
        if( $this->tables == 'ALL' ){
            $this->tables = array();
            $tables_db = $this->getRows('SHOW tables FROM ' . DB_NAME);
            foreach($tables_db as $key => $db_table){
                $tmp = (array)$db_table;
                $this->tables[] = array_shift($tmp);
            }
            $this->table_prefix = '';
        }
        
        if( count($this->tables) > 0 ){
            foreach($this->tables as $key => $columns){
                
                if( !is_array($columns) && is_string($columns) ){
                    $columns_array = array();
                    $table_name = $columns;
                    $table_pk = array();
                    $columns_db = $this->getRows('SHOW COLUMNS FROM ' . $this->table_prefix . $columns);
                    
                    foreach ($columns_db as $col){
                        if( $col->Key != 'PRI' ){
                            $columns_array[] = $col->Field;
                        }else{
                            $table_pk[] = $col->Field;
                        }
                    }
                }else{
                    $table_pk = array();
                    $columns_db = $this->getRows('SHOW COLUMNS FROM ' . $this->table_prefix . $key);
                    $table_name = $key;
                    $columns_array = array();
                    foreach ($columns_db as $col){
                        if( $col->Key != 'PRI' ){
                            if(in_array($col->Field, $columns) ){
                                $columns_array[] = $col->Field;
                            }
                        }else{
                            $table_pk[] = $col->Field;
                        }
                    }
                }
                
                if( $table_name != '' && count($columns_array) > 0 ){
                    
                    $rows_affected[$this->table_prefix . $table_name] = 0;
                    
                    $where_array = array();
                    foreach ($columns_array as $col){
                        $where_array[] = ( $col . ' LIKE "%'.$this->old_site_url . '%" ' );
                    }
                    
                    $query = " SELECT " . join(',', $table_pk) . ", " . join(',', $columns_array) . " FROM `{$this->table_prefix}{$table_name}` WHERE " . join(' OR ', $where_array);
                    $rows = $this->getRows($query);
                    foreach ($rows as $columns){
                        $condition_row = array();
                        $update_row = array();
                        foreach ($columns as $key => $value){
                            if( in_array($key, $table_pk) ){
                                $condition_row[$key] = $value;
                            }else if( trim($value) != '' ){
                                $new_value = $this->fix_string($value);
                                if( $new_value != $value ){
                                    $update_row[$key] = $new_value; 
                                }
                            }
                        }
                        
                        if( count($condition_row) > 0 && count($update_row) > 0 ){
                            $update_query = "UPDATE {$this->table_prefix}{$table_name} SET " . join(' = ?, ', array_keys($update_row)) . " = ? WHERE " . join(' = ?, ', array_keys($condition_row) ) . " = ? ";
                            $result = $result && $this->updateRow($update_query, array_merge(array_values($update_row), array_values($condition_row) ) );
                            $rows_affected[$this->table_prefix . $table_name]++;
                        }   
                    }
                }
            }
        }
        
        echo "<h3 style='color: " . ($result ? '#0f0' : "#f00") . "'>Result: " . ( $result ? 'success' : 'error') . '</h3><br/>';
        echo "Row Affected: ";
        echo "<pre>";
        print_r($rows_affected);
        echo "</pre>";
    }
    
    private function fix_string( $string ){
        if( $this->is_serialized($string) ){
            return $this->fix_serialized_string($string);
        }else if($this->is_json($string) ){
            return $this->fix_json_string($string);
        }else if(is_string($string)){
            return str_replace($this->old_site_url, $this->new_site_url, $string);
        }
        return $string;
    }
    
    private function fix_serialized_string( $string ){
        $data = unserialize($string);
        if(is_array($data) || is_object($data) ){
            foreach ($data as &$value){
                if(is_array($value)|| is_object($value) ){
                    $value = $this->fix_object($value);
                }else if( is_serialized($value) ){
                    $value = $this->fix_serialized_string($value);
                }else if( is_string($value) && $this->is_json($value) ){
                    $value = $this->fix_json_string($value);
                }else if(is_string($value)){
                    $value = str_replace($this->old_site_url, $this->new_site_url, $value);
                }
            }
        }
        return serialize($data);
    }
    
    private function fix_object( $data ){
        foreach ( $data as &$value ){
            if( is_array($value)|| is_object($value) ){
                $value = $this->fix_object($value);
            }else if(is_string($value)){
                $value = str_replace($this->old_site_url, $this->new_site_url, $value);
            }
        }
        return $data;
    }
    
    private function fix_json_string( $string ){
        $data = json_decode($string);
        foreach ($data as &$value){
            if(is_array($value)|| is_object($value) ){
                $value = $this->fix_object($value);
            }else if( is_serialized($value) ){
                $value = $this->fix_serialized_string($value);
            }else if(is_string($value) && $this->is_json($value) ){
                $value = $this->fix_json_string($value);
            }else if(is_string($value)){
                $value = str_replace($this->old_site_url, $this->new_site_url, $value);
            }
        }
        return json_encode($data);
    }
    
    function is_json($string) {
        return (json_last_error() == JSON_ERROR_NONE && (is_object($data) || is_array($data) ) );
    }
    
    private function is_serialized( $data, $strict = true ) {
        // if it isn't a string, it isn't serialized.
        if ( ! is_string( $data ) ) {
            return false;
        }
        $data = trim( $data );
        if ( 'N;' == $data ) {
            return true;
        }
        if ( strlen( $data ) < 4 ) {
            return false;
        }
        if ( ':' !== $data[1] ) {
            return false;
        }
        if ( $strict ) {
            $lastc = substr( $data, -1 );
            if ( ';' !== $lastc && '}' !== $lastc ) {
                return false;
            }
        } else {
            $semicolon = strpos( $data, ';' );
            $brace     = strpos( $data, '}' );
            // Either ; or } must exist.
            if ( false === $semicolon && false === $brace )
                return false;
            // But neither must be in the first X characters.
            if ( false !== $semicolon && $semicolon < 3 )
                return false;
            if ( false !== $brace && $brace < 4 )
                return false;
        }
        $token = $data[0];
        switch ( $token ) {
            case 's' :
                if ( $strict ) {
                    if ( '"' !== substr( $data, -2, 1 ) ) {
                        return false;
                    }
                } elseif ( false === strpos( $data, '"' ) ) {
                    return false;
                }
                // or else fall through
            case 'a' :
            case 'O' :
                return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
            case 'b' :
            case 'i' :
            case 'd' :
                $end = $strict ? '$' : '';
                return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
        }
        return false;
    }
}

class Database {

    public $isConnected;
    /**
     *
     * @var PDO
     */
    protected $datab;

    public function __construct($username = DB_USER, $password = DB_PASSWORD, $host = DB_HOST, $dbname = DB_NAME) {
        $this->isConnected = true;
        try {
            $this->datab = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8", $username, $password, array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8" ));
            $this->datab->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->datab->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        } catch (PDOException $e) {
            $this->isConnected = false;
            throw new Exception($e->getMessage());
        }
    }

    public function Disconnect() {
        $this->datab = null;
        $this->isConnected = false;
    }

    public function getRow($query, $params = array()) {
        try {
            $stmt = $this->datab->prepare($query);
            $stmt->execute($params);
            return $stmt->fetch(PDO::FETCH_OBJ);
        } catch (PDOException $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function getRows($query, $params = array()) {

        try {
            $stmt = $this->datab->prepare($query);
            $stmt->execute($params);
            return $stmt->fetchAll(PDO::FETCH_OBJ);
        } catch (PDOException $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function insertRow($query, $params) {

        try {
            $stmt = $this->datab->prepare($query);
            $stmt->execute($params);
            return $this->datab->lastInsertId();
        } catch (PDOException $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function updateRow($query, $params) {
        try {
            $stmt = $this->datab->prepare($query);
            $stmt->execute($params);
            return true;
        } catch (PDOException $e) {
            throw new Exception($e->getMessage());
        }
    }

    public function deleteRow($query, $params) {
        return $this->updateRow($query, $params);
    }

    public function beginTransaction() {
        $this->datab->beginTransaction();
    }

    public function inTransaction() {
        return $this->datab->inTransaction();
    }

    public function commit() {
        $this->datab->commit();
    }

    public function rollBack() {
        $this->datab->rollBack();
    }

}

Migrate::getInstance($old_site_url, $new_site_url, $tables)->run();