プログラムソース

<?php
/**
 * PHP Rampartクラス
 *
 * セキュリティ対策についてあまり詳しくない初心者の方でも、
 * 簡単にセキュリティ対策が施せるように設計された
 * PHPテンプレートエンジンです。
 *
 * @copyright  2008 kude
 * @license    http://rampart.kudelab.com/license.html
 * @version    Release: 1.0.0
 * @link       http://rampart.kudelab.com/
 */
class Rampart
{
    // バージョン
    const VERSION = '1.0.0';
    
    // 文字コード
    private $_encoding = 'UTF-8';
    private $_encodingList = 'auto';
    
    // 変数を保持する
    private $_values = array();
    private $_hscKeys = array();
    private $_outputVals;
    
    // trim するかしないかのフラグ
    private $_trimFlag = true;
    
    // 半角カナを全角カナに変換するかしないかのフラグ
    private $_doubleKana = true;
    
    // キャッシュ条件
    public $cacheObj = null;
    private $_cacheDir;
    
    // preg_matchパターン
    const NUMBER_PATTERN = '/[\D]/';
    const ALPHANUM_PATTERN = '/[^a-z0-9]/i';
    const WORD_PATTERN = '/[^-\w]/';
    
    // switch 用の値
    const BOOLEAN_ID = 0;
    const INT_ID = 1;
    const FLOAT_ID = 2;
    const NOHSC_ID = 3;
    const WITHOUT_ID = 4;
    
    // クライアントキャッシュの指定
    const CACHE_PUBLIC = 'public';
    const CACHE_PRIVATE = 'private_no_expire';
    const CACHE_PRIVATE_EXPIRE = 'private';
    const CACHE_NO = 'no-cache';
    
    // デリミター
    private $_leftDelimiter = '{';
    private $_rightDelimiter = '}';
    
    // literalフラグ
    private $_literalFlag = false;
    
    // 文字コードを設定する
    // 引数:文字コード、検出順序、置換文字設定、ヘッダ出力フラグ
    // 戻り値:なし
    public function setEncoding($code, $list = 'UTF-8,EUCJP-WIN,SJIS-WIN,JIS,ASCII', $substrchar='none', $headerFlag = true)
    {
        $this->_encoding = $code;
        mb_internal_encoding($code);
        $this->_encodingList = $list;
        mb_detect_order($list);
        
        // 置換文字を設定
        if (!is_null($substrchar)) {
            mb_substitute_character($substrchar);
        }

        // 文字コードをヘッダ出力
        if ($headerFlag) {
            header('Content-type: text/html; charset=' . $code);
        }
    }
    
    // 文字コードを指定文字コードに変換する
    // 引数:変換前の文字列
    // 戻り値:変換後の文字列
    public function encode($str)
    {
       return mb_convert_encoding($str, $this->_encoding, $this->_encodingList);
    }

    // 全角英数とスペースを半角にする
    // 引数:変換前の文字列
    // 戻り値:変換後の文字列
    public function oneByteChar($str)
    {
        return mb_convert_kana($str, 'as', $this->_encoding);
    }
    
    // 自動的に trim をかけるかどうかの設定
    // 引数:true なら自動的に trim をかける、false ならかけない
    public function setTrimFlag($flag)
    {
        $this->_trimFlag = $flag;
    }
    
    // 自動的に半角カナを全角カナに変換するかしないかの設定
    // 引数:true なら変換する、false なら変換しない
    public function setDoubleByteKanaFlag($flag)
    {
        $this->_doubleKana = $flag;
    }
    
    // 変数の値を文字列としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function set($key, $val, $length = 0)
    {
        $this->_values[$key] = $this->_setCore($key, $val, $length);
        $this->_hscKeys[$key] = true;
    }
    
    // 変数の値を hsc を適用しない文字列としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setNoHsc($key, $val, $length = 0)
    {
        $this->setWithoutChange($key, $this->_setCore($key, $val, $length));
    }

