`

【转】yii框架,命令行应用程序设计

    博客分类:
  • php
 
阅读更多

首先,当您接触yii框架时,您会发现,它已经精心设计好了一套命令行应用程序,那就是yiic
命令yiic原有的一些命令,我们可以创建web应用/控制器/模型/模块等等。

我们可以安yiic的风格,写出基于yii框架的命令行应用,这里的命令行应用基本上是配合Web应用来做的,什么时候会用到呢,最明显的例子是,crontab的应用,
例如:我们设计一个发送邮件系统,由于用户,或发送的信件很多,通过web方式发送非常站用http服务器资源,
估,我们准备通过数据表模拟发送队列,通过crontab定时执行这个命令,读取数据库表中的部分数据,发送邮件,
这个命令只占用系统中的一个进程,不会影响http的响应。当然,大型网站,会有专门的job服务器。

费话不多说了,先搭建环境
1.当你通过yiic创建一个webapp应用后,
会在webapp/protected/下生成yiic.php, 由于是命令行应用,所以这里的yiic.php其实就是与webapp下的index.php一样的,命令行入口文件。
2.打开yiic文件,添加一行设置,将commands目录的路径添加到yiic中,这样,yiic就能够找到commands目录下的命令文件了,修改后的代码如下,红色为新加入代码:
<?php
// change the following paths if necessary
$yiic=dirname(__FILE__).'/../../yii-read-only/framework/yiic.php';
$config=dirname(__FILE__).'/config/console.php';
@putenv('YII_CONSOLE_COMMANDS='. dirname(__FILE__).'/commands' );
require_once($yiic);
3.我们在commands目录下创建一个文件,由于我们要做一个发邮件的命令,所以命名为MailCommand.php
代码如下:
<?php
/**
 * Description of MailCommand
 *
 * @author syang
 */
class MailCommand extends CConsoleCommand {

    public $defaultAction='cron';  //这是一个缺省action名称,默认是index,这里我修改成了cron

    /**
     * cron action
     * @param int $limit
     */
    public function actionCron($limit=5) {
        if (!$this->checkMon(__METHOD__)) {
            $this->runError('The command is running. Please try again later.');
        }

        if ($limit >= 100 or $limit <= 0) {
            $this->usageError('The limit parameter greater than 0 and less than 100');
        }

        MailQueue::model()->send($limit);
    }


    public function usageError($message)
    {
        echo("Error: $message\n\n".$this->getHelp()."\n");
        exit(1);

    }

    public function runError($message)
    {
        echo("Error: $message\n\n");
        exit(1);
    }

    protected function checkMon($action='', $process_num=1) {
        if ($action=='') $action = $this->defaultAction;
        $commandname = get_class($this);
        $commandname = str_replace('command', '', strtolower($commandname));
        
        if (strpos($action, "::")!== false) {
            $action = substr(strtolower(end(explode("::", $action))), strlen('action'));
        }
        $cmd = "ps -ef | grep -v grep | grep yiic | grep \"{$commandname}\" ";
        if ($action!=$this->defaultAction) {
            $cmd .= "| grep \"{$action}\" ";
        }
        $cmd .= "| wc -l ";

        $current_process_num = shell_exec($cmd);
        if ( intval($current_process_num) > $process_num ) {
            return false;
        }
        else {
            return true;
        }
    }

}

ok,让我先讲解一下MailCommand类,这个扩展了CConsoleCommand,主要方法actionCron,与我们在Web应用中的控制器写方类似,actionCron方法有一个参数叫limit,它有一个缺省值,
注意:
如果actionXXX方法有参数,且没有缺省值,则在调用命令时,则必须指定参数(否则会报用法错误)。
如果actionXXX方法没有参数,则在调用命令时,不能指定参数 (否则会报用法错误)。
如果actionXXX有参数且有缺省值,则调用命令时,可以指定也可以不指定参数


usageError方法是继承自CConsoleCommand,由于CConsoleCommand类中定义的usageError仅仅只是die掉了,shell下返回的值还是0。所以,这里我修改了一下。
注意:通过在shell下执行一条命令后,可以通过$?获得刚才那条命令的执行情况。通常它与普通的程序语言的真假正好相反,即,$?为0表示上一条命令执行成功,如果$?非0则表示上条命令存在错误,执行不成功。

runError方法与usageError类似,只是不显示用法信息。

checkMon方法是我自己写的,主要是通过shell命令(仅限类nix系统)获得当前命令的进程数,举个例子,我们创建的mail命令,是通过crontab定义跑的,如果命令执行时间超过了下一次定时启动,则可能会出现2个或2个以上的进程。通过checkMon,我们基本上可以限制,这条命令可以启动几个进程,默认只有一个进程。通过checkMon方法,我们判断了如果有一个进程存在,就退出。不再运行。

4.ok,下面我们创建一个表mail_queue,sql如下:
CREATE TABLE IF NOT EXISTS `mail_queue` (
  `queue_id` int(11) unsigned NOT NULL auto_increment,
  `mail_to` varchar(150) NOT NULL default '',
  `mail_encoding` varchar(50) NOT NULL default '',
  `mail_subject` varchar(255) NOT NULL default '',
  `mail_body` text NOT NULL,
  `priority` tinyint(1) unsigned NOT NULL default '2',
  `err_num` tinyint(1) unsigned NOT NULL default '0',
  `add_time` int(11) NOT NULL default '0',
  `lock_expiry` int(11) NOT NULL default '0',
  PRIMARY KEY  (`queue_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8
