写一个cli的入口文件
cli.php
define(‘MODE_NAME‘, ‘cli‘);
// 检测PHP环境
if(version_compare(PHP_VERSION,‘5.3.0‘,‘ 5.3.0 !‘);
define(‘APP_DEBUG‘, true);
// 定义应用目录
define(‘APP_PATH‘, __DIR__ . ‘/Application/‘);
// 引入ThinkPHP入口文件
require __DIR__ . ‘/ThinkPHP/ThinkPHP.php‘;
写一个执行文件
cron.php
define(‘AUTO_CRON‘, true);
include __DIR__ . ‘/cli.php‘;
数据库设计
DROP TABLE IF EXISTS `cron`;
CREATE TABLE IF NOT EXISTS `cron` (
`cron_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`expression` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`class` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`method` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`type` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`status` varchar(30) COLLATE utf8_unicode_ci NOT NULL DEFAULT ‘‘,
`created_at` timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00‘,
`updated_at` timestamp NOT NULL DEFAULT ‘0000-00-00 00:00:00‘,
`run_at` timestamp NULL DEFAULT NULL,
`ms` int(10) unsigned NOT NULL DEFAULT ‘0‘,
`error` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`cron_id`),
KEY `name` (`name`,`created_at`),
KEY `cron_status_index` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
执行文件 init.php
/写个hook程序执行init.php
use Think\Log, Think\Db, Cron\Model\Cron;
$Model = new \Think\Model();
$Has = !$Model->query("SHOW TABLES LIKE ‘cron‘")?false:true;
if(defined("AUTO_CRON") && $Has){
class CronCommand
{
protected $_initializedJobs;
protected $_jobs;
protected $_now;
public function __construct()
{
$this->_now = strtotime(date(‘Y-n-j H:i‘));
import("Cron.Common.Cron.tdcron_entry",‘‘,‘.php‘);
import("Cron.Common.Cron.tdcron",‘‘,‘.php‘);
}
/**
* 这里是放要执行的代码
*/
public function fire()
{
restore_error_handler();
restore_exception_handler();
$this->_initializedJobs = array();
$jobs = M(‘cron‘)->where("status = ‘initialized‘")->select();
/**
* @var $cron Cron
* 已存在 cron
*/
if($jobs) {
$cron = new Cron();
foreach ($jobs as $data) {
$cron->setData($data)->isNew(false);
$this->_initializedJobs[$data[‘name‘]] = $cron;
}
}
/**
* 新 cron
*/
foreach ($this->getCronJobs() as $name => $cronJob) {
if (isset($cronJob[‘expression‘])) {
$expression = $cronJob[‘expression‘];
} else {
Log::write(‘Cron expression is required for cron job "‘ . $name . ‘"‘,Log::WARN);
continue;
}
if ($this->_now != tdCron::getNextOccurrence($expression, $this->_now)) continue;
$cronJob[‘name‘] = $name;
$cron = isset($this->_initializedJobs[$name]) ? $this->_initializedJobs[$name] : $this->_initializedJobs[$name] = new Cron();
$cron->initialize($cronJob);
}
/* @var $cron Cron 处理*/
foreach ($this->_initializedJobs as $cron) {
$cron->run();
}
}
/**
* Get All Defined Cron Jobs
* 获取配置
* @return array
*/
public function getCronJobs()
{
if ($this->_jobs === null) {
$this->_jobs = C(‘beastalkd‘);
}
return $this->_jobs;
}
}
$command = new CronCommand();
$command->fire();
}
cron 模型
namespace Cron\Model;
use Common\Model;
use Think\Log;
/**
* Class Cron
* @method string getClass()
* @method string getMethod()
* @method string getName()
* @method string getType()
* @package Cron\Model
*/
class Cron extends Model{
const STATUS_COMPLETED = ‘completed‘;
const STATUS_FAILED = ‘failed‘;
const STATUS_INITIALIZED = ‘initialized‘;
const STATUS_RUNNING = ‘running‘;
protected $name = ‘cron‘;
protected $tableName = ‘cron‘;
protected $pk = ‘cron_id‘;
protected $_originalData = array();
/**
* 保存配置信息CLASS
*/
protected static $_cron_classes = array();
/**
* @param $class
* @return mixed 获取配置的 CLASS
*/
public function getSingleton($class)
{
isset(static::$_cron_classes[$class]) or static::$_cron_classes[$class] = new $class;
return static::$_cron_classes[$class];
}
/**
* @param $cronJob
* @return $this
* 初始化 任务状态
*/
public function initialize($cronJob)
{
foreach ($cronJob as $k => $v) {
$this->setData($k, $v);
}
$now = date(‘Y-m-d H:i:s‘);
$this->setData(‘status‘,self::STATUS_INITIALIZED)->setData(‘created_at‘,$now)->setData(‘updated_at‘,$now)->save();
return $this;
}
/**
* @return $this run 命令
*/
public function run()
{
$this->setData(‘run_at‘,date(‘Y-m-d H:i:s‘))->setData(‘status‘,self::STATUS_RUNNING)->save();
Timer::start();
try {
$class = $this->getData(‘class‘);
$method = $this->getData(‘method‘);
if (!class_exists($class)) throw new \Exception(sprintf(‘Class "%s" not found!‘, $class));
if (!method_exists($class, $method)) throw new \Exception(sprintf(‘Method "%s::%s()" not found!‘, $class, $method));
$callback = array($this->getSingleton($class), $method);
//new CLASS 使用操作方法
call_user_func($callback);
Timer::stop();
$this->setData(‘ms‘,round(Timer::diff() * 1000))->setData(‘status‘,self::STATUS_COMPLETED)->save();
} catch (\Exception $e) {
Timer::stop();
$this->setData(‘ms‘,round(Timer::diff() * 1000))
->setData(‘status‘,self::STATUS_FAILED)
->setData(‘error‘,$e->getMessage() . "\nParams:\n" . var_export($this->getDbFields(), true))->save();
Log::write($e->getMessage() . "\n" . $e->getTraceAsString(),Log::ERR);
}
return $this;
}
}
Common\Model 模型
namespace Common;
use Think\Model as ThinkModel;
/**
* Class Model
* @package Common
*
* @property \Think\Db\Driver\Mysql $db DB instance
*/
abstract class Model extends ThinkModel {
protected $_isNew = true;
protected $_jsonFields = array();
protected $_originalData = array();
protected function _after_find(&$result, $options) {
foreach ($this->_jsonFields as $field) {
is_string($_data = fnGet($result, $field)) and $result[$field] = json_decode($_data, true);
}
$this->_originalData = $result;
$this->_isNew = !$result;
parent::_after_find($result, $options);
}
protected function _after_save($result) {
}
protected function _before_find() {
$this->_originalData = array();
}
protected function _facade($data) {
foreach ($this->_jsonFields as $field) {
is_array($_data = fnGet($data, $field)) and $data[$field] = json_encode($_data);
}
return parent::_facade($data);
}
public function find($options = array()) {
$this->_before_find();
return parent::find($options);
}
public function getData($key = null) {
return $key === null ? $this->data : $this->__get($key);
}
public function getOptions() {
return $this->options;
}
public function getOriginalData($key = null) {
return $key === null ? $this->_originalData : fnGet($this->_originalData, $key);
}
/**
* Get or set isNew flag
*
* @param bool $flag
*
* @return bool
*/
public function isNew($flag = null) {
if ($flag !== null) $this->_isNew = (bool)$flag;
return $this->_isNew;
}
public function save($data = ‘‘, $options = array()) {
if ($this->_isNew) {
$oldData = $this->data;
$result = $this->add($data, $options);
$this->data = $oldData;
if ($result && $this->pk && is_string($this->pk)) {
$this->setData($this->pk, $result);
}
$this->_isNew = false;
} else {
$oldData = $this->data;
$result = parent::save($data, $options);
$this->data = $oldData;
}
$this->_after_save($result);
return $result;
}
public function setData($key, $value = null) {
is_array($key) ?
$this->data = $key :
$this->data[$key] = $value;
return $this;
}
}
原文:http://my.oschina.net/xinson/blog/416652