    // set, setHsc の共通処理
    private function _setCore($key, $val, $length) {
        if ($this->_trimFlag) {
            $val = trim($val);
        }
        $val = str_replace("\x0D\x0A", "\n", $val);
        $val = str_replace("\x0D", "\n", $val);
        $val = str_replace("\x0A", "\n", $val);
        $val = str_replace("\0", "", $val);
        $val = $this->encode($val);
        if ($this->_doubleKana) {
            $val = mb_convert_kana($val, 'KV', $this->_encoding);
        }
        
        // 不正なエンコーディングによる攻撃の可能性をチェック
        if (!mb_check_encoding($val, $this->_encoding)) {
            $val = '';
        }
        else {
            // 文字長が指定された場合、丸める
            $val = $this->_lengthCut($val, $length);
        }
        
        return $val;
    }
    
    // 文字長が指定された場合、オーバーした分をカットする共通処理
    private function _lengthCut($val, $length)
    {
        if ($length > 0) {
            $val = mb_substr($val, 0, $length);
        }
        return $val;
    }
    
    
    // 変数の値を hsc を適用しない文字列としてセットする
    // 改行コードの統一や文字コードの変換などの処理を行わない
    // 引数:セットするキー、セットする値
    public function  setWithoutChange($key, $val)
    {
        $this->_values[$key] = $val;
        unset($this->_hscKeys[$key]);
    }
    
    // 変数の値をブール値としてセットする
    // 引数:セットするキー、セットする値
    public function setBoolean($key, $val)
    {
        $val = $this->encode($val);
        $val = trim($this->oneByteChar($val));
        if ($val == 'false') {
            $val = false;
        }
        else {
            $val = (boolean)$val;
        }
        $this->setWithoutChange($key, $val);
    }
    
    // 変数の値を整数値としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setInt($key, $val, $length = 0)
    {
        $val = $this->encode($val);
        $val = trim($this->oneByteChar($val));
        $val = $this->_lengthCut($val, $length);
        $this->setWithoutChange($key, (int)$val);
    }
    
    // 変数の値を浮動小数点値としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setFloat($key, $val, $length = 0)
    {
        $val = $this->encode($val);
        $val = trim($this->oneByteChar($val));
        $val = $this->_lengthCut($val, $length);
        $this->setWithoutChange($key, (float)$val);
    }
    
    // 変数の値を 数値 としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setNumber($key, $val, $length = 0)
    {
        $val = $this->encode($val);
        $val = preg_replace(self::NUMBER_PATTERN, '', $this->oneByteChar($val));
        $val = $this->_lengthCut($val, $length);
        $this->setWithoutChange($key, $val);
    }
    
    // 変数の値を 英数 としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setAlphanum($key, $val, $length = 0)
    {
        $val = $this->encode($val);
        $val = preg_replace(self::ALPHANUM_PATTERN, '', $this->oneByteChar($val));
        $val = $this->_lengthCut($val, $length);
        $this->setWithoutChange($key, $val);
    }
    
    // 変数の値を 英数_- としてセットする
    // 引数:セットするキー、セットする値、文字数制限
    public function setWord($key, $val, $length = 0)
    {
        $val = $this->encode($val);
        $val = preg_replace(self::WORD_PATTERN, '', $this->oneByteChar($val));
        $val = $this->_lengthCut($val, $length);
        $this->setWithoutChange($key, $val);
    }
    
