导入逻辑基本完成

This commit is contained in:
joyqi 2017-08-16 23:15:47 +08:00
parent 3d8fbc8a61
commit aa2eac4dcc
3 changed files with 160 additions and 129 deletions

View File

@ -1,115 +0,0 @@
<?php
/**
* 此类主要帮助用户在命令行界面对博客进行管理
*
* @package Helper
* @author joyqi
* @version 1.0.0
* @link http://typecho.org
*/
class CLI
{
/**
* @var array
*/
private $_args = array();
/**
* @var array
*/
private $_definition = array();
/**
* @var string
*/
private $_help = "Usage: php index.php [options] [--] [args...]\n";
/**
* CLI constructor.
*/
public function __construct()
{
$this->_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;
}
}
}
}

View File

@ -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);
}
/**
* 绑定动作
*/

View File

@ -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初始化 */