<?php
/*
 * system/DataFileWriter.class.php
 * 
 * CopyRight(C) 2010 Shopformer Development Team. All Right Reserved
 * URL: http://sourceforge.jp/projects/shopformer/
 * 
 * 
 * Mail: m_nakashima@users.sourceforge.jp
 * Auther: Masanori Nakashima
 * Last Update: 2010-06-18
 */
require_once(dirname(__FILE__)
.DIRECTORY_SEPARATOR.'PackageConfig.class.php');
/**
 * システムで扱うデータファイル保存を行うためのファイル操作クラスです。
 * システム設定で分散サーバー構成で設置されている場合に、
 * 本クラスオブジェクトを利用してファイルシステムの操作をおこなうことで全ての分散サーバーに自動的に同じ情報を保存して
 * 読み込むことができます。
 * 
 * 操作をおこなうことができる対象のフォルダは下記の通りで、対処ルート指定時に下記の定数を利用します。
 * <ul>
 * <li>DIR_NAME_DATA<br />spider/dataフォルダ以下のファイルを操作する場合</li>
 * <li>DIR_NAME_PAGES<br />spider/pagesフォルダ以下のファイルを操作する場合</li>
 * <li>DIR_NAME_TEMPLATES<br />spider/templatesフォルダ以下のファイルを操作する場合</li>
 * <li>DIR_NAME_WIDGETS<br />spider/widgetフォルダ以下のファイルを操作する場合</li>
 * <li>'public'<br />公開フォルダ以下のファイルを操作する場合</li>
 * <li>'spider'<br />spiderフォルダ以下のファイルを操作する場合</li>
 * </ul>
 * 
 * @package system
 * @category Utility_Object
 * @version 1.2.00
 * @copyright Copyright(c)2011, Shopformer Development Team. <shopformer-dev@lists.sourceforge.jp>
 * @author Masanori Nakashima <m_nakashima@users.sourceforge.jp>
 * @access public
 */