    // 配列の値を一度にセットする
    // 引数:配列、セットするキー
    //    セットするキーの個数は、0以上の可変長
    public function setFromArray()
    {
        $this->_arrayValues(func_get_args());
    }
    public function setNoHscFromArray()
    {
        $this->_arrayValues(func_get_args(), self::NOHSC_ID);
    }
    public function setWithoutChangeFromArray()
    {
        $this->_arrayValues(func_get_args(), self::WITHOUT_ID);
    }
    public function setBooleanFromArray()
    {
        $this->_arrayValues(func_get_args(), self::BOOLEAN_ID);
    }
    public function setIntFromArray()
    {
        $this->_arrayValues(func_get_args(), self::INT_ID);
    }
    public function setFloatFromArray()
    {
        $this->_arrayValues(func_get_args(), self::FLOAT_ID);
    }
    public function setNumberFromArray()
    {
        $this->_arrayValues(func_get_args(), self::NUMBER_PATTERN);
    }
    public function setAlphanumFromArray()
    {
        $this->_arrayValues(func_get_args(), self::ALPHANUM_PATTERN);
    }
    public function setWordFromArray()
    {
        $this->_arrayValues(func_get_args(), self::WORD_PATTERN);
    }
    private function _arrayValues(&$args, $pattern = null)
    {
        $params =& $args;
        $target = array_shift($params);
        $num = count($params);
        if ($num == 0) {
            $params =  array_keys($target);
            $num = count($params);
        }
        for($i = 0; $i < $num; $i++) {
            $key = $params[$i];
            if ($pattern === self::BOOLEAN_ID) {
                $this->setBoolean($key, $target[$key]);
            }
            else if ($pattern === self::INT_ID) {
                $this->setInt($key, $target[$key]);
            }
            else if ($pattern === self::FLOAT_ID) {
                $this->setFloat($key, $target[$key]);
            }
            else if ($pattern === self::NUMBER_PATTERN) {
                $this->setNumber($key, $target[$key]);
            }
            else if ($pattern === self::ALPHANUM_PATTERN) {
                $this->setAlphanum($key, $target[$key]);
            }
            else if ($pattern === self::WORD_PATTERN) {
                $this->setWord($key, $target[$key]);
            }
            else if ($pattern === self::NOHSC_ID) {
                $this->setNoHsc($key, $target[$key]);
            }
            else if ($pattern === self::WITHOUT_ID) {
                $this->setWithoutChange($key, $target[$key]);
            }
            else {
                $this->set($key, $target[$key]);
            }
        }
    }
    
    // 指定したキーの値を返す
    // 引数:取得するキー
    // 戻り値:キーに対応する値、キーを省略した場合はすべてのキーと値の連想配列
    public function get($key = null)
    {
        if (is_null($key)) {
            return $this->_values;
        }
        else {
            return $this->_values[$key];
        }
    }
    
    // 指定したキーの値を hsp を施して返す
    // 引数:取得するキー
    // 戻り値:キーに対応する値、キーを省略した場合はすべてのキーと値の連想配列
    public function getHsc($key = null)
    {
        if (is_null($key)) {
            $hscVals = array();
            foreach($this->_values as $key => $val) {
                if (array_key_exists($key, $this->_hscKeys)) {
                    $hscVals[$key] = $this->hsc($val);
                }
                else {
                    $hscVals[$key] = $val;
                }
            }
            return $hscVals;
        }
        else if (array_key_exists($key, $this->_hscKeys)) {
            return $this->hsc($this->_values[$key]);
        }
        else {
            return $this->_values[$key];
        }
    }
    
    // 指定したキーの値を削除する
    // 引数:削除するキー
    //    キーを指定しない場合は、すべて削除
    public function del($key = null)
    {
        if (is_null($key)) {
            $this->_values = array();
        }
        else {
            unset( $this->_values[$key] );
        }
    }
    
    // 文字数制限の指定
    // 引数:文字数、セットするキー
    //    セットするキーの個数は、0以上の可変長
    public function setLength()
    {
        $length = func_get_arg(0);
        if ($length > 0) {
            // 文字数を制限するキーの配列
            // 省略すると、現在セットされているキーすべて
            $num = func_num_args();
            if ($num > 1) {
                $keys = array_splice(func_get_args(), 1);
                $num--;
            }
            else {
                $keys = array_keys($this->_values);
                $num = count($keys);
            }
            foreach($keys as $key) {
                $this->_values[$key] = $this->_lengthCut($this->_values[$key], $length);
            }
        }
    }
    
