/** * Starts the package build process * @return DUP_Package */ public function Build() { global $wp_version; global $wpdb; global $current_user; $timerStart = DUP_Util::GetMicrotime(); $this->Archive->File = "{$this->NameHash}_archive.zip"; $this->Installer->File = "{$this->NameHash}_installer.php"; $this->Database->File = "{$this->NameHash}_database.sql"; //START LOGGING DUP_Log::Open($this->NameHash); $php_max_time = @ini_get("max_execution_time"); $php_max_memory = @ini_set('memory_limit', DUPLICATOR_PHP_MAX_MEMORY); $php_max_time = $php_max_time == 0 ? "(0) no time limit imposed" : "[{$php_max_time}] not allowed"; $php_max_memory = $php_max_memory === false ? "Unabled to set php memory_limit" : DUPLICATOR_PHP_MAX_MEMORY . " ({$php_max_memory} default)"; $info = "********************************************************************************\n"; $info .= "PACKAGE-LOG: " . @date("Y-m-d H:i:s") . "\n"; $info .= "NOTICE: Do NOT post to public sites or forums \n"; $info .= "********************************************************************************\n"; $info .= "VERSION:\t" . DUPLICATOR_VERSION . "\n"; $info .= "WORDPRESS:\t{$wp_version}\n"; $info .= "PHP INFO:\t" . phpversion() . ' | ' . 'SAPI: ' . php_sapi_name() . "\n"; $info .= "SERVER:\t\t{$_SERVER['SERVER_SOFTWARE']} \n"; $info .= "PHP TIME LIMIT: {$php_max_time} \n"; $info .= "PHP MAX MEMORY: {$php_max_memory} \n"; $info .= "MEMORY STACK: " . DUP_Server::GetPHPMemory(); DUP_Log::Info($info); $info = null; //CREATE DB RECORD $packageObj = serialize($this); if (!$packageObj) { DUP_Log::Error("Unable to serialize pacakge object while building record."); } $this->ID = $this->FindHashKey($this->Hash); if ($this->ID != 0) { $this->SetStatus(DUP_PackageStatus::START); } else { $results = $wpdb->insert($wpdb->prefix . "duplicator_packages", array('name' => $this->Name, 'hash' => $this->Hash, 'status' => DUP_PackageStatus::START, 'created' => current_time('mysql', get_option('gmt_offset', 1)), 'owner' => isset($current_user->user_login) ? $current_user->user_login : '******', 'package' => $packageObj)); if ($results == false) { $error_result = $wpdb->print_error(); DUP_Log::Error("Duplicator is unable to insert a package record into the database table.", "'{$error_result}'"); } $this->ID = $wpdb->insert_id; } //START BUILD //PHPs serialze method will return the object, but the ID above is not passed //for one reason or another so passing the object back in seems to do the trick $this->Database->Build($this); $this->Archive->Build($this); $this->Installer->Build($this); //INTEGRITY CHECKS DUP_Log::Info("\n********************************************************************************"); DUP_Log::Info("INTEGRITY CHECKS:"); DUP_Log::Info("********************************************************************************"); $dbSizeRead = DUP_Util::ByteSize($this->Database->Size); $zipSizeRead = DUP_Util::ByteSize($this->Archive->Size); $exeSizeRead = DUP_Util::ByteSize($this->Installer->Size); DUP_Log::Info("SQL File: {$dbSizeRead}"); DUP_Log::Info("Installer File: {$exeSizeRead}"); DUP_Log::Info("Archive File: {$zipSizeRead} "); if (!($this->Archive->Size && $this->Database->Size && $this->Installer->Size)) { DUP_Log::Error("A required file contains zero bytes.", "Archive Size: {$zipSizeRead} | SQL Size: {$dbSizeRead} | Installer Size: {$exeSizeRead}"); } //Validate SQL files completed $sql_tmp_path = DUP_UTIL::SafePath(DUPLICATOR_SSDIR_PATH_TMP . '/' . $this->Database->File); $sql_complete_txt = DUP_Util::TailFile($sql_tmp_path, 3); if (!strstr($sql_complete_txt, 'DUPLICATOR_MYSQLDUMP_EOF')) { DUP_Log::Error("ERROR: SQL file not complete. The end of file marker was not found. Please try to re-create the package."); } $timerEnd = DUP_Util::GetMicrotime(); $timerSum = DUP_Util::ElapsedTime($timerEnd, $timerStart); $this->Runtime = $timerSum; $this->ExeSize = $exeSizeRead; $this->ZipSize = $zipSizeRead; $this->buildCleanup(); //FINAL REPORT $info = "\n********************************************************************************\n"; $info .= "RECORD ID:[{$this->ID}]\n"; $info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n"; $info .= "PEAK PHP MEMORY USED: " . DUP_Server::GetPHPMemory(true) . "\n"; $info .= "DONE PROCESSING => {$this->Name} " . @date("Y-m-d H:i:s") . "\n"; DUP_Log::Info($info); DUP_Log::Close(); $this->SetStatus(DUP_PackageStatus::COMPLETE); return $this; }
/** * CREATE * Creates the zip file and adds the SQL file to the archive */ public static function Create(DUP_Archive $archive) { try { $timerAllStart = DUP_Util::GetMicrotime(); $package_zip_flush = DUP_Settings::Get('package_zip_flush'); self::$compressDir = rtrim(DUP_Util::SafePath($archive->PackDir), '/'); self::$sqlPath = DUP_Util::SafePath("{$archive->Package->StorePath}/{$archive->Package->Database->File}"); self::$zipPath = DUP_Util::SafePath("{$archive->Package->StorePath}/{$archive->File}"); self::$zipArchive = new ZipArchive(); self::$networkFlush = empty($package_zip_flush) ? false : $package_zip_flush; $filterDirs = empty($archive->FilterDirs) ? 'not set' : $archive->FilterDirs; $filterExts = empty($archive->FilterExts) ? 'not set' : $archive->FilterExts; $filterOn = $archive->FilterOn ? 'ON' : 'OFF'; //LOAD SCAN REPORT $json = file_get_contents(DUPLICATOR_SSDIR_PATH_TMP . "/{$archive->Package->NameHash}_scan.json"); self::$scanReport = json_decode($json); DUP_Log::Info("\n********************************************************************************"); DUP_Log::Info("ARCHIVE (ZIP):"); DUP_Log::Info("********************************************************************************"); $isZipOpen = self::$zipArchive->open(self::$zipPath, ZIPARCHIVE::CREATE) === TRUE; if (!$isZipOpen) { DUP_Log::Error("Cannot open zip file with PHP ZipArchive.", "Path location [" . self::$zipPath . "]"); } DUP_Log::Info("ARCHIVE DIR: " . self::$compressDir); DUP_Log::Info("ARCHIVE FILE: " . basename(self::$zipPath)); DUP_Log::Info("FILTERS: *{$filterOn}*"); DUP_Log::Info("DIRS: {$filterDirs}"); DUP_Log::Info("EXTS: {$filterExts}"); DUP_Log::Info("----------------------------------------"); DUP_Log::Info("COMPRESSING"); DUP_Log::Info("SIZE:\t" . self::$scanReport->ARC->Size); DUP_Log::Info("STATS:\tDirs " . self::$scanReport->ARC->DirCount . " | Files " . self::$scanReport->ARC->FileCount); //ADD SQL $isSQLInZip = self::$zipArchive->addFile(self::$sqlPath, "database.sql"); if ($isSQLInZip) { DUP_Log::Info("SQL ADDED: " . basename(self::$sqlPath)); } else { DUP_Log::Error("Unable to add database.sql to archive.", "SQL File Path [" . self::$sqlath . "]"); } self::$zipArchive->close(); self::$zipArchive->open(self::$zipPath, ZipArchive::CREATE); //ZIP DIRECTORIES foreach (self::$scanReport->ARC->Dirs as $dir) { if (self::$zipArchive->addEmptyDir(ltrim(str_replace(self::$compressDir, '', $dir), '/'))) { self::$countDirs++; } else { //Don't warn when dirtory is the root path if (strcmp($dir, rtrim(self::$compressDir, '/')) != 0) { DUP_Log::Info("WARNING: Unable to zip directory: '{$dir}'" . rtrim(self::$compressDir, '/')); } } } /* ZIP FILES: Network Flush * This allows the process to not timeout on fcgi * setups that need a response every X seconds */ if (self::$networkFlush) { foreach (self::$scanReport->ARC->Files as $file) { if (self::$zipArchive->addFile($file, ltrim(str_replace(self::$compressDir, '', $file), '/'))) { self::$limitItems++; self::$countFiles++; } else { DUP_Log::Info("WARNING: Unable to zip file: {$file}"); } //Trigger a flush to the web server after so many files have been loaded. if (self::$limitItems > DUPLICATOR_ZIP_FLUSH_TRIGGER) { $sumItems = self::$countDirs + self::$countFiles; self::$zipArchive->close(); self::$zipArchive->open(self::$zipPath); self::$limitItems = 0; DUP_Util::FcgiFlush(); DUP_Log::Info("Items archived [{$sumItems}] flushing response."); } } //Normal } else { foreach (self::$scanReport->ARC->Files as $file) { if (self::$zipArchive->addFile($file, ltrim(str_replace(self::$compressDir, '', $file), '/'))) { self::$countFiles++; } else { DUP_Log::Info("WARNING: Unable to zip file: {$file}"); } } } DUP_Log::Info(print_r(self::$zipArchive, true)); //-------------------------------- //LOG FINAL RESULTS DUP_Util::FcgiFlush(); $zipCloseResult = self::$zipArchive->close(); $zipCloseResult ? DUP_Log::Info("COMPRESSION RESULT: '{$zipCloseResult}'") : DUP_Log::Error("ZipArchive close failure.", "This hosted server may have a disk quota limit.\nCheck to make sure this archive file can be stored."); $timerAllEnd = DUP_Util::GetMicrotime(); $timerAllSum = DUP_Util::ElapsedTime($timerAllEnd, $timerAllStart); self::$zipFileSize = @filesize(self::$zipPath); DUP_Log::Info("COMPRESSED SIZE: " . DUP_Util::ByteSize(self::$zipFileSize)); DUP_Log::Info("ARCHIVE RUNTIME: {$timerAllSum}"); DUP_Log::Info("MEMORY STACK: " . DUP_Server::GetPHPMemory()); } catch (Exception $e) { DUP_Log::Error("Runtime error in package.archive.zip.php constructor.", "Exception: {$e}"); } }
private function phpDump() { global $wpdb; $wpdb->query("SET session wait_timeout = " . DUPLICATOR_DB_MAX_TIME); $handle = fopen($this->dbStorePath, 'w+'); $tables = $wpdb->get_col('SHOW TABLES'); $filterTables = isset($this->FilterTables) ? explode(',', $this->FilterTables) : null; $tblAllCount = count($tables); $tblFilterOn = $this->FilterOn ? 'ON' : 'OFF'; $qryLimit = DUP_Settings::Get('package_phpdump_qrylimit'); if (is_array($filterTables) && $this->FilterOn) { foreach ($tables as $key => $val) { if (in_array($tables[$key], $filterTables)) { unset($tables[$key]); } } } $tblCreateCount = count($tables); $tblFilterCount = $tblAllCount - $tblCreateCount; DUP_Log::Info("TABLES: total:{$tblAllCount} | filtered:{$tblFilterCount} | create:{$tblCreateCount}"); DUP_Log::Info("FILTERED: [{$this->FilterTables}]"); $sql_header = "/* DUPLICATOR MYSQL SCRIPT CREATED ON : " . @date("Y-m-d H:i:s") . " */\n\n"; $sql_header .= "SET FOREIGN_KEY_CHECKS = 0;\n\n"; fwrite($handle, $sql_header); //BUILD CREATES: //All creates must be created before inserts do to foreign key constraints foreach ($tables as $table) { //$sql_del = ($GLOBALS['duplicator_opts']['dbadd_drop']) ? "DROP TABLE IF EXISTS {$table};\n\n" : ""; //@fwrite($handle, $sql_del); $create = $wpdb->get_row("SHOW CREATE TABLE `{$table}`", ARRAY_N); @fwrite($handle, "{$create[1]};\n\n"); } //BUILD INSERTS: //Create Insert in 100 row increments to better handle memory foreach ($tables as $table) { $row_count = $wpdb->get_var("SELECT Count(*) FROM `{$table}`"); //DUP_Log::Info("{$table} ({$row_count})"); if ($row_count > $qryLimit) { $row_count = ceil($row_count / $qryLimit); } else { if ($row_count > 0) { $row_count = 1; } } if ($row_count >= 1) { fwrite($handle, "\n/* INSERT TABLE DATA: {$table} */\n"); } for ($i = 0; $i < $row_count; $i++) { $sql = ""; $limit = $i * $qryLimit; $query = "SELECT * FROM `{$table}` LIMIT {$limit}, {$qryLimit}"; $rows = $wpdb->get_results($query, ARRAY_A); if (is_array($rows)) { foreach ($rows as $row) { $sql .= "INSERT INTO `{$table}` VALUES("; $num_values = count($row); $num_counter = 1; foreach ($row as $value) { if (is_null($value) || !isset($value)) { $num_values == $num_counter ? $sql .= 'NULL' : ($sql .= 'NULL, '); } else { $num_values == $num_counter ? $sql .= '"' . @esc_sql($value) . '"' : ($sql .= '"' . @esc_sql($value) . '", '); } $num_counter++; } $sql .= ");\n"; } fwrite($handle, $sql); } } //Flush buffer if enabled if ($this->networkFlush) { DUP_Util::FcgiFlush(); } $sql = null; $rows = null; } $sql_footer = "\nSET FOREIGN_KEY_CHECKS = 1; \n\n"; $sql_footer .= "/* Duplicator WordPress Timestamp: " . date("Y-m-d H:i:s") . "*/\n"; $sql_footer .= "/* " . DUPLICATOR_DB_EOF_MARKER . " */\n"; fwrite($handle, $sql_footer); $wpdb->flush(); fclose($handle); }
/** * Starts the package build process * @return DUP_Package */ public function Build() { global $wp_version; global $wpdb; global $current_user; $timerStart = DUP_Util::GetMicrotime(); $this->Archive->File = "{$this->NameHash}_archive.zip"; $this->Installer->File = "{$this->NameHash}_installer.php"; $this->Database->File = "{$this->NameHash}_database.sql"; //START LOGGING DUP_Log::Open($this->NameHash); $php_max_time = @ini_get("max_execution_time"); $php_max_memory = @ini_set('memory_limit', DUPLICATOR_PHP_MAX_MEMORY); $php_max_time = $php_max_time == 0 ? "(0) no time limit imposed" : "[{$php_max_time}] not allowed"; $php_max_memory = $php_max_memory === false ? "Unabled to set php memory_limit" : DUPLICATOR_PHP_MAX_MEMORY . " ({$php_max_memory} default)"; $info = "********************************************************************************\n"; $info .= "PACKAGE-LOG: " . @date("Y-m-d H:i:s") . "\n"; $info .= "NOTICE: Do NOT post to public sites or forums \n"; $info .= "********************************************************************************\n"; $info .= "VERSION:\t" . DUPLICATOR_VERSION . "\n"; $info .= "WORDPRESS:\t{$wp_version}\n"; $info .= "PHP INFO:\t" . phpversion() . ' | ' . 'SAPI: ' . php_sapi_name() . "\n"; $info .= "SERVER:\t\t{$_SERVER['SERVER_SOFTWARE']} \n"; $info .= "PHP TIME LIMIT: {$php_max_time} \n"; $info .= "PHP MAX MEMORY: {$php_max_memory}"; DUP_Log::Info($info); unset($info); //CREATE DB RECORD $packageObj = serialize($this); if (!$packageObj) { DUP_Log::Error("Unable to serialize pacakge object while building record."); } $this->ID = $this->FindHashKey($this->Hash); if ($this->ID != 0) { $this->SetStatus(DUP_PackageStatus::START); } else { $results = $wpdb->insert($wpdb->prefix . "duplicator_packages", array('name' => $this->Name, 'hash' => $this->Hash, 'status' => DUP_PackageStatus::START, 'created' => current_time('mysql', get_option('gmt_offset', 1)), 'owner' => isset($current_user->user_login) ? $current_user->user_login : '******', 'package' => $packageObj)); if ($results == false) { $error_result = $wpdb->print_error(); DUP_Log::Error("Unable to insert record into database table.", "'{$error_result}'"); } $this->ID = $wpdb->insert_id; } //START BUILD //PHPs serialze method will return the object but the ID above is not passed //for one reason or another so passing the object back in seems to do the trick $this->Database->Build($this); $this->Archive->Build($this); $this->Installer->Build($this); //VALIDATE FILE SIZE $dbSizeRead = DUP_Util::ByteSize($this->Database->Size); $zipSizeRead = DUP_Util::ByteSize($this->Archive->Size); $exeSizeRead = DUP_Util::ByteSize($this->Installer->Size); if (!($this->Archive->Size && $this->Database->Size && $this->Installer->Size)) { DUP_Log::Error("A required file contains zero bytes.", "Archive Size: {$zipSizeRead} | SQL Size: {$dbSizeRead} | Installer Size: {$exeSizeRead}"); } $this->SetStatus(DUP_PackageStatus::COMPLETE); $timerEnd = DUP_Util::GetMicrotime(); $timerSum = DUP_Util::ElapsedTime($timerEnd, $timerStart); $this->Runtime = $timerSum; $this->ExeSize = $exeSizeRead; $this->ZipSize = $zipSizeRead; $this->buildCleanup(); $info = "\n********************************************************************************\n"; $info .= "RECORD ID:[{$this->ID}]\n"; $info .= "FILE SIZE: Archive:{$zipSizeRead} | SQL:{$dbSizeRead} | Installer:{$exeSizeRead}\n"; $info .= "TOTAL PROCESS RUNTIME: {$timerSum}\n"; $info .= "DONE PROCESSING => {$this->Name} " . @date("Y-m-d H:i:s") . "\n"; $info .= "********************************************************************************\n"; DUP_Log::Info($info); DUP_Log::Close(); return $this; }
private static function flushResponse() { //Check if were over our count*/ if (self::$limitItems > self::$limit) { $sumItems = self::$countDirs + self::$countFiles + self::$countLinks; self::$zipArchive->close(); self::$zipArchive->open(self::$zipPath); self::$limitItems = 0; DUP_Util::FcgiFlush(); DUP_Log::Info("Items archived [{$sumItems}] flushing response."); } }
/** * createFromTemplate * Generates the final installer file from the template file */ private function createFromTemplate($template) { global $wpdb; DUP_Log::Info("Preping for use"); $installer = DUP_Util::SafePath(DUPLICATOR_SSDIR_PATH_TMP) . "/{$this->Package->NameHash}_installer.php"; //Option values to delete at install time $deleteOpts = $GLOBALS['DUPLICATOR_OPTS_DELETE']; $replace_items = array("fwrite_url_old" => get_option('siteurl'), "fwrite_package_name" => "{$this->Package->NameHash}_archive.zip", "fwrite_package_notes" => $this->Package->Notes, "fwrite_secure_name" => $this->Package->NameHash, "fwrite_url_new" => $this->Package->Installer->OptsURLNew, "fwrite_dbhost" => $this->Package->Installer->OptsDBHost, "fwrite_dbport" => $this->Package->Installer->OptsDBPort, "fwrite_dbname" => $this->Package->Installer->OptsDBName, "fwrite_dbuser" => $this->Package->Installer->OptsDBUser, "fwrite_dbpass" => '', "fwrite_ssl_admin" => $this->Package->Installer->OptsSSLAdmin, "fwrite_ssl_login" => $this->Package->Installer->OptsSSLLogin, "fwrite_cache_wp" => $this->Package->Installer->OptsCacheWP, "fwrite_cache_path" => $this->Package->Installer->OptsCachePath, "fwrite_wp_tableprefix" => $wpdb->prefix, "fwrite_opts_delete" => json_encode($deleteOpts), "fwrite_blogname" => esc_html(get_option('blogname')), "fwrite_wproot" => DUPLICATOR_WPROOTPATH, "fwrite_duplicator_version" => DUPLICATOR_VERSION); if (file_exists($template) && is_readable($template)) { $err_msg = "ERROR: Unable to read/write installer. \nERROR INFO: Check permission/owner on file and parent folder.\nInstaller File = <{$installer}>"; $install_str = $this->parseTemplate($template, $replace_items); empty($install_str) ? DUP_Log::Error("{$err_msg}", "DUP_Installer::createFromTemplate => file-empty-read") : DUP_Log::Info("Template parsed with new data"); //INSTALLER FILE $fp = !file_exists($installer) ? fopen($installer, 'x+') : fopen($installer, 'w'); if (!$fp || !fwrite($fp, $install_str, strlen($install_str))) { DUP_Log::Error("{$err_msg}", "DUP_Installer::createFromTemplate => file-write-error"); } @fclose($fp); } else { DUP_Log::Error("Installer Template missing or unreadable.", "Template [{$template}]"); } @unlink($template); DUP_Log::Info("Complete [{$installer}]"); }