class system_DataFileWriter {
	/**
	 * ターゲットルート
	 * @access private
	 */
	var $targetRoot			= null;
	/**
	 * 保存ファイルのURI
	 * @access private
	 */
	var $fileUri			= null;
	/**
	 * オープンモード
	 * @access private
	 */
	var $mode				= null;
	/**
	 * 現在のサーバーのファイルハンドル
	 * @access private
	 */
	var $fileHandle			= null;
	/**
	 * コンストラクタ
	 * @access public
	 */
	function system_DataFileWriter() {
	}
	/**
	 * システム基本設定が保存済みか確認します。
	 * @return boolean 設定済みならtrue,それ以外ならfalse
	 * @access public
	 */
	function isSystemSetup() {
		if( file_exists(DIR_PATH_DATA.DIRECTORY_SEPARATOR.'system.define.inc.php') ) {
			require_once(DIR_PATH_DATA.DIRECTORY_SEPARATOR.'system.define.inc.php');
			return true;
		} else {
			return false;
		}
	}
	/**
	 * 指定されたルート種別の設置サーバー上のルートパスを取得します
	 * @param string $targetRoot 読み込むルート種別を指定します。
	 * @return string 設置サーバー上のフォルダのルートパス
	 * @access private
	 */
	function getTargetRootPath( $targetRoot ) {
		if( DIR_NAME_DATA == $targetRoot ) {
			return DIR_PATH_DATA;
		} else if( 'public' == $targetRoot ) {
			return SPIDER_BASE_PATH;
		} else if( 'spider' == $targetRoot ) {
			return DIR_PATH_SPIDER;
		} else if( DIR_NAME_PAGES == $targetRoot ) {
			return DIR_PATH_PAGES;
		} else if( DIR_NAME_LIB == $targetRoot ) {
			return DIR_PATH_LIB;
		} else if( DIR_NAME_WIDGETS == $targetRoot ) {
			return DIR_PATH_WIDGETS;
		} else if( DIR_NAME_TEMPLATES == $targetRoot ) {
			return DIR_PATH_TEMPLATES;
		} else if( DIR_NAME_TMP == $targetRoot ) {
			return DIR_PATH_TMP;
		} else if( DIR_NAME_LOCK == $targetRoot ) {
			return DIR_PATH_LOCK;
		} else if( DIR_NAME_CACHE == $targetRoot ) {
			return DIR_PATH_CACHE;
		} else {
			return DIR_PATH_DATA;
		}
	}
	/**
	 * 指定されたルート種別の種類に応じて各サーバーのフォルダパスを取得します
	 * @param string $targetRoot 読み込むルート種別を指定します。
	 * @return array 分散サーバー上のフォルダのルートパスの配列
	 * @access private
	 */
	function getTargetMountPathArray( $targetRoot ) {
		$returnArray	= array();
		if( 'public' == $targetRoot ) {
			if( defined('SYSTEM_MOUNT_ROOT_PUBLIC')
				&& strlen(SYSTEM_MOUNT_ROOT_PUBLIC) > 3
				&& is_dir(SYSTEM_MOUNT_ROOT_PUBLIC) ) {
				if ($dh = @opendir(SYSTEM_MOUNT_ROOT_PUBLIC)) {
					while (($fileName = readdir($dh)) !== false) {
						if( preg_match('/^\\.$/',$fileName) == 0
							&& preg_match('/^\\.\\.$/',$fileName) == 0 ) {
							$absolutePath		= SYSTEM_MOUNT_ROOT_PUBLIC.DIRECTORY_SEPARATOR.$fileName;
							if( preg_match('/[dD][iI][rR]/',filetype($absolutePath)) ) {
								array_push( $returnArray, $absolutePath );
							}
						}
					}
				}
			}
		} else if( defined('SYSTEM_MOUNT_ROOT_SPIDER')
			&& strlen(SYSTEM_MOUNT_ROOT_SPIDER) > 3
			&& is_dir(SYSTEM_MOUNT_ROOT_SPIDER) ) {
			if ($dh = @opendir(SYSTEM_MOUNT_ROOT_SPIDER)) {
				while (($fileName = readdir($dh)) !== false) {
					if( preg_match('/^\\.$/',$fileName) == 0
						&& preg_match('/^\\.\\.$/',$fileName) == 0 ) {
						$absolutePath		= SYSTEM_MOUNT_ROOT_SPIDER.DIRECTORY_SEPARATOR.$fileName;
						if( preg_match('/[dD][iI][rR]/',filetype($absolutePath)) ) {
							if( DIR_NAME_PAGES == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_PAGES );
							} else if( 'spider' == $targetRoot ) {
								array_push( $returnArray, $absolutePath );
							} else if( DIR_NAME_LIB == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_LIB );
							} else if( DIR_NAME_WIDGETS == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_WIDGETS );
							} else if( DIR_NAME_TEMPLATES == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_TEMPLATES );
							} else if( DIR_NAME_TMP == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_WORK.DIRECTORY_SEPARATOR.DIR_NAME_TMP );
							} else if( DIR_NAME_CACHE == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_WORK.DIRECTORY_SEPARATOR.DIR_NAME_CACHE );
							} else if( DIR_NAME_LOCK == $targetRoot ) {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_WORK.DIRECTORY_SEPARATOR.DIR_NAME_LOCK );
							} else {
								array_push( $returnArray, $absolutePath.DIRECTORY_SEPARATOR.DIR_NAME_WORK.DIRECTORY_SEPARATOR.DIR_NAME_DATA );
							}
						}
					}
				}
				@closedir($dh);
			} else {
				return array();
			}
		}
		return $returnArray;
	}
	/**
	 * 指定URIのファイルをオープンします。第４引数に渡す値は本クラスの概要をご覧ください。
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri 操作対象のファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $mode ファイルオープンのモードをfopen関数と同様に指定してください。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean ファイルオープンに成功したらtrue、失敗したらfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function open( & $request, $fileUri, $mode='w', $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$this->targetRoot	= DIR_NAME_DATA;
		} else {
			$this->targetRoot	= $targetRoot;
		}
		$this->fileUri	= $fileUri;
		$this->mode		= $mode;
		// ファイルURIから稼働中サーバーのフォルダ内のファイルパスを生成する
		$targetPath		= system_DataFileWriter::getTargetRootPath( $this->targetRoot ).$fileUri;
		// 階層のフォルダがないなら生成する
		if( !file_exists( dirname($targetPath) ) ) {
			$dirNameArray	= explode('/',$fileUri);
			// 一番最後の要素はファイル名として除去する
			array_pop($dirNameArray);
			// フォルダ階層がなければ作成する
			$dirPath		= system_DataFileWriter::getTargetRootPath( $this->targetRoot );
			foreach( $dirNameArray as $dirName ) {
				if( strlen($dirName) > 0 ) {
					$dirPath	.= DIRECTORY_SEPARATOR.$dirName;
					if( !file_exists( $dirPath ) ) {
						$permittion	= SPIDER_PERMITTION_DATA_FOLDER;
						if( 'public' == $this->targetRoot ) {
							$permittion	= SPIDER_PERMITTION_PUBLIC_FOLDER;
						}
						if( @mkdir( $dirPath, $permittion) ) {
							@chmod( $dirPath, $permittion );
						} else {
							$request->addLocaledError('system.error.writer.failtomkdir',SPIDER_LOG_LEVEL_ERROR,array($dirPath));
							return false;
						}
					}
				}
			}
		}
		if( $this->fileHandle = @fopen($targetPath,$mode) ) {
			$lockType	= LOCK_SH;
			if( preg_match('/([aA]|[wW])/',$mode) > 0 ) {
				$lockType	= LOCK_EX;
			}
			if ( @flock($this->fileHandle, $lockType)) {
				return true;
			} else {
				// 書き込みロック取得に失敗したらファイルをクローズして失敗
				$request->addLocaledError('system.error.writer.lockw',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
				@fclose($this->fileHandle);
				$this->fileHandle	= null;
				return false;
			}
		} else {
			$request->addLocaledError('system.error.writer.open',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
			return false;
		}
	}
	/**
	 * 現在オープンしているファイルに書き込みます
	 * @param string $string 書き込む文字列
	 * @return boolean 書き込みに失敗したらfalse
	 * @access public
	 */
	function write( $strings ) {
		// 開いているリソースがあるなら処理
		if( $this->fileHandle ) {
			return fwrite($this->fileHandle,$strings);
		}
		return false;
	}
	/**
	 * オープンしているファイルを現在位置から指定バイト数バイナリセーフで読み込みます。fread関数と同様の位置づけです。
	 * @param $length 現在位置らから読み込むバイト数。
	 * @return mixed 読み込んだ文字バイト。失敗したらfalse。
	 * @access public
	 */
	function read( $length=null ) {
		if( $this->fileHandle ) {
			if( is_null($length) ) {
				return fread($this->fileHandle);
			} else {
				return fread($this->fileHandle, $length);
			}
		}
		return false;
	}
	/**
	 * オープンしているファイルから次の 1 行を取得します。fgetsと同じ位置づけです。
	 * @param $length 現在位置らから読み込むバイト数。
	 * @return mixed 読み込んだ文字バイト。失敗したらfalse。
	 * @access public
	 */
	function gets( $length=null ) {
		if( $this->fileHandle ) {
			if( is_null($length) ) {
				return fgets($this->fileHandle);
			} else {
				return fgets($this->fileHandle, $length);
			}
		}
		return false;
	}
	/**
	 * 現在オープンしているファイルポインタがファイルの週単に達しているか確認します。
	 * @return boolean ファイル終端ならtrueを戻します
	 * @access public
	 */
	function eof() {
		if( $this->fileHandle ) {
			return feof($this->fileHandle);
		}
		return false;
	}
	/**
	 * オープンしているファイルを保存して終了します。
	 * 書き込みモードでオープンされている場合、同時にすべての分散サーバーに同一内容のファイル書き込み処理をおこないます。
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @return boolean 書き込みに成功したらtrue、失敗したらfalse
	 * @access public
	 */
	function close(& $request) {
		// 開いているリソースがあるなら処理
		if( $this->fileHandle ) {
			$srcPath		= system_DataFileWriter::getTargetRootPath( $this->targetRoot ).$this->fileUri;
			// ロックを解除してファイルをクローズ
			@flock( $this->fileHandle, LOCK_UN );
			@fclose( $this->fileHandle );
			if( 'public' == $this->targetRoot && preg_match('/\\.php$/',$this->fileUri) > 0 ) {
				@chmod($srcPath,SPIDER_PERMITTION_PUBLIC_FILE);
			} else {
				@chmod($srcPath,SPIDER_PERMITTION_DATA_FILE);
			}
			if( preg_match('/^r/',$this->mode) > 0 ) {
				// 読み込みモードならコピーしないで終了
				return true;
			}
			// マウントポイントの確認
			$mountPointArray		= array();
			if( system_DataFileWriter::isSystemSetup() ) {
				$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $this->targetRoot );
			}
			if( is_array($mountPointArray) ) {
				// マルチサーバーのマウントポイントに対して強制コピー
				foreach( $mountPointArray as $mountPoint ) {
					if( strlen(trim($mountPoint)) > 0 ) {
						// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
						$targetPath		= $mountPoint.$this->fileUri;
						// 階層のフォルダがないなら生成する
						if( !file_exists( dirname($targetPath) ) ) {
							$dirNameArray	= explode('/',$this->fileUri);
							// 一番最後の要素はファイル名として除去する
							array_pop($dirNameArray);
							// フォルダ階層がなければ作成する
							$dirPath		= $mountPoint;
							foreach( $dirNameArray as $dirName ) {
								if( strlen($dirName) > 0 ) {
									$dirPath	.= DIRECTORY_SEPARATOR.$dirName;
									if( !file_exists( $dirPath ) ) {
										$permittion	= SPIDER_PERMITTION_DATA_FOLDER;
										if( 'public' == $this->targetRoot ) {
											$permittion	= SPIDER_PERMITTION_PUBLIC_FOLDER;
										}
										if( @mkdir( $dirPath, $permittion) ) {
											@chmod( $dirPath, $permittion );
										} else {
											$request->addLocaledError('system.error.writer.failtomkdir',SPIDER_LOG_LEVEL_ERROR,array($dirPath));
											return false;
										}
									}
								}
							}
						}
						// ファイルをコピー
						if( $srcPath != $targetPath ) {
							if( !copy($srcPath,$targetPath) ) {
								$request->addLocaledError('system.error.writer.copy',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
								return false;
							} else {
								if( 'public' == $this->targetRoot && preg_match('/\\.php$/',$targetPath) > 0 ) {
									@chmod($targetPath,SPIDER_PERMITTION_PUBLIC_FILE);
								} else {
									@chmod($targetPath,SPIDER_PERMITTION_DATA_FILE);
								}
							}
						}
					}
				}
			}
			return true;
		} else {
			return false;
		}
	}
	/**
	 * 動作中サーバーのファイルの絶対パスを指定してマルチサーバー管理外のファイルを指定URIに取り込みます
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $srcPath インポート元のファイル絶対パス
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 成功したらtrue、失敗したらfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function import( & $request, $fileUri, $srcPath, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		// ファイルURIから稼働中サーバーのフォルダ内のファイルパスを生成する
		$targetPath		= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		// 階層のフォルダがないなら生成する
		if( !file_exists( dirname($targetPath) ) ) {
			$dirNameArray	= explode('/',$fileUri);
			// 一番最後の要素はファイル名として除去する
			array_pop($dirNameArray);
			// フォルダ階層がなければ作成する
			$dirPath		= system_DataFileWriter::getTargetRootPath( $targetRoot );
			foreach( $dirNameArray as $dirName ) {
				if( strlen($dirName) > 0 ) {
					$dirPath	.= DIRECTORY_SEPARATOR.$dirName;
					if( !file_exists( $dirPath ) ) {
						$permittion	= SPIDER_PERMITTION_DATA_FOLDER;
						if( 'public' == $targetRoot ) {
							$permittion	= SPIDER_PERMITTION_PUBLIC_FOLDER;
						}
						if( @mkdir( $dirPath, $permittion) ) {
							@chmod( $dirPath, $permittion );
						} else {
							$request->addLocaledError('system.error.writer.failtomkdir',SPIDER_LOG_LEVEL_ERROR,array($dirPath));
							return false;
						}
					}
				}
			}
		}
		// コピーする
		if( !@copy($srcPath,$targetPath) ) {
			$request->addLocaledError('system.error.writer.copy',SPIDER_LOG_LEVEL_ERROR,array($srcPath.':'.$targetPath));
			return false;
		}
		// マウントポイントの確認
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		if( is_array($mountPointArray) ) {
			// マルチサーバーのマウントポイントに対して強制コピー
			foreach( $mountPointArray as $mountPoint ) {
				if( strlen(trim($mountPoint)) > 0 ) {
					// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
					$targetPath		= $mountPoint.$fileUri;
					// 階層のフォルダがないなら生成する
					if( !file_exists( dirname($targetPath) ) ) {
						$dirNameArray	= explode('/',$fileUri);
						// 一番最後の要素はファイル名として除去する
						array_pop($dirNameArray);
						// フォルダ階層がなければ作成する
						$dirPath		= $mountPoint;
						foreach( $dirNameArray as $dirName ) {
							if( strlen($dirName) > 0 ) {
								$dirPath	.= DIRECTORY_SEPARATOR.$dirName;
								if( !file_exists( $dirPath ) ) {
									$permittion	= SPIDER_PERMITTION_DATA_FOLDER;
									if( 'public' == $targetRoot ) {
										$permittion	= SPIDER_PERMITTION_PUBLIC_FOLDER;
									}
									if( @mkdir( $dirPath, $permittion) ) {
										@chmod( $dirPath, $permittion );
									} else {
										$request->addLocaledError('system.error.writer.failtomkdir',SPIDER_LOG_LEVEL_ERROR,array($dirPath));
										return false;
									}
								}
							}
						}
					}
					// ファイルをコピー
					if( $srcPath != $targetPath ) {
						if( !copy($srcPath,$targetPath) ) {
							$request->addLocaledError('system.error.writer.copy',SPIDER_LOG_LEVEL_ERROR,array($srcPath.':'.$targetPath));
							return false;
						} else {
							if( 'public' == $targetRoot && preg_match('/\\.php$/',$targetPath) > 0 ) {
								@chmod($targetPath,SPIDER_PERMITTION_PUBLIC_FILE);
							} else {
								@chmod($targetPath,SPIDER_PERMITTION_DATA_FILE);
							}
						}
					}
				}
			}
		}
		return true;
	}
	/**
	 * ファイルを全てのサーバーから削除します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 成功したらtrue、失敗したらfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function delete( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		// マウントポイントの確認
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		// ファイルを削除
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			if( is_dir($targetPath) ) {
				if( @rmdir( $targetPath ) ) {
				} else {
					$request->addLocaledError('system.error.writer.delete',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
				}
			} else {
				if( @unlink( $targetPath ) ) {
				} else {
					$request->addLocaledError('system.error.writer.delete',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
				}
			}
		}
		if( is_array($mountPointArray) ) {
			foreach( $mountPointArray as $mountPoint ) {
				if( strlen(trim($mountPoint)) > 0 ) {
					// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
					$targetPath		= $mountPoint.$fileUri;
					if( file_exists($targetPath) ) {
						if( is_dir($targetPath) ) {
							if( @rmdir( $targetPath ) ) {
							} else {
								$request->addLocaledError('system.error.writer.rmdir',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
							}
						} else {
							if( @unlink( $targetPath ) ) {
							} else {
								$request->addLocaledError('system.error.writer.delete',SPIDER_LOG_LEVEL_ERROR,array($targetPath));
							}
						}
					}
				}
			}
		}
		if( $request->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * ファイルを全てのサーバーでタッチします
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 成功したらtrue、失敗したらfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function touchFile( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		// マウントポイントの確認
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		// ファイルにタッチ
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		@touch($targetPath);
		if( is_array($mountPointArray) ) {
			foreach( $mountPointArray as $mountPoint ) {
				if( strlen(trim($mountPoint)) > 0 ) {
					// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
					$targetPath		= $mountPoint.$fileUri;
					@touch($targetPath);
				}
			}
		}
		if( $request->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * 指定URIのデータファイルが存在するか確認します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 存在したらtrue、それ以外はfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function isExists( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).str_replace('/',DIRECTORY_SEPARATOR,$fileUri);
		if( file_exists($targetPath) ) {
			return true;
		} else {
			return false;
		}
	}
	/**
	 * 指定URIのファイルを分散サーバのいずれかが保持していればフルパスを返します。
	 * 保持していなかった場合はnullを返します。
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return mixed ファイルフルパス文字列。存在しないならnull
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getFilePathOnAnyHost( & $request, $fileUri, $targetRoot=null ) {
		if( system_DataFileWriter::isExists($request, $fileUri, $targetRoot) ) {
			return system_DataFileWriter::getTargetRootPath( $targetRoot ).str_replace('/',DIRECTORY_SEPARATOR,$fileUri);
		}
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		if( is_array($mountPointArray) ) {
			foreach( $mountPointArray as $mountPoint ) {
				if( strlen(trim($mountPoint)) > 0 ) {
					// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
					$targetPath		= $mountPoint.$fileUri;
					if( file_exists($targetPath) ) {
						return $targetPath;
					}
				}
			}
		}
		return null;
	}
	/**
	 * ファイル最終更新タイムスタンプを取得
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return int timestamp
	 * @access public
	 * @see system_DataFileWriter
	 */
	function timeModifiered( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			return filemtime($targetPath);
		} else {
			return false;
		}
	}
	/**
	 * ファイル作成タイムスタンプを取得
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return int timestamp
	 * @access public
	 * @see system_DataFileWriter
	 */
	function timeCreated( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			return filectime($targetPath);
		} else {
			return false;
		}
	}
	/**
	 * 指定URIのデータファイルが読み込み可能か確認します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 可能ならtrue
	 * @access public
	 * @see system_DataFileWriter
	 */
	function isReadable( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath		= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			if( is_readable($targetPath) ) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
	/**
	 * 指定URIのデータファイルが書き込み可能か確認します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 可能ならtrue
	 * @access public
	 * @see system_DataFileWriter
	 */
	function isWritable( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		// マウントポイントの確認
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		$targetPath		= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			if( !is_writable($targetPath) ) {
				return false;
			}
		} else {
			$parentPath	= dirname($targetPath);
			$isWritable	= false;
			while( strlen($parentPath) > strlen($targetRoot)+1 ) {
				if( file_exists($parentPath) ) {
					if( is_writable($parentPath) && is_readable($parentPath) && is_writable($parentPath) ) {
						$isWritable	= true;
						break;
					}
				}
				$parentPath	= dirname($parentPath);
			}
			if( !$isWritable ) {
				return false;
			}
		}
		if( is_array($mountPointArray) ) {
			// マルチサーバーのマウントポイント全てのファイルの書き込み権限を確認
			foreach( $mountPointArray as $mountPoint ) {
				if( strlen(trim($mountPoint)) > 0 ) {
					// ファイルURIからマウントポイントのフォルダ内のファイルパスを生成する
					$targetPath		= $mountPoint.$fileUri;
					if( file_exists($targetPath) ) {
						if( !is_writable($targetPath) ) {
							return false;
						}
					} else {
						$parentPath	= dirname($targetPath);
						if( !is_writable($parentPath) || !is_readable($parentPath) || !is_writable($parentPath) ) {
							return false;
						}
					}
				}
			}
		}
		return true;
	}
	/**
	 * 指定URIのデータファイルの内容を全て返します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return string ファイル内容
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getContents( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).str_replace('/',DIRECTORY_SEPARATOR,$fileUri);
		if( file_exists($targetPath) ) {
			return file_get_contents( $targetPath );
		} else {
			return false;
		}
	}
	/**
	 * 指定URIのデータファイルの内容を改行で区切った配列で全て取得します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return array ファイルの行配列
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getArray( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).$fileUri;
		if( file_exists($targetPath) ) {
			return file( $targetPath );
		} else {
			return false;
		}
	}
	/**
	 * 指定URIのデータファイルの内容をCSVに見立てて指定行の情報を２次元配列で取得します。
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @param int $offset 読み込み行オフセット
	 * @param int $rows 読み込む行数
	 * @param boolean $ignoreFirstLine 一行目をラベル行として無視する場合はtrue。デフォルトはtrue。
	 * @return array CSVデータの二次元配列
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getCsvRecordLineArray( & $request, $fileUri, $targetName, $offset=0, $rows=1, $ignoreFirstLine=true ) {
		$absolutePath	= system_DataFileWriter::getRealAbsolutePath( $request, $fileUri, $targetName );
		$recordArray	= array();
		$fp				= @fopen($absolutePath,'r');
		if( $fp ) {
			if ( @flock($fp, LOCK_SH)) {
				$dataRowNumber	= 0;
				$dataLine		= '';
				for ( $lineNumber = 1; !feof( $fp ); $lineNumber++ ) {
					$line			= fgets( $fp );
					// 一行目を無視する場合は行を取得してポインタを進めてから評価
					if( $lineNumber == 1 && $ignoreFirstLine ) {
						continue;
					}
					mb_internal_encoding('UTF-8');
					$line		= mb_convert_encoding( $line, 'UTF-8', 'SJIS-win' );
					$dataLine		.= $line;
					$quoteCount	= substr_count( $dataLine, '"' );
					if( $quoteCount % 2 != 0 ) {
						// クォータ数が偶数でない場合は行でデータが完結していないので次の行へ
						continue;
					} else if( strlen(trim($dataLine)) > 0 ) {
						$dataRowNumber++;
						// 行データが完結してオフセット処理範囲内なら処理
						if( $dataRowNumber > $offset
							&& $dataRowNumber < $offset + $rows ) {
							array_push( $recordArray, trim($dataLine) );
							$dataLine	= '';
						} else if ( $dataRowNumber >= $offset + $rows ) {
							break;
						} else {
							$dataLine	= '';
						}
					}
				}
				// 最後の行があるなら処理する
				if( strlen(trim($dataLine)) > 0 ) {
					array_push( $recordArray, trim($dataLine) );
					$dataLine	= '';
				}
				@flock( $fp, LOCK_UN );
			}
			@fclose( $fp );
		}
		return $recordArray;
	}
	/**
	 * 指定URIのデータファイルの内容をCSVで区切ってハッシュ配列で取得します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param array $columnSelectedArray csvカラムの順序どおりにキーを並べた配列
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @param int $offset 読み込み行オフセット
	 * @param int $rows 読み込む行数
	 * @param boolean $ignoreFirstLine 一行目をラベル行として無視する場合はtrue。デフォルトはtrue。
	 * @return array CSVデータの二次元配列
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getCsvRecordHashArray( & $request, $columnSelectedArray, $fileUri, $targetName, $offset=0, $rows=1, $ignoreFirstLine=true ) {
		$recordLineArray	= system_DataFileWriter::getCsvRecordLineArray( $request, $fileUri, DIR_NAME_TMP,
			$offset, $rows, $ignoreFirstLine );
		$returnHashArray	= array();
		foreach( $recordLineArray as $recordLine ) {
			if( strlen(trim($recordLine)) > 0 ) {
				// カラム名=>値のハッシュに整理
				$columnValues		= util_CharUtility::csv2Array( $recordLine );
				$columnValueHash	= array();
				foreach( $columnSelectedArray as $number => $varName ) {
					$columnValueHash[$varName]	= $columnValues[$number];
				}
				array_push( $returnHashArray, $columnValueHash );
			}
		}
		return $returnHashArray;
	}
	/**
	 * 指定コンテンツファイルの稼働サーバー上のリアルパスを取得します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $fileUri インポート先ファイルURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return string 実ファイルパス
	 * @access public
	 * @see system_DataFileWriter
	 */
	function getRealAbsolutePath( & $request, $fileUri, $targetRoot=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		$targetPath	= system_DataFileWriter::getTargetRootPath( $targetRoot ).str_replace('/',DIRECTORY_SEPARATOR,$fileUri);
		return $targetPath;
	}
	/**
	 * ディレクトリを再帰的に全てのサーバーから削除します
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $dirUri 削除対象ディレクトリURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @param int $pasttime 指定時間以上経過したファイルのみ削除する場合は秒数で指定。nullの場合はすべて削除。
	 * @return boolean 成功したらtrue。
	 * @access public
	 * @see system_DataFileWriter
	 */
	function deleteDir( & $request, $dirUri, $targetRoot=null, $pasttime=null ) {
		if( is_null($targetRoot) || strlen(trim($targetRoot)) == 0 ) {
			$targetRoot	= DIR_NAME_DATA;
		}
		// マウントポイントの確認
		$mountPointArray		= array();
		if( system_DataFileWriter::isSystemSetup() ) {
			$mountPointArray	= system_DataFileWriter::getTargetMountPathArray( $targetRoot );
		}
		// 動作中サーバーのディレクトリ
		$targetRootPath	= system_DataFileWriter::getTargetRootPath( $targetRoot );
		$targetPath		= $targetRootPath.$dirUri;
		$targetPath		= str_replace('/',DIRECTORY_SEPARATOR,$targetPath);
		if( is_dir($targetPath) ) {
			$remainFiles	= false;
			if ($dh = opendir($targetPath)) {
				while (($fileName = readdir($dh)) !== false) {
					if( $fileName != '.' && $fileName != '..' ) {
						$absolutePath	= $targetPath.DIRECTORY_SEPARATOR.$fileName;
						$absolutePath	= str_replace('/',DIRECTORY_SEPARATOR,$absolutePath);
						$tUri			= $dirUri.'/'.$fileName;
						if( is_dir($absolutePath) ) {
							system_DataFileWriter::deleteDir( $request, $tUri, $targetRoot,$pasttime );
							if( system_DataFileWriter::isExists( $request, $tUri, $targetRoot ) ) {
								$remainFiles	= true;
							}
						} else {
							if( !is_null($pasttime) && $pasttime > 0 ) {
								if( time() - system_DataFileWriter::timeModifiered( $request, $tUri, $targetRoot ) > $pasttime ) {
									system_DataFileWriter::delete( $request, $tUri, $targetRoot );
								} else {
									$remainFiles	= true;
								}
							} else {
								system_DataFileWriter::delete( $request, $tUri, $targetRoot );
							}
						}
					}
				}
				closedir($dh);
			}
			if( !$remainFiles ) {
				system_DataFileWriter::delete( $request, $dirUri, $targetRoot );
			}
		}
		
		if( $request->isError() ) {
			return false;
		} else {
			return true;
		}
	}
	/**
	 * 対象ディレクトリ内を再帰的にシステムデータにインポートします
	 * @param spider_HttpRequest &$request spider_HttpRequestインスタンス参照
	 * @param string $dirUri インポート先ディレクトリURI。ディレクトリ区切り文字は/で固定とし、/から開始するものとします。
	 * @param string $srcPath インポート元のディレクトリ絶対パス
	 * @param string $targetRoot 対象ルート。クラスドキュメント参照。デフォルトはDIR_NAME_DATAです。
	 * @return boolean 成功したらtrue、失敗したらfalseを戻します
	 * @access public
	 * @see system_DataFileWriter
	 */
	function importDir( & $request, $dirUri, $srcPath, $targetRoot=null ) {
		$srcPath	= str_replace('/',DIRECTORY_SEPARATOR,$srcPath);
		$dirUri		= str_replace(DIRECTORY_SEPARATOR,'/',$dirUri);
		if( preg_match('/\\/$/',$dirUri) == 0 ) {
			$dirUri	.= '/';
		}
		if( is_dir($srcPath) ) {
			if ($dh = opendir($srcPath)) {
				while (($fileName = readdir($dh)) !== false) {
					if( $fileName != '.' && $fileName != '..' && preg_match('/\\.svn/',$fileName) == 0 ) {
						$absolutePath	= $srcPath.DIRECTORY_SEPARATOR.$fileName;
						$fileUri		= $dirUri.$fileName;
						if( is_dir($absolutePath) ) {
							system_DataFileWriter::importDir( $request, $fileUri, $absolutePath, $targetRoot );
						} else {
							system_DataFileWriter::import( $request, $fileUri, $absolutePath, $targetRoot );
						}
					}
				}
			}
		} else {
			return false;
		}
	}
}
?>