    // htmlspecialchars をかける
    // 引数:対象となる文字列あるいは配列
    //    引数に配列を指定すると、配列の値すべてに適用する
    public function hsc($target)
    {
        if (is_array($target)) {
            return array_map(array(&$this, 'hsc'), $target);
        }
        else {
            return htmlspecialchars($target, ENT_QUOTES, $this->_encoding);
        }
    }
    
    // デリミタを設定する
    // 引数:左デリミタ、右デリミタ
    public function setDelimiter($left, $right)
    {
        $this->_leftDelimiter = $left;
        $this->_rightDelimiter = $right;
    }
    
    // テンプレートに変数を展開して出力
    // 引数:テンプレートファイル
    public function display($file)
    {
        // htmlspecialchars をかける
        $this->_hscValues();
        
        // テンプレートファイルをオープン
        if ($fh = fopen($file, 'r')) {
            $ld = $this->_leftDelimiter;
            $rd = $this->_rightDelimiter;
            $ldLen = mb_strlen($ld);
            $rdLen = mb_strlen($rd);
            $lindex = false;
            $rindex = false;
            while(!feof($fh)) {
                $buffer = fgets($fh);
                $cnt = 0;
                while(($lindex = mb_strpos($buffer, $ld)) !== false) {
                    echo mb_substr($buffer, 0, $lindex);
                    $buffer = mb_substr($buffer, $lindex + $ldLen);
                    $rindex = mb_strpos($buffer, $rd);
                    if ($rindex !== false) {
                        echo $this->_code2val(mb_substr($buffer, 0, $rindex));
                        $buffer = mb_substr($buffer, $rindex + $rdLen);
                    }
                }
                echo $buffer;
            }
            fclose($fh);
        }
        unset($this->_outputVals);
    }
    
    // テンプレートに変数を展開して文字列として返す
    // 引数:テンプレートファイル
    // 戻り値:展開後の文字列
    public function fetch($file)
    {
        // htmlspecialchars をかける
        $this->_hscValues();
        
        // テンプレートファイルをオープン
        $str = '';
        if ($fh = fopen($file, 'r')) {
            $ld = $this->_leftDelimiter;
            $rd = $this->_rightDelimiter;
            $ldLen = mb_strlen($ld);
            $rdLen = mb_strlen($rd);
            $lindex = false;
            $rindex = false;
            while(!feof($fh)) {
                $buffer = fgets($fh);
                $cnt = 0;
                while(($lindex = mb_strpos($buffer, $ld)) !== false) {
                    $str .= mb_substr($buffer, 0, $lindex);
                    $buffer = mb_substr($buffer, $lindex + $ldLen);
                    $rindex = mb_strpos($buffer, $rd);
                    if ($rindex !== false) {
                        $str .= $this->_code2val(mb_substr($buffer, 0, $rindex));
                        $buffer = mb_substr($buffer, $rindex + $rdLen);
                    }
                }
                $str .= $buffer;
            }
            fclose($fh);
        }
        unset($this->_outputVals);
        
        return $str;
    }
    
    // テンプレートを文字列として与える display
    // 引数:テンプレート文字列
    public function strDisplay($strTemplate)
    {
        // htmlspecialchars をかける
        $this->_hscValues();
        
        // テンプレートを解析
        $ld = $this->_leftDelimiter;
        $rd = $this->_rightDelimiter;
        $ldLen = mb_strlen($ld);
        $rdLen = mb_strlen($rd);
        $lindex = false;
        $rindex = false;
        $cnt = 0;
        while(($lindex = mb_strpos($strTemplate, $ld)) !== false) {
            echo mb_substr($strTemplate, 0, $lindex);
            $strTemplate = mb_substr($strTemplate, $lindex + $ldLen);
            $rindex = mb_strpos($strTemplate, $rd);
            if ($rindex !== false) {
                echo $this->_code2val(mb_substr($strTemplate, 0, $rindex));
                $strTemplate = mb_substr($strTemplate, $rindex + $rdLen);
            }
        }
        echo $strTemplate;
        unset($this->_outputVals);
    }
    
