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

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