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

Revision 22857, 20.3 KB checked in by Seasoft, 11 years ago (diff)

#2043 (typo修正・ソース整形・ソースコメントの改善 for 2.13.0)

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