    // テンプレートを文字列として与える fetch
    // 引数:テンプレート文字列
    public function strFetch($strTemplate)
    {
        // htmlspecialchars をかける
        $this->_hscValues();
        
        // テンプレートを解析
        $str = '';
        $ld = $this->_leftDelimiter;
        $rd = $this->_rightDelimiter;
        $ldLen = mb_strlen($ld);
        $rdLen = mb_strlen($rd);
        $lindex = false;
        $rindex = false;
        $cnt = 0;
        while(($lindex = mb_strpos($strTemplate, $ld)) !== false) {
            $str .= mb_substr($strTemplate, 0, $lindex);
            $strTemplate = mb_substr($strTemplate, $lindex + $ldLen);
            $rindex = mb_strpos($strTemplate, $rd);
            if ($rindex !== false) {
                $str .= $this->_code2val(mb_substr($strTemplate, 0, $rindex));
                $strTemplate = mb_substr($strTemplate, $rindex + $rdLen);
            }
        }
        $str .= $strTemplate;
        unset($this->_outputVals);
        
        return $str;
    }
    
    // テンプレートエンジンの作業用メソッド
    private function _hscValues()
    {
        // htmlspecialchars をかける
        $this->_outputVals = array();
        foreach($this->_values as $key => $val) {
            if (array_key_exists($key, $this->_hscKeys)) {
                $this->_outputVals[$key] = $this->hsc($val);
            }
            else {
                $this->_outputVals[$key] = $val;
            }
        }
    }
    private function _code2val($code)
    {
        if ($this->_literalFlag) {
            if ($code == '/literal') {
                $this->_literalFlag = false;
            }
            else {
                return $this->_leftDelimiter . $code . $this->_rightDelimiter;
            }
        }
        else if (array_key_exists($code, $this->_outputVals)) {
            return $this->_outputVals[$code];
        }
        else if ($code == '#literal') {
            $this->_literalFlag = true;
        }
        else if ($code == '#ld') {
            return $this->_leftDelimiter;
        }
        else if ($code == '#rd') {
            return $this->_rightDelimiter;
        }
        else if (substr($code, 0, 5) == '#file') {
            return file_get_contents( substr($code, strpos($code, '"', 5) + 1, -1) );
        }
        else if (substr($code, 0, 8) == '#include') {
            ob_start();
            require( substr($code, strpos($code, '"', 8) + 1, -1) );
            $str = ob_get_contents();
            ob_end_clean();
            return $str;
        }
    }

    // キャッシュ条件を設定する
    // 引数:キャッシュディレクトリ、有効時間、クリーニング率、キャッシュディレクトリレベル
    public function setCache($cacheDir, $lifetime = null, $cleaning = 100, $dirlevel = 0)
    {
        // Cache_Lite オブジェクトを生成する
        require_once('Cache/Lite/Output.php');
        if (substr($cacheDir, -1, 1) != '/') {
            $cacheDir .= '/';
        }
        $options = array(
              'cacheDir' => $cacheDir
            , 'lifeTime' => $lifetime
            , 'automaticCleaningFactor' => $cleaning
            , 'hashedDirectoryLevel' => $dirlevel
        );
        $this->cacheObj = new Cache_Lite_Output($options);
        $this->_cacheDir = $cacheDir;
    }
    
    // キャッシュする
    // 引数:キャッシュする文字列、キャッシュID、キャッシュグループ
    // 戻り値:成功すれば true、失敗すれば false
    public function saveCache($str, $id, $group = 'default')
    {
        return $this->cacheObj->save($str, $id, $group);
    }
    
