source: branches/version-2_13-dev/data/class/pages/admin/system/LC_Page_Admin_System_Bkup.php @ 23606

Revision 23606, 20.4 KB checked in by kimoto, 10 years ago (diff)

#2448 typo修正・ソース整形・ソースコメントの改善 for 2.13.3

 https://scrutinizer-ci.com/g/nobuhiko/EC-CUBE/inspections/e0f27994-b3c7-4fc6-ad70-55d3cd63769b/patches

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2/*
3 * This file is part of EC-CUBE
4 *
5 * Copyright(c) 2000-2014 LOCKON CO.,LTD. All Rights Reserved.
6 *
7 * http://www.lockon.co.jp/
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 */
23
24require_once CLASS_EX_REALDIR . 'page_extends/admin/LC_Page_Admin_Ex.php';
25
26/**
27 * バックアップ のページクラス.
28 *
29 * @package Page
30 * @author LOCKON CO.,LTD.
31 * @version $Id$
32 */
33class LC_Page_Admin_System_Bkup extends LC_Page_Admin_Ex
34{
35    /** リストア中にエラーが発生したか */
36    public $tpl_restore_err = false;
37
38    /** 対象外とするシーケンス生成器 */
39    public $arrExcludeSequence = array(
40        'plsql_profiler_runid', // Postgres Plus Advanced Server 9.1
41        'snapshot_num',         // Postgres Plus Advanced Server 9.1
42    );
43
44    /** ヘッダーを出力するか (cbOutputCSV 用) */
45    private $output_header = false;
46
47    /**
48     * Page を初期化する.
49     *
50     * @return void
51     */
52    public function init()
53    {
54        parent::init();
55        $this->tpl_mainpage = 'system/bkup.tpl';
56        $this->tpl_mainno = 'system';
57        $this->tpl_subno = 'bkup';
58        $this->tpl_maintitle = 'システム設定';
59        $this->tpl_subtitle = 'バックアップ管理';
60
61        $this->bkup_dir = DATA_REALDIR . 'downloads/backup/';
62        $this->bkup_ext = '.tar.gz';
63    }
64
65    /**
66     * Page のプロセス.
67     *
68     * @return void
69     */
70    public function process()
71    {
72        $this->action();
73        $this->sendResponse();
74    }
75
76    /**
77     * Page のアクション.
78     *
79     * @return void
80     */
81    public function action()
82    {
83        $objFormParam = new SC_FormParam_Ex;
84
85        // パラメーターの初期化
86        $this->initParam($objFormParam, $_POST);
87
88        $arrErrTmp  = array();
89        $arrForm = array();
90
91        $this->mode = $this->getMode();
92        switch ($this->mode) {
93            // バックアップを作成する
94            case 'bkup':
95
96                // データ型エラーチェック
97                $arrErrTmp[1] = $objFormParam->checkError();
98
99                // データ型に問題がない場合
100                if (SC_Utils_Ex::isBlank($arrErrTmp[1])) {
101                    // データ型以外のエラーチェック
102                    $arrErrTmp[2] = $this->lfCheckError($objFormParam->getHashArray(), $this->mode);
103                }
104
105                // エラーがなければバックアップ処理を行う
106                if (SC_Utils_Ex::isBlank($arrErrTmp[1]) && SC_Utils_Ex::isBlank($arrErrTmp[2])) {
107                    $arrData = $objFormParam->getHashArray();
108
109                    $work_dir = $this->bkup_dir . $arrData['bkup_name'] . '/';
110                    // バックアップデータの事前削除
111                    SC_Helper_FileManager_Ex::deleteFile($work_dir);
112                    // バックアップファイル作成
113                    $res = $this->lfCreateBkupData($arrData['bkup_name'], $work_dir);
114                    // バックアップデータの事後削除
115                    SC_Helper_FileManager_Ex::deleteFile($work_dir);
116
117                    $arrErrTmp[3] = array();
118                    if ($res !== true) {
119                        $arrErrTmp[3]['bkup_name'] = 'バックアップに失敗しました。(' . $res . ')';
120                    }
121
122                    // DBにデータ更新
123                    if (SC_Utils_Ex::isBlank($arrErrTmp[3])) {
124                        $this->lfUpdBkupData($arrData);
125                    } else {
126                        $arrForm = $arrData;
127                        $arrErr = $arrErrTmp[3];
128                    }
129
130                    $this->tpl_onload = "alert('バックアップ完了しました');";
131                } else {
132                    $arrForm = $objFormParam->getHashArray();
133                    $arrErr = array_merge((array) $arrErrTmp[1], (array) $arrErrTmp[2]);
134                }
135                break;
136
137            // リストア
138            case 'restore_config':
139            case 'restore':
140                // データベースに存在するかどうかチェック
141                $arrErr = $this->lfCheckError($objFormParam->getHashArray(), $this->mode);
142
143                // エラーがなければリストア処理を行う
144                if (SC_Utils_Ex::isBlank($arrErr)) {
145                    $arrData = $objFormParam->getHashArray();
146
147                    $msg = '「' . $arrData['list_name'] . '」のリストアを開始します。';
148                    GC_Utils_Ex::gfPrintLog($msg);
149
150                    $success = $this->lfRestore($arrData['list_name'], $this->bkup_dir, $this->bkup_ext, $this->mode);
151
152                    $msg = '「' . $arrData['list_name'] . '」の';
153                    $msg .= $success ? 'リストアを終了しました。' : 'リストアに失敗しました。';
154
155                    $this->tpl_restore_msg .= $msg . "\n";
156                    GC_Utils_Ex::gfPrintLog($msg);
157                }
158                break;
159
160            // 削除
161            case 'delete':
162
163                // データベースに存在するかどうかチェック
164                $arrErr = $this->lfCheckError($objFormParam->getHashArray(), $this->mode);
165
166                // エラーがなければリストア処理を行う
167                if (SC_Utils_Ex::isBlank($arrErr)) {
168                    $arrData = $objFormParam->getHashArray();
169
170                    // DBとファイルを削除
171                    $this->lfDeleteBackUp($arrData, $this->bkup_dir, $this->bkup_ext);
172                }
173
174                break;
175
176                // ダウンロード
177            case 'download' :
178
179                // データベースに存在するかどうかチェック
180                $arrErr = $this->lfCheckError($objFormParam->getHashArray(), $this->mode);
181
182                // エラーがなければダウンロード処理を行う
183                if (SC_Utils_Ex::isBlank($arrErr)) {
184                    $arrData = $objFormParam->getHashArray();
185
186                    $filename = $arrData['list_name'] . $this->bkup_ext;
187                    $dl_file = $this->bkup_dir.$arrData['list_name'] . $this->bkup_ext;
188
189                    // ダウンロード開始
190                    SC_Response_Ex::headerForDownload($filename);
191                    header('Content-Length: ' . filesize($dl_file));
192                    readfile($dl_file);
193                    SC_Response_Ex::actionExit();
194                    break;
195                }
196
197            default:
198                break;
199        }
200
201        // 不要になった変数を解放
202        unset($arrErrTmp);
203
204        // バックアップリストを取得する
205        $arrBkupList = $this->lfGetBkupData('ORDER BY create_date DESC');
206        // テンプレートファイルに渡すデータをセット
207        $this->arrErr = isset($arrErr) ? $arrErr : array();
208        $this->arrForm = isset($arrForm) ? $arrForm : array();
209        $this->arrBkupList = $arrBkupList;
210    }
211
212    /**
213     * パラメーター初期化.
214     *
215     * @param  SC_FormParam_Ex $objFormParam
216     * @param  array  $arrParams    $_POST値
217     * @return void
218     */
219    public function initParam(&$objFormParam, &$arrParams)
220    {
221        $objFormParam->addParam('バックアップ名', 'bkup_name', STEXT_LEN, 'a', array('EXIST_CHECK', 'MAX_LENGTH_CHECK', 'NO_SPTAB', 'FILE_NAME_CHECK_BY_NOUPLOAD'));
222        $objFormParam->addParam('バックアップメモ', 'bkup_memo', MTEXT_LEN, 'KVa', array('MAX_LENGTH_CHECK'));
223        $objFormParam->addParam('バックアップ名(リスト)', 'list_name', STEXT_LEN, 'a', array('MAX_LENGTH_CHECK', 'NO_SPTAB', 'FILE_NAME_CHECK_BY_NOUPLOAD'));
224        $objFormParam->setParam($arrParams);
225        $objFormParam->convParam();
226    }
227
228    /**
229     * データ型以外のエラーチェック.
230     *
231     * @param array  $arrForm
232     * @param string $mode
233     * @return $arrErr
234     */
235    public function lfCheckError(&$arrForm, $mode)
236    {
237        switch ($mode) {
238            case 'bkup':
239                $name = $arrForm['bkup_name'];
240                break;
241
242            case 'restore_config':
243            case 'restore':
244            case 'download':
245            case 'delete':
246                $name = $arrForm['list_name'];
247                break;
248
249            default:
250                trigger_error('不明な処理', E_USER_ERROR);
251                break;
252        }
253
254        // 重複・存在チェック
255        $ret = $this->lfGetBkupData('', $name);
256        if (count($ret) > 0 && $mode == 'bkup') {
257            $arrErr['bkup_name'] = 'バックアップ名が重複しています。別名を入力してください。';
258        } elseif (count($ret) <= 0 && $mode != 'bkup') {
259            $arrErr['list_name'] = '選択されたデータがみつかりませんでした。既に削除されている可能性があります。';
260        }
261
262        return $arrErr;
263    }
264
265    /**
266     * バックアップファイル作成.
267     *
268     * @param  string      $bkup_name
269     * @param string $work_dir
270     * @return boolean|int 結果。true:成功 int:失敗 FIXME 本来は int ではなく、エラーメッセージを戻すべき
271     */
272    public function lfCreateBkupData($bkup_name, $work_dir)
273    {
274        $objQuery =& SC_Query_Ex::getSingletonInstance();
275        $csv_autoinc = '';
276        $arrData = array();
277
278        $success = mkdir($work_dir, 0777, true);
279        if (!$success) {
280            return __LINE__;
281        }
282
283        // 全テーブル取得
284        $arrTableList = $objQuery->listTables();
285
286        // 各テーブル情報を取得する
287        foreach ($arrTableList as $table) {
288            if ($table == 'dtb_bkup' || $table == 'mtb_zip') {
289                continue;
290            }
291
292            // dataをCSV出力
293            $csv_file = $work_dir . $table . '.csv';
294            $this->fpOutput = fopen($csv_file, 'w');;
295            if (!$this->fpOutput) {
296                return __LINE__;
297            }
298
299            // 全データを取得
300            $sql = 'SELECT * FROM ' . $objQuery->conn->quoteIdentifier($table);
301
302            $this->output_header = true;
303            $success = $objQuery->doCallbackAll(array(&$this, 'cbOutputCSV'), $sql);
304
305            fclose($this->fpOutput);
306
307            if ($success === false) {
308                return __LINE__;
309            }
310
311            // タイムアウトを防ぐ
312            SC_Utils_Ex::sfFlush();
313        }
314
315        // 自動採番型の構成を取得する
316        $csv_autoinc = $this->lfGetAutoIncrement();
317
318        $csv_autoinc_file = $work_dir . 'autoinc_data.csv';
319
320        // CSV出力
321
322        // 自動採番をCSV出力
323        $fp = fopen($csv_autoinc_file, 'w');
324        if ($fp) {
325            if ($csv_autoinc != '') {
326                $success = fwrite($fp, $csv_autoinc);
327                if (!$success) {
328                    return __LINE__;
329                }
330            }
331            fclose($fp);
332        }
333
334        //圧縮フラグTRUEはgzip圧縮をおこなう
335        $tar = new Archive_Tar($this->bkup_dir . $bkup_name . $this->bkup_ext, TRUE);
336
337        //bkupフォルダに移動する
338        chdir($work_dir);
339
340        //圧縮をおこなう
341        $zip = $tar->create('./');
342
343        return true;
344    }
345
346    /**
347     * CSV作成 テンポラリファイル出力 コールバック関数
348     *
349     * @param  mixed   $data 出力データ
350     * @return boolean true (true:固定 false:中断)
351     */
352    public function cbOutputCSV($data)
353    {
354        // 1行目のみヘッダーを出力する
355        if ($this->output_header) {
356            fputcsv($this->fpOutput, array_keys($data));
357            $this->output_header = false;
358        }
359        fputcsv($this->fpOutput, $data);
360        SC_Utils_Ex::extendTimeOut();
361
362        return true;
363    }
364
365    /**
366     * シーケンス一覧をCSV出力形式に変換する.
367     *
368     * シーケンス名,シーケンス値 の形式に出力する.
369     *
370     * @return string シーケンス一覧の文字列
371     * @return string $ret
372     */
373    public function lfGetAutoIncrement()
374    {
375        $objQuery =& SC_Query_Ex::getSingletonInstance();
376        $arrSequences = $objQuery->listSequences();
377
378        foreach ($arrSequences as $name) {
379            if (in_array($name, $this->arrExcludeSequence, true)) {
380                continue 1;
381            }
382
383            // XXX SC_Query::currVal は、PostgreSQL で nextval と等しい値を戻すケースがある。欠番を生じうるが、さして問題無いと推測している。
384            $seq = $objQuery->currVal($name);
385
386            // TODO CSV 生成の共通処理を使う
387            $ret .= $name . ',';
388            $ret .= is_null($seq) ? '0' : $seq;
389            $ret .= "\r\n";
390        }
391
392        return $ret;
393    }
394
395    // バックアップテーブルにデータを更新する
396    public function lfUpdBkupData($data)
397    {
398        $objQuery =& SC_Query_Ex::getSingletonInstance();
399
400        $arrVal = array();
401        $arrVal['bkup_name'] = $data['bkup_name'];
402        $arrVal['bkup_memo'] = $data['bkup_memo'];
403        $arrVal['create_date'] = 'CURRENT_TIMESTAMP';
404
405        $objQuery->insert('dtb_bkup', $arrVal);
406    }
407
408    /**
409     * バックアップの一覧を取得する
410     */
411    public function lfGetBkupData($sql_option = '', $filter_bkup_name = '')
412    {
413        $objQuery =& SC_Query_Ex::getSingletonInstance();
414
415        // テーブルから取得
416        $arrVal = array();
417
418        $sql = 'SELECT bkup_name, bkup_memo, create_date FROM dtb_bkup';
419        if (strlen($filter_bkup_name) >= 1) {
420            $sql .= ' WHERE bkup_name = ?';
421            $arrVal[] = $filter_bkup_name;
422        }
423        if ($sql_option != '') {
424            $sql .= ' ' . $sql_option;
425        }
426
427        $ret = $objQuery->getAll($sql, $arrVal);
428
429        // ファイルのみのものを取得
430        $glob = glob($this->bkup_dir . '*' . $this->bkup_ext);
431        if (is_array($glob)) {
432            foreach ($glob as $path) {
433                $bkup_name = basename($path, $this->bkup_ext);
434                if (strlen($filter_bkup_name) >= 1 && $bkup_name !== $filter_bkup_name) {
435                    continue 1;
436                }
437                unset($row);
438                foreach ($ret as $key => $value) {
439                    if ($ret[$key]['bkup_name'] == $bkup_name) {
440                        $row =& $ret[$key];
441                    }
442                }
443                if (!isset($row)) {
444                    $ret[] = array();
445                    $row =& $ret[array_pop(array_keys($ret))];
446                    $row['bkup_name'] = $bkup_name;
447                    $row['bkup_memo'] = '(記録なし。バックアップファイルのみ。)';
448                    $row['create_date'] = date('Y-m-d H:i:s', filemtime($path));
449                }
450            }
451        }
452
453        return $ret;
454    }
455
456    /**
457     * バックアップファイルをリストアする
458     *
459     * @param  string $bkup_name
460     * @param  string $bkup_dir
461     * @param  string $bkup_ext
462     * @param string $mode
463     * @return boolean
464     */
465    public function lfRestore($bkup_name, $bkup_dir, $bkup_ext, $mode)
466    {
467        $objQuery =& SC_Query_Ex::getSingletonInstance();
468
469        $bkup_filepath = $bkup_dir . $bkup_name . $bkup_ext;
470        $work_dir = $bkup_dir . $bkup_name . '/';
471
472        //圧縮フラグTRUEはgzip解凍をおこなう
473        $tar = new Archive_Tar($bkup_filepath, TRUE);
474
475        //指定されたフォルダ内に解凍する
476        $success = $tar->extract($work_dir);
477
478        if (!$success) {
479            $msg = 'バックアップファイルの展開に失敗しました。' . "\n";
480            $msg .= '展開元: ' . $bkup_filepath . "\n";
481            $msg .= '展開先: ' . $work_dir;
482            trigger_error($msg, E_USER_ERROR);
483        }
484
485        // トランザクション開始
486        $objQuery->begin();
487
488        // INSERT実行
489        $success = $this->lfExeInsertSQL($objQuery, $work_dir, $mode);
490
491        // シーケンス生成器を復元する
492        if ($success) $this->restoreSequence($objQuery, $work_dir . 'autoinc_data.csv');
493
494        // リストア成功ならコミット失敗ならロールバック
495        if ($success) {
496            $objQuery->commit();
497            $this->tpl_restore_err = true;
498        } else {
499            $objQuery->rollback();
500            $this->tpl_restore_name = $bkup_name;
501        }
502
503        // FIXME この辺りで、バックアップ時と同等の一時ファイルの削除を実行すべきでは?
504
505        SC_Utils_Ex::extendTimeOut();
506
507        return $success;
508    }
509
510    /**
511     * CSVファイルからインサート実行.
512     *
513     * @param  SC_Query $objQuery
514     * @param  string $dir
515     * @param  string $mode
516     * @return boolean
517     */
518    public function lfExeInsertSQL(&$objQuery, $dir, $mode)
519    {
520        $tbl_flg = false;
521        $col_flg = false;
522        $ret = true;
523        $pagelayout_flg = false;
524        $arrVal = array();
525        $arrCol = array();
526        $arrAllTableList = $objQuery->listTables();
527
528        $objDir = dir($dir);
529        while (false !== ($file_name = $objDir->read())) {
530            if (!preg_match('/^((dtb|mtb|plg)_(\w+))\.csv$/', $file_name, $matches)) {
531                continue;
532            }
533            $file_path = $dir . $file_name;
534            $table = $matches[1];
535
536            // テーブル存在チェック
537            if (!in_array($table, $arrAllTableList)) {
538                if ($mode === 'restore_config') {
539                    continue;
540                }
541
542                return false;
543            }
544
545            // csvファイルからデータの取得
546            $fp = fopen($file_path, 'r');
547            if ($fp === false) {
548                trigger_error($file_name . ' のファイルオープンに失敗しました。', E_USER_ERROR);
549            }
550
551            GC_Utils_Ex::gfPrintLog('リストア実行: ' . $table);
552            $objQuery->delete($table);
553
554            $line = 0;
555            $arrColName = array();
556            while (!feof($fp)) {
557                $line++;
558                $arrCsvLine = fgetcsv($fp, 1024 * 1024);
559
560                // 1行目: 列名
561                if ($line === 1) {
562                    $arrColName = $arrCsvLine;
563                    continue;
564                }
565
566                // 空行を無視
567                // false との比較は PHP 5.2.x Windows バグ対応
568                // 参考: http://www.php.net/manual/ja/function.fgetcsv.php#98502
569                if ($arrCsvLine === array(null) || $arrCsvLine === false) {
570                    continue;
571                }
572
573                $arrVal = array_combine($arrColName, $arrCsvLine);
574                $objQuery->insert($table, $arrVal);
575
576                SC_Utils_Ex::extendTimeOut();
577            }
578
579            fclose($fp);
580        }
581
582        return $ret;
583    }
584
585    /**
586     * シーケンス生成器を復元する
587     * @param string $csv
588     * @param SC_Query $objQuery
589     */
590    public function restoreSequence(&$objQuery, $csv)
591    {
592        // csvファイルからデータの取得
593        $arrCsvData = file($csv);
594
595        foreach ($arrCsvData as $line) {
596            list($name, $currval) = explode(',', trim($line));
597
598            if (in_array($name, $this->arrExcludeSequence, true)) {
599                continue 1;
600            }
601
602            // FIXME テーブルと同様に整合チェックを行う。また不整合時はスキップして続行する。
603
604            // XXX +1 ではなく、nextVal を呼ぶべきかも。
605            $objQuery->setVal($name, $currval + 1);
606        }
607    }
608
609    // 選択したバックアップをDBから削除
610    public function lfDeleteBackUp(&$arrForm, $bkup_dir, $bkup_ext)
611    {
612        $objQuery =& SC_Query_Ex::getSingletonInstance();
613
614        $del_file = $bkup_dir.$arrForm['list_name'] . $bkup_ext;
615        // ファイルの削除
616        if (is_file($del_file)) {
617            $ret = unlink($del_file);
618        }
619
620        $objQuery->delete('dtb_bkup', 'bkup_name = ?', array($arrForm['list_name']));
621    }
622}
Note: See TracBrowser for help on using the repository browser.