<?php
/**
 *
 * ファイルシステムによってロックするクラス
 *
 */
// ファイルロック保持時間：15分
define( "UTIL_LOCK_FILE_MAX_TIME",	60*15 );
class util_LockData {
	
	var $file_name;
	var $operator_id;
	var $owner_id;
	var $lock_file_name;
	var $lock_limit_length	= UTIL_LOCK_FILE_MAX_TIME;
	var $lock_time;
	
	var $message;
	
	/**
	 * コンストラクタ
	 */	
	function util_LockData($file_name,$operator_id){
		$this->file_name		= $file_name;
		$this->operator_id		= $operator_id;
		$this->lock_file_name	= DIR_PATH_LOCK.DIRECTORY_SEPARATOR.'util';
		if(!file_exists($this->lock_file_name)) {
			if( @mkdir($this->lock_file_name,SPIDER_PERMITTION_DATA_FOLDER) ) {
				@chmod($this->lock_file_name,SPIDER_PERMITTION_DATA_FOLDER);
			}
		}
		$this->lock_file_name	.= DIRECTORY_SEPARATOR.'filelock';
		if(!file_exists($this->lock_file_name)) {
			if( @mkdir($this->lock_file_name,SPIDER_PERMITTION_DATA_FOLDER) ) {
				@chmod($this->lock_file_name,SPIDER_PERMITTION_DATA_FOLDER);
			}
		}
		$this->lock_file_name	.= DIRECTORY_SEPARATOR.$this->file_name;
		if( file_exists($this->lock_file_name) ) {
			$this->lock_time	= filemtime($this->lock_file_name);
		}
	}
	/**
	 * ロックを確認、開始します。
	 * @return 成功したらtrue / ロックに失敗したらfalse
	 */
	function lock(){
		if( !$this->is_avairable() ) {
			return false;
		}
		if( $this->is_lock() ) {
			// ロックされている場合はロックオーナーを確認
			if( $this->is_editable() ) {
				// オーナーが自身ならファイルを更新してロック取得OK
				@chmod( $this->lock_file_name, SPIDER_PERMITTION_DATA_FILE );
				@touch( $this->lock_file_name );
				return true;
			} else {
				// 自分がオーナーでないならエラー
				$this->message	= 'This Lock is not editable. owner='.$this->owner_id
					.': operator='.$this->operator_id;
				return false;
			}
		} else {
			// ロックされていない場合は新規にロックファイルを作成
			$fp	= @fopen( $this->lock_file_name, "w" );
			if( $fp ) {
				if( @flock( $fp, LOCK_EX ) ) {
					@fwrite( $fp, $this->operator_id );
					@flock( $fp, LOCK_UN );
					@fclose( $fp );
					@chmod( $this->lock_file_name, SPIDER_PERMITTION_DATA_FILE );
					$this->owner_id	= $this->operator_id;
					return true;
				} else {
					@fclose( $fp );
					@chmod( $this->lock_file_name, SPIDER_PERMITTION_DATA_FILE );
					$this->message	= 'IO ERROR: can\'t lock file!';
					return false;
				} 
			} else {
				$this->message	= 'IO ERROR: can\'t open file!';
				return false;
			}
		}
	}
	/**
	 * ロックされているか確認します
	 * @return ロックされているならtrue / ロックされていないならfalse
	 */
	function is_lock() {
		if( !$this->is_avairable() ) {
			return false;
		}
		if( file_exists( $this->lock_file_name ) ) {
			// ファイルの更新日時を確認し、操作可能か確認する
			$lock_file_limit_date = filemtime( $this->lock_file_name )
				+ $this->lock_limit_length;
			if ( $lock_file_limit_date < time() ) {
				// ロックファイルの期限日時を越えているためロック解除
				unlink( $this->lock_file_name );
				return false;
			} else {
				// ロック中
				return true;
			}
		} else {
			return false;
		}
	}
	/**
	 * ロックされている場合にロックオーナーの情報を返します
	 * @return string ロックオーナー情報文字列 / 取得に失敗したらfalse
	 */
	function get_owner() {
		if( !$this->is_avairable() ) {
			return false;
		}
		if( $this->is_lock() ) {
			$strings		= file_get_contents($this->lock_file_name);
			$this->owner_id	= trim($strings);
			return $this->owner_id;
		} else {
			return '';
		}
	}
	/**
	 * ロックオブジェクトが設定された作業者情報の情報と同じであり編集可能かを確認します。
	 * @return boolean 編集可能ならtrue
	 */
	function is_editable() {
		if( !$this->is_avairable() ) {
			return false;
		}
		if( $this->is_lock() ) {
			if( $this->get_owner() == $this->operator_id ) {
				return true;
			} else {
				return false;
			}
		} else {
			return true;
		}
	}
	/**
	 * ファイルのロックを解除します。
	 */
	function release(){
		if( !$this->is_avairable() ) {
			return false;
		}
		if( $this->lock() ) {
			if( file_exists($this->lock_file_name) ) {
				if( unlink($this->lock_file_name) ) {
					return true;
				} else {
					return false;
				}
			} else {
				return true;
			}
		} else {
			return false;
		}
	}
	/**
	 * 有効なロック名か確認します
	 */
	function is_avairable() {
		if( strlen($this->operator_id) == 0 ) {
			$this->message	= 'Fatal: set operator_id!';
			return false;
		} else if( strlen($this->file_name) == 0 ) {
			$this->message	= 'Fatal: set file_name!';
			return false;
		}
		return true;
	}
}

?>