    // キャッシュデータを取得する
    // 引数:キャッシュID、キャッシュグループ
    // 戻り値:有効なキャッシュデータがあればその文字列を、なければ false
    public function getCache($id, $group = 'default')
    {
        return $this->cacheObj->get($id, $group);
    }
    
    // 出力キャッシュを開始する
    // 有効なキャッシュがある場合はそれを出力して false を返す
    // 引数:キャッシュID、キャッシュグループ
    public function startOutputCache($id, $group = 'default')
    {
        return !$this->cacheObj->start($id, $group);
    }
    
    // 出力キャッシュを終了する
    public function endOutputCache()
    {
        $this->cacheObj->end();
    }
    
    // 指定したキャッシュを削除する
    // 引数:キャッシュID、キャッシュグループ
    public function removeCache($id, $group = 'default')
    {
        return $this->cacheObj->remove($id, $group);
    }
    
    // すべてのキャッシュを削除する
    public function cleanAllCache()
    {
        return $this->cacheObj->clean();
    }
    
    // 無効なキャッシュを削除する
    public function cleanOldCache()
    {
        return $this->cacheObj->clean(false, 'old');
    }
    
    // 指定したグループのキャッシュを削除する
    // 引数:キャッシュグループ
    public function cleanGroupCache($group = 'default')
    {
        return $this->cacheObj->clean($group);
    }
    
    // 指定したIDのキャッシュを削除する
    // 引数:キャッシュID
    public function cleanIDCache($id)
    {
        if (isset($this->_cacheDir)) {
            $this->_cleanIDCacheCore($this->_cacheDir, md5($id));
        }
    }
    private function _cleanIDCacheCore($dir, $id)
    {
        $files = glob($dir . 'cache_*');
        if (!empty($files)) {
            foreach($files as $f) {
                if (is_dir($f)) {
                    $this->_cleanIDCacheCore($f . '/', $id);
                }
                else if (substr($f, strrpos($f, '_') + 1) == $id) {
                    unlink($f);
                }
            }
        }
    }
    
    // クライアントサイドのキャッシュを設定するヘッダーを出力
    // 引数:キャッシュコントロールモード、有効時間
    public function setClientCache($mode, $lifetime = 86400)
    {
        if ($mode == self::CACHE_NO) {
            header( 'Cache-Control: no-store, no-cache, must-revalidate' );
            header( 'Cache-Control: post-check=0, pre-check=0', false );
            header( 'Pragma: no-cache' );
            header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
            header( 'Last-Modified: ' . gmdate('D, d M Y H:i:s T'));
        }
        else {
            header('Cache-Control: ' . $mode . ', max-age=' . $lifetime);
            header('Cache-Control: post-check=' . $lifetime . ', pre-check=' . $lifetime, false);
            if ($mode != self::CACHE_PRIVATE) {
                header('Expires: ' . gmdate('D, d M Y H:i:s T', time() + $lifetime));
            }
            header( 'Last-Modified: ' . gmdate('D, d M Y H:i:s T'));
        }
    }
    
    // 条件付きGETを実行
    // http://www.avoidnote.com/archives/2005/07/modified_since.php を参考
    // 引数:引数がなければ、スクリプトファイルの更新日時を、
    //    引数が数値の場合はその値を、
    //    引数がファイル名の場合はその更新日時をタイムスタンプに設定する
    //    引数を複数指定すると、そのうちの最新の日時を使用する
    public function conditionalGet()
    {
        if (func_num_args() == 0) {
            $timestamp = getlastmod();
        }
        else {
            $timestamp = 0;
            foreach(func_get_args() as $val) {
                if (is_numeric($val)) {
                    if ($val > $timestamp) {
                        $timestamp = $val;
                    }
                }
                else if (is_file($val)) {
                    $t = filemtime($val);
                    if ($t > $timestamp) {
                        $timestamp = $t;
                    }
                }
            }
        }
        $lastModified = gmdate('D, d M Y H:i:s T', $timestamp);
        $etag = '"' . md5($lastModified) . '"';
        header('Last-Modified: '.$lastModified);
        header('ETag: '.$etag);
        
        if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
            $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
            
            // 念のためセミコロン以降を削除してから処理
            $pos = strpos($ifModifiedSince, ';');
            if ($pos !== false) {
                $ifModifiedSince = substr($ifModifiedSince, 0, $pos);
            }
            if (strrpos($ifModifiedSince, 'GMT') === false) {
                $ifModifiedSince .= ' GMT';
            }
            $ifModifiedSince = strtotime( stripslashes($ifModifiedSince) );
        }
        else {
            $ifModifiedSince = false;
        }
        
