From aa2eac4dcc3205cc9b21d4f30e3c66968470c051 Mon Sep 17 00:00:00 2001 From: joyqi Date: Wed, 16 Aug 2017 23:15:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=85=A5=E9=80=BB=E8=BE=91=E5=9F=BA?= =?UTF-8?q?=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- var/CLI.php | 115 ----------------------------- var/Widget/Backup.php | 163 ++++++++++++++++++++++++++++++++++++++++-- var/Widget/Init.php | 11 +-- 3 files changed, 160 insertions(+), 129 deletions(-) delete mode 100644 var/CLI.php diff --git a/var/CLI.php b/var/CLI.php deleted file mode 100644 index 9c075c60..00000000 --- a/var/CLI.php +++ /dev/null @@ -1,115 +0,0 @@ -_definition = array( - 'h' => array(_t('帮助信息')), - 'v' => array(_t('获取版本信息')), - 'x' => array(_t('导出数据')), - 'i' => array(_t('导入数据')) - ); - - $this->parseArgs(); - $this->parseDefinition(); - - switch (true) { - case !empty($this->_args['v']): - $this->handleVersion(); - break; - case !empty($this->_args['h']): - default: - echo $this->_help; - break; - } - } - - /** - * 获取版本信息 - */ - private function handleVersion() - { - echo 'Typecho ' . Typecho_Common::VERSION . "\n"; - echo 'PHP ' . phpversion() . "\n"; - echo Typecho_Db::get()->getVersion() . "\n"; - } - - /** - * 解析帮助信息 - */ - private function parseDefinition() - { - $splitted = false; - - foreach ($this->_definition as $key => $val) { - $placeholder = isset($val[1]) ? " <{$val[1]}>" : ''; - $prefix = strlen($key) > 1 ? '--' : '-'; - - if ($prefix == '--' && !$splitted) { - $this->_help .= "\n"; - $splitted = true; - } - - $this->_help .= "\n " . str_pad($prefix . $key . $placeholder, 28, ' ', STR_PAD_RIGHT) . $val[0]; - } - - $this->_help .= "\n\n"; - } - - /** - * 解析命令行参数 - */ - private function parseArgs() - { - global $argv; - - if ($argv[0] == $_SERVER['PHP_SELF']) { - array_shift($argv); - } - - $last = NULL; - - foreach ($argv as $arg) { - if (preg_match("/^\-\-([_a-z0-9-]+)(=(.+))?$/i", $arg, $matches)) { - $last = $matches[1]; - $val = isset($matches[3]) ? $matches[3] : true; - - $this->_args[$last] = $val; - } else if (preg_match("/^\-([a-z0-9])(.*)$/i", $arg, $matches)) { - $last = $matches[1]; - $val = $matches[2]; - - $this->_args[$last] = strlen($val) == 0 ? true : $val; - } else if (!empty($last)) { - $this->_args[$last] = $arg; - } - } - } -} diff --git a/var/Widget/Backup.php b/var/Widget/Backup.php index d08af34f..6202b4a0 100644 --- a/var/Widget/Backup.php +++ b/var/Widget/Backup.php @@ -14,17 +14,29 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit; */ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_Do { + const HEADER = '%TYPECHO_BACKUP_FILE%'; + /** * @var array */ - private $_types = [ + private $_types = array( 'contents' => 1, 'comments' => 2, 'metas' => 3, 'relationships' => 4, 'users' => 5, 'fields' => 6 - ]; + ); + + /** + * @var array + */ + private $_cleared = array(); + + /** + * @var bool + */ + private $_login = false; /** * @param $type @@ -41,15 +53,126 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_ $schema[$key] = strlen($val); $body .= $val; } - $schemaHeader = json_encode($schema); + $schemaHeader = Json::encode($schema); - $buffer .= pack('C', $type); // 写入类型 + $buffer .= pack('v', $type); // 写入类型 $buffer .= pack('v', strlen($schemaHeader)); // 写入头 + $buffer .= pack('v', strlen($body)); // 写入头 $buffer .= $schemaHeader . $body; return $buffer; } + /** + * 解析数据 + * + * @param $file + */ + private function extractData($file) + { + $fp = @fopen($file, 'rb'); + + if (!$fp) { + $this->widget('Widget_Notice')->set(_t('无法读取备份文件'), 'error'); + $this->response->goBack(); + } + + $fileHeader = @fread($fp, strlen(self::HEADER)); + + if (!$fileHeader || $fileHeader != self::HEADER) { + $this->widget('Widget_Notice')->set(_t('备份文件格式错误'), 'error'); + $this->response->goBack(); + } + + while (!feof($fp)) { + $type = unpack('v', fread($fp, 2))[0]; + $headerLen = unpack('v', fread($fp, 2))[0]; + $bodyLen = unpack('v', fread($fp, 2))[0]; + $header = fread($fp, $headerLen); + $body = fread($fp, $bodyLen); + + $this->processData($type, $header, $body); + } + + $this->widget('Widget_Notice')->set(_t('备份数据倒入完成'), 'success'); + $this->response->goBack(); + } + + /** + * @param $type + * @param $header + * @param $body + */ + private function processData($type, $header, $body) + { + $table = array_search($type, $this->_types); + + if (!empty($table)) { + $schema = Json::decode($header, true); + $data = []; + $offset = 0; + + foreach ($schema as $key => $val) { + $data[$key] = substr($body, $offset, $val); + $offset += $val; + } + + $this->importData($table, $data); + } else { + Typecho_Plugin::factory(__CLASS__)->import($type, $header, $body); + } + } + + /** + * 倒入单条数据 + * + * @param $table + * @param $data + */ + private function importData($table, $data) + { + $db = $this->db; + + try { + if (empty($this->_cleared[$table])) { + // 清除数据 + $db->query($db->delete('table.' . $table)); + $this->_cleared[$table] = true; + } + + if (!$this->_login && 'users' == $table && $data['group'] == 'administrator') { + // 重新登录 + $this->reLogin($data); + } + + $db->query($db->insert('table.' . $table)->rows($data)); + } catch (Exception $e) { + $this->widget('Widget_Notice')->set(_t('备份过程中遇到如下错误: %s', $e->getMessage()), 'error'); + $this->response->goBack(); + } + } + + /** + * 备份过程会重写用户数据 + * 所以需要重新登录当前用户 + * + * @param $user + */ + private function reLogin(&$user) + { + if (empty($user['authCode'])) { + $user['authCode'] = function_exists('openssl_random_pseudo_bytes') ? + bin2hex(openssl_random_pseudo_bytes(16)) : sha1(Typecho_Common::randString(20)); + } + + $user['activated'] = $this->options->gmtTime; + $user['logged'] = $user['activated']; + + Typecho_Cookie::set('__typecho_uid', $user['uid']); + Typecho_Cookie::set('__typecho_authCode', Typecho_Common::hash($user['authCode'])); + $this->_login = true; + } + /** * 导出数据 */ @@ -60,8 +183,8 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_ $this->response->setHeader('Content-Disposition', 'attachment; filename="' . date('Ymd') . '_' . $host . '_' . uniqid() . '.dat"'); - $buffer = ''; - $db = Typecho_Db::get(); + $buffer = self::HEADER; + $db = $this->db; foreach ($this->_types as $type => $val) { $page = 1; @@ -89,6 +212,34 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_ Typecho_Plugin::factory(__CLASS__)->export(); } + /** + * 倒入数据 + */ + private function import() + { + $path = NULL; + + if (!empty($_FILES)) { + $file = array_pop($_FILES); + + if (0 == $file['error'] && is_uploaded_file($file['tmp_name'])) { + $path = $file['tmp_name']; + } else { + $this->widget('Widget_Notice')->set(_t('备份文件上传失败'), 'error'); + $this->response->goBack(); + } + } else if ($this->request->is('file')) { + $path = __TYPECHO_BACKUP_DIR__ . '/' . $this->request->get('file'); + + if (!file_exists($path)) { + $this->widget('Widget_Notice')->set(_t('备份文件不存在'), 'error'); + $this->response->goBack(); + } + } + + $this->extractData($file); + } + /** * 绑定动作 */ diff --git a/var/Widget/Init.php b/var/Widget/Init.php index 51c5b328..3a21ae7c 100644 --- a/var/Widget/Init.php +++ b/var/Widget/Init.php @@ -23,10 +23,6 @@ class Widget_Init extends Typecho_Widget */ public function execute() { - if (php_sapi_name() == 'cli') { - define('__TYPECHO_CLI__', true); - } - /** 对变量赋值 */ $options = $this->widget('Widget_Options'); @@ -36,10 +32,9 @@ class Widget_Init extends Typecho_Widget Typecho_I18n::setLang($dir . '/' . $options->lang . '.mo'); } - /** 进入命令行格式 */ - if (defined('__TYPECHO_CLI__')) { - new CLI(); - exit(0); + /** 备份文件目录初始化 */ + if (!defined('__TYPECHO_BACKUP_DIR__')) { + define('__TYPECHO_BACKUP_DIR__', __TYPECHO_ROOT_DIR__ . '/usr/backups'); } /** cookie初始化 */