You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
simple-yewu/console/helpers/CronParser.php

234 lines
6.9 KiB

5 years ago
<?php
/**
* Created by PhpStorm.
* User: xzz
* Date: 2018/4/11
* Time: 9:38
*/
namespace console\helpers;
/**
* crontab格式解析工具类
* @author jlb <497012571@qq.com>
*/
/**
* crontab格式解析工具类
* @author jlb <497012571@qq.com>
*/
class CronParser
{
protected static $tags = [];
protected static $weekMap = [
0 => 'Sunday',
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
];
/**
* 检查crontab格式是否支持
* @param string $cronstr
* @return boolean true|false
*/
public static function check($cronstr, $checkCount = true)
{
$cronstr = trim($cronstr);
$splitTags = preg_split('#\s+#', $cronstr);
if ($checkCount && count($splitTags) !== 5) {
return false;
}
foreach ($splitTags as $tag) {
$r = '#^\*(\/\d+)?|\d+([\-\/]\d+(\/\d+)?)?(,\d+([\-\/]\d+(\/\d+)?)?)*$#';
if (preg_match($r, $tag) == false) {
return false;
}
}
return true;
}
/**
* 格式化crontab格式字符串
* @param string $cronstr
* @param interge $maxSize 设置返回符合条件的时间数量, 默认为1
* @return array 返回符合格式的时间
*/
public static function formatToDate($cronstr, $maxSize = 1)
{
if (!static::check($cronstr)) {
throw new \Exception("格式错误: $cronstr", 1);
}
$dates = [];
self::$tags = preg_split('#\s+#', $cronstr);
$crons = [
'minutes' => static::parseTag(self::$tags[0], 0, 59), //分钟
'hours' => static::parseTag(self::$tags[1], 0, 23), //小时
'day' => static::parseTag(self::$tags[2], 1, 31), //一个月中的第几天
'month' => static::parseTag(self::$tags[3], 1, 12), //月份
'week' => static::parseTag(self::$tags[4], 0, 6), // 星期
];
$crons['week'] = array_map(function($item){
return static::$weekMap[$item];
}, $crons['week']);
return self::getDateList($crons, $maxSize);
}
/**
* 递归获取符合格式的日期,直到取到满足$maxSize的数为止
* @param array $crons 解析crontab字符串后的数组
* @param interge $maxSize 最多返回多少数据的时间
* @param interge $year 指定年
* @return array|null 符合条件的日期
*/
private static function getDateList(array $crons, $maxSize, $year = null)
{
$dates = [];
// 年份基点
$nowyear = ($year) ? $year : date('Y');
// 时间基点已当前为准,用于过滤小于当前时间的日期
$nowtime = strtotime(date("Y-m-d H:i"));
foreach ($crons['month'] as $month) {
// 获取此月最大天数
$maxDay = cal_days_in_month(CAL_GREGORIAN, $month, $nowyear);
foreach (range(1, $maxDay) as $day) {
foreach ($crons['hours'] as $hours) {
foreach ($crons['minutes'] as $minutes) {
$i = mktime($hours, $minutes, 0, $month, $day, $nowyear);
if ($nowtime >= $i) {
continue;
}
$date = getdate($i);
// 解析是第几天
if (self::$tags[2] != '*' && in_array($date['mday'], $crons['day'])) {
$dates[] = date('Y-m-d H:i', $i);
}
// 解析星期几
if (self::$tags[4] != '*' && in_array($date['weekday'], $crons['week'])) {
$dates[] = date('Y-m-d H:i', $i);
}
// 天与星期几
if (self::$tags[2] == '*' && self::$tags[4] == '*') {
$dates[] = date('Y-m-d H:i', $i);
}
$dates = array_unique($dates);
if (isset($dates) && count($dates) == $maxSize) {
break 4;
}
}
}
}
}
// 已经递归获取了.但是还是没拿到符合的日期时间,说明指定的时期时间有问题
if ($year && !count($dates)) {
return [];
}
if (count($dates) != $maxSize) {
// 向下一年递归
$dates = array_merge(self::getDateList($crons, $maxSize, ($nowyear + 1)), $dates);
}
return $dates;
}
/**
* 解析元素
* @param string $tag 元素标签
* @param integer $tmin 最小值
* @param integer $tmax 最大值
* @throws \Exception
*/
private static function parseTag($tag, $tmin, $tmax)
{
if ($tag == '*') {
return range($tmin, $tmax);
}
$step = 1;
$dateList = [];
// x-x/2 情况
if (false !== strpos($tag, ',')) {
$tmp = explode(',', $tag);
// 处理 xxx-xxx/x,x,x-x
foreach ($tmp as $t) {
if (self::checkExp($t)) {// 递归处理
$dateList = array_merge(self::parseTag($t, $tmin, $tmax), $dateList);
} else {
$dateList[] = $t;
}
}
}
else if (false !== strpos($tag, '/') && false !== strpos($tag, '-')) {
list($number, $mod) = explode('/', $tag);
list($left, $right) = explode('-', $number);
if ($left > $right) {
throw new \Exception("$tag 不支持");
}
foreach (range($left, $right) as $n) {
if ($n % $mod === 0) {
$dateList[] = $n;
}
}
}
else if (false !== strpos($tag, '/')) {
$tmp = explode('/', $tag);
$step = isset($tmp[1]) ? $tmp[1] : 1;
$dateList = range($tmin, $tmax, $step);
}
else if (false !== strpos($tag, '-')) {
list($left, $right) = explode('-', $tag);
if ($left > $right) {
throw new \Exception("$tag 不支持");
}
$dateList = range($left, $right, $step);
}
else {
$dateList = array($tag);
}
// 越界判断
foreach ($dateList as $num) {
if ($num < $tmin || $num > $tmax) {
throw new \Exception('数值越界');
}
}
sort($dateList);
return array_unique($dateList);
}
/**
* 判断tag是否可再次切割
* @return 需要切割的标识符|null
*/
private static function checkExp($tag)
{
return (false !== strpos($tag, ',')) || (false !== strpos($tag, '-')) || (false !== strpos($tag, '/'));
}
}