        $ifNoneMatch = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
            stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : false;
    
        if (!$ifModifiedSince && !$ifNoneMatch) {
            return;
        }
        if ($ifNoneMatch && ($ifNoneMatch != $etag)) {
            return;
        }
        if ($ifModifiedSince && ($ifModifiedSince < $timestamp)) {
            return;
        }
        
        header('HTTP/1.1 304 Not Modified');
        exit;
    }
    
    // ファイル名のパスを安全に生成してを返す
    // 引数:起点となるディレクトリ、ファイル名
    // 戻り値:ディレクトリと結合させたファイル名
    public function filename($baseDir, $filepath)
    {
        // 念のため、NULLバイト文字を除去する
        $filepath = str_replace("\0", '', $filepath);
        
        // ファイル名に親ディレクトリが含まれている場合はエラー
        if (strpos($filepath, '../') !== false) {
            throw new Exception('filename has "../"');
        }
        else if (strpos($filepath, '/') === 0) {
            throw new Exception('filename begins from "/"');
        }
        
        // ディレクトリ文字列を補う
        if ($baseDir != '' and substr($baseDir, -1, 1) != '/') {
            $baseDir .= '/';
        }
        return $baseDir . $filepath;
    }
    
    // PHP.iniの設定をチェックする
    public static function checkPHPini()
    {
        echo '<html><body><table border="1"><tr><th>item</th><th>your</th><th>recommended</th></tr>';
        $phpini = array(
              'register_globals' => 'Off'
            , 'magic_quotes_gpc' => 'Off'
            , 'expose_php' => 'Off'
            , 'display_errors' => 'Off'
            , 'log_errors' => 'On'
            , 'enable_dl' => 'Off'
            , 'file_uploads' => 'Off'
            , 'allow_url_fopen' => 'Off'
            , 'allow_url_include' => 'Off'
            , 'session.use_cookies' => 'On'
            , 'session.use_only_cookies' => 'On'
            , 'session.auto_start' => 'Off'
            , 'session.use_trans_sid' => 'Off'
        );
        foreach($phpini as $key => $val) {
            $ini = ini_get($key) ? "On":"Off";
            echo '<tr align="center"><th>'.$key.'</th><td>'.$ini.'</td><td>';
            if ($ini != $val) {
                echo '<font color="red">'.$val.'</font>';
            }
            else {
                echo $val;
            }
            echo '</td></tr>';
        }
        $phpini = array(
              'error_reporting' => E_ALL|E_STRICT
            , 'session.hash_function' => '1'
        );
        foreach($phpini as $key => $val) {
            $ini = ini_get($key);
            echo '<tr align="center"><th>'.$key.'</th><td>'.$ini.'</td><td>';
            if ($ini != $val) {
                echo '<font color="red">'.$val.'</font>';
            }
            else {
                echo $val;
            }
            echo '</td></tr>';
        }
        $phpini = array(
              'default_chaset'
            , 'open_basedir'
            , 'disable_functions'
            , 'upload_max_filesize'
            , 'session.save_path'
            , 'session.cookie_lifetime'
            , 'session.cookie_path'
        );
        foreach($phpini as $key) {
            echo '<tr align="center"><th>'.$key.'</th><td colspan="2">'.ini_get($key).'</td></tr>';
        }
        echo '</table></body></html>';
        
        exit();
    }
}
?>