<?php
/**
* 定时器控制台
*
* php timer.php show 显示已开启的定时器,进程ID,及上次执行时间
* php timer.php start 开启定时器,判断是否已有开启，有则不再重复开启
* php timer.php restart 重启所有定时器
* php timer.php restart timer1 重启timer1的定时器
* php timer.php stop 关闭所有定时器
* php timer.php stop timer1 关闭timer1的定时器
* @author hzqghost <313143468@qq.com>
* @copyright 2020 yipart.com
* @version 1.0.0
*/
!defined('MVC_OS') && define('MVC_OS',strtolower(substr(PHP_OS,0,3)));
!defined('MVC_OS_IS_WIN') && define('MVC_OS_IS_WIN',MVC_OS=='win'); //是否是windows
$cmd = (isset($argv[1])) ? $argv[1] : '';
$timerName = (isset($argv[2])) ? $argv[2] : null;
$dir = str_replace('\\','/',__DIR__);
$commonCfg = require_once($dir.'/../config/common.php');
$cfg = require_once($dir.'/config.php');
$timerPhpFile = $commonCfg['mvcDir'] . '/timerProcess.php';
$timerPath = $dir;
$processPath = $timerPath . '/process';

if(!is_dir($processPath)) mkdir($processPath,0777);
function start(){
	global $timerPath, $endTimePath, $processPath, $timerPhpFile,$cfg,$dir;
	//检测
	foreach($cfg as $name=>$item){
		if(!is_numeric($item['interval'])){
			exit('ERROR: '.$name."'s interval is not numberic".PHP_EOL);
		}
		if(empty($item['site'])){
			exit('ERROR: '.$name."'s site is null".PHP_EOL);
		}
		if(empty($item['action'])){
			exit('ERROR: '.$name."'s action is null".PHP_EOL);
		}
	}
	foreach($cfg as $name=>$item){
		if($item['interval']*1<1){
			continue;
		}
		if(!empty($item['endTime'])){
			if(strtotime($item['endTime']) > time()){
				//如果配置中的结束时间大于现在的时间，则删除原来的超时标记
				@unlink($endTimeFile);
			}else{
				echo 'Warning: '.$name.' end time exceeded '. $item['endTime'] .PHP_EOL;
				continue;
			}
		}
		if($pid = getPid($name)){
			echo $name.' already exist. pid:'.$pid.PHP_EOL;
		}else{
			if(MVC_OS_IS_WIN){
				$cmd = 'start /B php '.$timerPhpFile . ' '. $name . ' '. $timerPath . ' &';
			}else{
				$cmd = 'php '.$timerPhpFile . ' '. $name. ' '. $timerPath .' > /dev/null &';
			}
			pclose(popen($cmd, 'r'));
			echo 'start '.$name.' success.'.PHP_EOL;
		}
	}
}
function show(){
	global $processPath;
	$files = scandir($processPath);
	$has = false;
	foreach($files as $file){
		if($file!='.' && $file!='..'){
			@list($name,$pid) = explode('.',$file);
			if($pid=='') continue;
			$filename = $processPath.'/'.$file;
			$mtime = filemtime($filename);
			echo $name.' pid:'.$pid.PHP_EOL;
			echo "\tlast time: ".date("Y-m-d H:i:s",$mtime).PHP_EOL;
			echo "\tfrom now: ". (time()-$mtime) . ' sec'.PHP_EOL;
			$has = true;
		}
	}
	if(!$has) echo "It's empty.".PHP_EOL;
}
function getPid($timerName){
	global $processPath;
	$files = scandir($processPath);
	foreach($files as $file){
		if($file!='.' && $file!='..'){
			@list($name,$pid) = explode('.',$file);
			if($pid=='') continue;
			if($name == $timerName) return $pid;
		}
	}
	return false;
}
function stop($timerName=''){
	global $processPath;
	$stopPid = null;
	if($timerName!=''){
		$stopPid = getPid($timerName);
		if($stopPid==false)
			exit('not found '.$timerName ." 's pid.".PHP_EOL);
		$stopFile = $processPath.'/stop_'.$timerName;
		file_put_contents($stopFile,'');
	}else{
		$stopFile = $processPath.'/stop';
		file_put_contents($stopFile,'');
	}
	echo 'ready';
	//等三秒钟，让各定时器进程去判断结束标识
	sleep(1); echo '.';
	sleep(1); echo '.';
	sleep(1); echo '.';
	$files = scandir($processPath);
	foreach($files as $file){
		if($file!='.' && $file!='..'){
			@list($name,$pid) = explode('.',$file);
			if($pid=='') continue;
			if($timerName==''){
				kill($name,$pid,$processPath.'/'.$file);
			}else{
				if($pid==$stopPid){
					kill($name,$pid,$processPath.'/'.$file);
				}
			}
		}
	}
	$files = scandir($processPath);
	foreach($files as $file){
		if(substr($file,0,4)=='lock' || substr($file,0,4)=='wait'){
			unlink($processPath.'/'.$file);
		}
	}
	//等三秒钟再删除结束标识，杀死进程偶尔有一秒左右的延时
	sleep(1); echo '.';
	sleep(1); echo '.';
	sleep(1); echo '.';
	echo PHP_EOL;
	unlink($stopFile);
	echo 'stop is success.'.PHP_EOL;
}
function kill($name,$pid,$processFile){
	global $processPath;
	$processActionDir =  $processPath.'/action';
	if(is_dir($processActionDir)){
		//杀死有锁标记的action任务进程
		$files = scandir($processActionDir);
		foreach($files as $file){
			if($file!='.' && $file!='..'){
				@list($outname,$pid) = explode('.',$file);
				if($pid=='') continue;
				if(MVC_OS_IS_WIN){
					pclose(popen(escapeshellcmd('taskkill /pid '.$pid.' -t -f'),'r'));
				}else{
					pclose(popen(escapeshellcmd("sudo kill ".$pid),'r')); //todo linux 下还没有测试
				}
				@unlink($processActionDir.'/'.$file);
			}
		}
	}
	if(MVC_OS_IS_WIN){
		pclose(popen(escapeshellcmd('taskkill /pid '.$pid.' -t -f'),'r'));
	}else{
		pclose(popen(escapeshellcmd("sudo kill ".$pid),'r')); //todo linux 下还没有测试
	}
	@unlink($processFile);
}
//开进程
if($cmd == 'start'){
	start();
}else if($cmd == 'show'){
	show();
}else if($cmd == 'stop'){
	stop($timerName);
}else if($cmd == 'restart'){
	if($timerName){
		stop($timerName);
	}else{
		stop();
	}
	start();
}else{
	echo 'Helper the command is:'.PHP_EOL."\tphp timer.php [show|start|stop|restart]".PHP_EOL;
}