注:为了演示,上面的表我直接使用了ecmall的邮件队列表,以下程序思路基本参考ecmall的程序
5.配置main/console.php,设置import路径,以及db连接,这部份与main.php类似。
并且在params部分添加了mailConfig的配置参数
            'params'=>array(
            // this is used in contact page
            'mailConfig'=>array(
                'sender'=> 'YiiBook Test Email',
                'from'=> 'webmaster@yiibook.com',
                'protocol' => '0', //1 is smtp
                'host' => '',
                'port' => '',
                'username' => '',
                'password' => '',
             )
         ),

6.通过yiic或gii创建mail_queue表的模型类MailQueue.php
7.  在  MailQueue.php中添加以下两个方法
   /**
    *    清除发送N次错误和过期的邮件
    */
    function clear()
    {
        $criteria=new CDbCriteria(array(
            'condition'=>" err_num > 3 or add_time < :addtime",
            'params'=>array(
                'addtime'=> time() - 259200
            )
        ));
        //这里使用了CDbCriteria创建查询条件,并通过指定占位符,进行变量绑定。
        return $this->deleteAll($criteria);
    }

    /**
    *    发送邮件
    */
    function send($limit = 5)
    {
        /* 清除不能发送的邮件 */
        $this->clear();

        $time = time();

        /* 取出所有未锁定的 */
        $mails  = $this->findAll(array(
            'condition'    =>  "lock_expiry < ?",
            'order'         =>  'add_time DESC, priority DESC, err_num ASC',
            'limit'         =>  $limit,
            'params'        => array($time)
        ));
        //这里直接使用数据代替CDbCriteria类作为条件参数,并使用?作为占位符,
       //条件中可以有多个?占位符,参数数组params中值的先后顺序会对应到条件中占位符的顺序。

        if (!$mails)
        {
            /* 没有邮件,不需要发送 */
            return 0;
        }

         /* 锁定待发送邮件 */
        $queueIds = $this->getQueueIds($mails);
        $lock_expiry = $time + 30;    //锁定30秒

        $this->updateAll(
            array(
                "err_num"=>new CDbExpression('err_num + 1'),
                'lock_expiry' => $lock_expiry
            ),
            "queue_id in ( {$queueIds} ) "
        );

        /* 获取邮件发送接口 */
        $mailer = new Mailer(Yii::app()->params->mailConfig);
        $mail_count = count($mails);
        $error_count= 0;
        $error      = '';

        /* 逐条发送 */
        for ($i = 0; $i < $mail_count; $i++)
        {
            $mail = $mails[$i];
            $result = $mailer->send($mail->mail_to, $mail->mail_subject, $mail->mail_body, $mail->mail_encoding, 1);
            if ($result)
            {
                /* 发送成功,从队列中删除 */
                $mail->delete();
            }
            else
            {
                $error_count++;
            }
        }
   
    }

    protected function getQueueIds($mails) {
        $queueIds = array();
        foreach($mails as $mail) {
            $queueIds[]=$mail->queue_id;
        }
        return implode(",", $queueIds);
    }
8.创建Mailer类
在components目录下,创建一个Mailer.php类,代码如下
<?php
/**
 * Description of Mail
 *
 * @author syang
 */
//注意,这里我们要使用一个第三方类库phpmailer,所以我们先将class.phpmailer.php放到vendors下,
//并通过import将路径加入到include_path中,

Yii::import('application.vendors.*');
//引用class.phpmailer.php,由于第三方类库可能不符合yii的加载规则,把以需要手动加载。
require_once 'class.phpmailer.php'; 


define('MAIL_PROTOCOL_LOCAL', '1');
define('CHARSET', 'UTF-8');

class Mailer {
    var $timeout    = 30;
    var $errors     = array();
    var $priority   = 3; // 1 = High, 3 = Normal, 5 = low
    var $debug      = false;

    var $mailer;

    function __construct($config)
    {
        @extract($config);
        $this->mailer = new phpmailer();

        $this->mailer->From     = $from;
        $this->mailer->FromName = $this->_base64_encode($sender);

        if ($protocol == MAIL_PROTOCOL_LOCAL)
        {
            /* mail */
            $this->mailer->IsMail();
        }
        else
        {
            /* smtp */
            $this->mailer->IsSMTP();
            $this->mailer->Host     = $host;
            $this->mailer->Port     = $port;
            $this->mailer->SMTPAuth = !empty($pass);
            $this->mailer->Username = $user;
            $this->mailer->Password = $pass;
        }
    }

    function send($mailto, $subject, $content, $charset, $is_html, $receipt = false)
    {
        $this->mailer->Priority     = $this->priority;
        $this->mailer->CharSet      = $charset;
        $this->mailer->IsHTML($is_html);
        $this->mailer->Subject      = $this->_base64_encode($subject);
        $this->mailer->Body         = $content;
        $this->mailer->Timeout      = $this->timeout;
        $this->mailer->SMTPDebug    = $this->debug;
        $this->mailer->ClearAddresses();
        $this->mailer->AddAddress($mailto);

        $res = $this->mailer->Send();
        if (!$res)
        {
            $this->errors[] = $this->mailer->ErrorInfo;
        }
        return $res;
    }

    function _base64_encode($str = '')
    {
        return '=?' . CHARSET . '?B?' . base64_encode($str) . '?=';
    }
}

9.ok,基本完成了,
在命令行下,如我们当前在protected目录下,执行
./yiic mail 

./yiic mail cron

./yiic mail cron --limit=10

总结,本篇仅通过一个具体的例子,演示了yii框架的命令行应用程序开发,仅做抛砖引玉。
涉及了shell的一些应用常识,yii框架的模型操作以及第三方类库的调用。
本人水平有限,欢迎拍砖。

分享到:
评论

相关推荐

    yii框架yii框架yii框架yii框架yii框架

    很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架很好用yii框架

    yii2命令行中使用migration备份和还原数据库

    yii2命令行中使用migration备份和还原数据库

    《Yii框架深度剖析》配套代码

    本书首先介绍 PHP 框架技术的概念及其应用领域,然后开始仿照 Yii 框架源代码 自定义框架,其中包括了 MVC 设计模式、单入口模式和应用(前端控制器模式)的实现。 充分了解了这部分知识后,读者对 Yii 框架也有了...

    php开发yii框架软件包

    yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架yii框架

    php最新框架YII框架

    据说是超越所有的PHP框架php最新框架YII框架php最新框架YII框架

    Yii框架学习手册

    Yii Yii框架 Yii框架快速入门,自己整理的很全的,很容易看懂

    YII框架应用

    YII框架应用

    yii框架中文文档、yii中文手册教程

    yii1.1中文文档 yii框架中文手册教程.pdf

    yii2.0基础高级应用程序模板

    yii2.0基础和高级应用程序模板两个,不想使用composer安装yii2.0的,就直接下载这个用吧

    yii框架教程

    yii框架教程yii框架教程yii框架教程yii框架教程yii框架教程yii框架教程

    YII框架中文手册

    YII框架中文手册,希望帮助需要帮助的人!

    yii 框架中文帮助文档

    YII框架的开发中文文档,帮助哪些正在学习YII框架的同学们,

    Yii 框架简介,主流PHP框架,值得学习。

    Yii 框架简介 全面轻松入手Yii 从这里开始 简单 明了 言简意赅

    yii框架(1.1.15)

    这是yii框架修复1.1.14漏洞后的最新版本1.1.15

    Yii框架快速入门

    Yii框架快速入门,本教材适用于PHP5.3以上,PHP5.2可能会存在 细节上的差异,主要介绍Yii入门的一些基础知识和简单例子.

    Yii开发框架 v2.0.12.zip

    通过一个简单的命令行工具yiic可以快速创建一个web应用程序的代码框架,开发者可以在生成的代码框架基础上添加业务逻辑,以快速完成应用程序的开发。 Yii开发框架 v2.0.12 更新日志 测试 测试覆盖对于早期检测...

    YII框架源码分析

    YII框架源码分析,这个比较适合深度去了解框架的原理!

    YII框架简介

    YII框架简介。学习yii框架必须要掌握的基础知识。

    yii框架中文手册

    Yii是一个高性能的PHP5的web应用程序开发框架。通过一个简单的命令行工具 yiic 可以快速创建一个web应用程序的代码框架,开发者可以在生成的代码框架基础上添加业务逻辑,以快速完成应用程序的开发。

    YII框架中文手册教程

    YII框架中文手册教程,YII框架中文,YII框架中文手册,YII框架中文教程,YII框架中文手册教程,YII框架中文,YII框架中文手册,YII框架中文

Global site tag (gtag.js) - Google Analytics