YiiBook.com-翻译动态


为pdo_mysql增加读写超时

需求,一般情况下,php读写mysql时并没有超时设定,仅仅通过php的set_time_limit设置总超时时间,强制停止。

如果是在web情况下,是没有问题的,但是如果在使用php做为命令行程序,那么就会出现问题,因为通常命令行程序是没有超时时间的,当一个php程序在读取mysql时(mysql可能当时连接过多,或过慢)那么这个php程序就会一个在等待,等待时间无限,这是一个非常棘手的问题,所以我们需要在程序中主动断掉这次请求,进行后续操作。ok,修改代码,重新编译php

源码修改

ext/pdo_mysql/mysql_driver.c

查找

#include "zend_exceptions.h"

改为

#include "zend_exceptions.h"
#include "SAPI.h"

查找

if (driver_options) {
        long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);

改为

if (driver_options) {
        long connect_timeout = pdo_attr_lval(driver_options, PDO_ATTR_TIMEOUT, 30 TSRMLS_CC);
        //new code
        long write_timeout = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_WRITE_TIMEOUT, 60 TSRMLS_CC);
        long read_timeout = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_READ_TIMEOUT, 60 TSRMLS_CC);
        long read_write_timeout_env = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_RW_TIMEOUT_ENV, PDO_MYSQL_RW_ENV_CLI TSRMLS_CC);
        int enable_read_write_timeout = 0;
        //all env
        if (read_write_timeout_env == PDO_MYSQL_RW_ENV_ALL)  {
                enable_read_write_timeout = 1;
        }
        // web
        else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_WEB && strcmp(sapi_module.name, "cli") != 0 ) {
                enable_read_write_timeout = 1;
        }
        // cli
        else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_CLI && strcmp(sapi_module.name, "cli") == 0 ) {
                enable_read_write_timeout = 1;
        }
        else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_NONE ) {
                enable_read_write_timeout = 0;
        }

查找

#ifndef PDO_USE_MYSQLND
        H->max_buffer_size = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, H->max_buffer_size TSRMLS_CC);
#endif

改为

#ifndef PDO_USE_MYSQLND
        H->max_buffer_size = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_MAX_BUFFER_SIZE, H->max_buffer_size TSRMLS_CC);
        // new code
        if (enable_read_write_timeout == 1) {
                if (mysql_options(H->server, MYSQL_OPT_WRITE_TIMEOUT, (const char *)&write_timeout)) {
                        pdo_mysql_error(dbh);
                        goto cleanup;
                }
                if (mysql_options(H->server, MYSQL_OPT_READ_TIMEOUT, (const char *)&read_timeout)) {
                        pdo_mysql_error(dbh);
                        goto cleanup;
                }
        }
#endif
#if defined(PDO_USE_MYSQLND)
        if (enable_read_write_timeout == 1) {
                H->server->data->net->data->options.timeout_read = (uint) read_timeout;
                H->server->data->net->data->options.timeout_write = (uint) write_timeout;
        }
#endif

ext/pdo_mysql/php_pdo_mysql_int.h

查找

#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
        PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY
#endif

改为

        PDO_MYSQL_RW_ENV_ALL,
        PDO_MYSQL_RW_ENV_CLI,
        PDO_MYSQL_RW_ENV_WEB,
        PDO_MYSQL_RW_ENV_NONE,
        PDO_MYSQL_ATTR_RW_TIMEOUT_ENV,
        PDO_MYSQL_ATTR_WRITE_TIMEOUT,
        PDO_MYSQL_ATTR_READ_TIMEOUT,
#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
        PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY
#endif

ext/pdo_mysql/pdo_mysql.c

查找

#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SERVER_PUBLIC_KEY", (long)PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY);
#endif

改为

        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_ALL",    (long)PDO_MYSQL_RW_ENV_ALL);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_CLI",    (long)PDO_MYSQL_RW_ENV_CLI);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_WEB",    (long)PDO_MYSQL_RW_ENV_WEB);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_NONE",    (long)PDO_MYSQL_RW_ENV_NONE);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_RW_TIMEOUT_ENV", (long)PDO_MYSQL_ATTR_RW_TIMEOUT_ENV);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_WRITE_TIMEOUT", (long)PDO_MYSQL_ATTR_WRITE_TIMEOUT);
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_TIMEOUT", (long)PDO_MYSQL_ATTR_READ_TIMEOUT);
#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
        REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SERVER_PUBLIC_KEY", (long)PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY);
#endif
编译

编译方式正常,不管是使用mysqlnd还是libmysqlclient方式,都可以。但是两种方式编译后,产生的读写超时,报错信息不一致

libmysqlclient方式会产生

SQLSTATE[HY000]: General error: 2013 Lost connection to MySQL server during query

mysqlnd

PDOStatement::execute(): MySQL server has gone away
这里说一个,因为mysqlnd在设置读写超时时,也是使用了stream_set_timeout,没有mysqlclient的错误信息直观

使用

通过以上操作,我们新增加了两个pdo的属性,用于动态设置读写超时,单位秒 , 默认60秒

PDO::MYSQL_ATTR_READ_TIMEOUT
PDO::MYSQL_ATTR_WRITE_TIMEOUT

另一个属性用来设置开启读写超时的运行环境

PDO::MYSQL_ATTR_RW_TIMEOUT_ENV

取值范围是

PDO::MYSQL_RW_ENV_ALL  // 所有环境
PDO::MYSQL_RW_ENV_CLI  // cli环境
PDO::MYSQL_RW_ENV_WEB  // web环境,或者说是非cli
PDO::MYSQL_RW_ENV_NONE //不使用读写超时

默认是PDO::MYSQL_RW_ENV_CLI, 即仅在cli方式下允许读写超市

在Yii中使用,只需配置一个数据库连接对象,请参考以下代码

'db'=>array(
    'connectionString' => 'mysql:host=localhost;dbname=blog',
    'schemaCachingDuration'=> YII_DEBUG ? 0: 3600,
    'emulatePrepare' => !YII_DEBUG,
    'enableParamLogging'=> !YII_DEBUG,
    'username' => 'root',
    'password' => '',
    'charset' => 'utf8',
    'attributes'=>array(
        PDO::MYSQL_ATTR_READ_TIMEOUT=>10,
        PDO::MYSQL_ATTR_WRITE_TIMEOUT=>10,
     )
),

通过上面的配置,本连接的查询或写入超时时间为10秒,即超过10秒, 客户端将主动段开连接。如果不配置这两个属性,默认读写超时时间为60秒。

测试

$sql = "select sleep(5)";
$a = Yii::app()->db->createCommand($sql)->execute();
var_dump($a);
$sql = "select sleep(20)";
$a = Yii::app()->db->createCommand($sql)->execute();
var_dump($a);

第一条sql执行成功,因为上面我们的配置是10秒超时。第二条过了10秒后,程序主动与mysql断开。

下载

http://github.com/netyum/pdo_mysql
下载对应版本

2.x中的GridView

参考2.0中的GridView的源码实现,2.0中的GridView比1.x中稍有增强,可定制化程序更高一下。

1.先说一下columns

<?php echo GridView::widget(array(
    'caption'=-->'<h1>Manage Posts</h1>',
	'dataProvider'=>$provider,
	'columns' => array(
		array(
			'class' => SerialColumn::className(),
			'header' => '#'
		),
		array(
			'class' => DataColumn::className(),
			'attribute'=>'title',
			'header' => 'title',
			'content'=>function($data, $row) {
				return Html::a(Html::encode($data->title), $data->url);
			},
			'format'=>'html',
			'value'=>function($data, $row) {
				return Html::a(Html::encode($data->title), $data->url);
			}
		),
		array(
			'class' => DataColumn::className(),
			'attribute'=>'status',
			'content'=>function($data, $row) {
				return Lookup::item("PostStatus",$data->status);
			},
			'header'=>'status'
		),
		array(
			'class' => DataColumn::className(),
			'attribute'=>'create_time',
			'content'=>function($data, $row) {
				return date("Y/m/d", $data->create_time);
			},
			'header' => 'create_time',
		),
		array(
			'class' => DataColumn::className(),
			'content'=>function($data, $row) {
				$html = Html::a(NULL, array("update", "id"=>$data->id), array('class' => 'icon icon-edit'));
				$html .= ' | ';
				$html .= Html::a(NULL, array("delete", "id"=>$data->id), array('class'=>'delete icon icon-trash'));
				return $html;
			}
		),
	)
));

一般情况下,字段的值,不需要进行处理的只需设置
array(
‘class’ => DataColumn::className(),
‘attribute’=>’title’,
‘format’=>’text’,
‘header’ => ‘title’,
),
如果需要进行设置,2.x给出两个属性,让用户自己决择,两属性都必须使用回调方式。
一个是value,一个是content

如果两个属性都在存,只取value属性
区别是,当设置value后,也必须设置format,即value的返回值,yii还会根据format的设置进行一次处理。
如果使用content属性, 则不需要format属性了,直接使用content的结果。
当然,如果没有设置value和content,则也要设置format属性。

由于还没有ButtonColumn,所以暂时只能使用DataColumn了,不过这样使用更灵活,

从yii1.x 到 yii2.x 需要调整的对比

yii2.0预览版本刚刚发布,我就迫不及待的加入了研究,与提交bug的行列!,经过两天的学习,并将yii1.x中的blog示例移植到了yii2.0中。

下面说说1.x与2.x的对比,说的不对,欢迎拍砖。

1. 先从mvc说起

在1.x中,view的概念不太明显,就是一个在controler中的aciton方法include一个模板。
在2.0中,controller与view的关系发生了变化。以至于,你不能够在模板中,通过$this->var,获得一个controller中的变量了。

2.0中的controller与view的数据交换,view与view这间的数据交换,如下
controler:

	//定义一个共有方法,一会模板调用。
	public $publicvar = 'from controler public var';

	public function actionIndex() {
		//$this->view,指向的是view类.
		//放view传递一个参数,有类点似于Yii::$app->params,你自己定义就好了。
		$this->view->params['view'] = 'from controller';
	}

view:

	//在view中,需要通过$this->context来获得上下文,
	//就目前来看,这个上下文指的就是上面的controller,
	//不过如果是从一个widget渲染的模板,那$this->context指的确是widget类。

	//获得上下文的属性
	echo $this->context->publicvar."
";

	//获得模板变量的。
	echo $this->params['view']."
";

	//当然你也可以在view中指定
	$this->params['ok'] = "That's ok!"."
";
	//那么你就可以在layout模板或中调用这个值。

Controller的改变
2.0中的controller封装的东西少了一些,可能后续版本会加进来,就目前看
$this->redirect不能使用了,需要使用$this->response->redirect

增加了$this->populate方法用于接受提交数据到模型。比之前的$model->attributes=$_POST['LoginForm'];更优雅
1.x使用 :

$model = new LoginForm;
if ($_POST['LoginForm']) {
	$model->attributes = $_POST['LoginForm'];
}

2.0使用:
if (this->populate($_POST, $model)) {
//会自动为$model赋值.
}

路由变化

当定义的一个action名称由多少单词组成,如:

//in MemberController
public function actionUserManager() {
}

在1.x中,我将使用的路由是member/userManager
在2.0中, 路由应该使用member/user-manager

Model的改变
模型中多了一些东西,ActiveQuery,去掉了1.x中的CDbCriteria,当然,他们之前还不太一样
增加了ActiveRelation,我觉得2.0中的模型关联,用着不太方便。
2.0中的查询操作

//model is Post
//查找一条,其实Post::find()返回的是ActiveQuery对象,one是ActionQuery的方法
$model = Post::find()->one();
//查找多条
$models = Post::find()->all();

//给定参数,返回值不一样
//获得一条,没找到,返回null
$model = Post::find(1); //查找主键为1的,并且返回对像,其内部会自动加上一个one;

//查找多条,并有条件
$models = Post::find()->where('id>5')->all();
未完,待续。

PHP5.4使用PHP原生模板:endif;

从PHP5.3转向PHP5.4,原来的模板报错

PHP Parse error:  syntax error, unexpected 'endif' (T_ENDIF)

原因可能是之前使用了这样的结构

<?php  
if (xxxxx) :
    if (xxxxx) :
?>
xxxxx
<?php     
    endif; 
endif; 
?>

看错误,貌似PHP5.4没能解析两个endif;这样的语法,无奈只能分到两个<?php ?>中了

tengine+lua实现时时在线图片缩放,剪切。

tenginx+lua+shell(conver)+其实也是nginx+lua,因为项目的需求变化,包括界面改版的变化,以至于每一版本的图片尺寸不定,所以不可能保存不同尺寸的版本。所以只能在线根据需求,由服务器来自动处理。

第一版我是使用了nginx+phpfpm,实现起来非常easy,但是稳定性方面不佳,

第二版换成了lua,效果还不错,由于lua本身没有对图片处理的模块(可能有第三方的,不过我还没有深入研究),所以lua是调用shell脚本实现的(当然在调用shell脚本时,程序会blocking,性能有些影响),稳定性增加。>

首先,我们定了一些生成的规则, 生成参数,是放在主文件名的后面

例如,http://pic.yiibook.com/example.jpg这个是源图片

那么在调用缩略图时,使用http://pic.yiibook.com/example!200×100.jpg 就会生成一张以源图为基础的200宽100高的图片 !200×100就是具体的参数

ok,下面说一下我定义的参数,有几种情况,具体是根据业务上面的需求,而自行调用的

1.固定尺寸缩放

!200×100 将源图缩放为宽200x高100

!200 将源图缩放为宽200x高200

!200×100-50 将源图缩放为宽200x高100 并且图片质量为50 (这个是为了给手机端使用的,因为手机端可能需要图片的size更小一些

!200-50 将源图缩放为宽200x高200 并且图片质量为50

这个参数会将源图强制缩放到这个尺寸,所以可能会有所变形

2.等比缩放

:w200 将源图以宽为准200,开始缩放,(意思是,强制将源图的宽缩到200,高不管,那么这个图是一个与源图比例相同的,不会变型

:h200 将源图以高为准200,开始缩放, 意思与上面类似

:m200 将源图以(宽,高那个值大,以哪个为准,进行缩放,比如源图是300×400,那就会以高为准,先将高缩到200),但是如果宽高都没有达到,而不处理

同时也支持 :w200-50 :h200-50 :m200-50 的图片质量

3.中心剪辑

@200×300 将源图以(宽,高那个值小,以哪个为准,进行缩放,并在缩放后的图片,以另一边中间点(就是正中间,进行剪辑)

@200×300-50 同时支持图片质量

暂时我的程序就支持这三种参数格式,

OK,下面看一下nginx配置

    server
    {
           listen       80;

           server_name  pic.yiibook.com;

           #access_log  logs/pic.access.log  main;

           root   /var/www/pic;

           location / {
               index index.html;
           }

           #宽,高 像素
           location ~ (.*)!(\d+)x(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 1;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽,高 像素 质量
           location ~ (.*)!(\d+)x(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 2;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽高相等
           location ~ (.*)!(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 3;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }
                      #宽高相等质量
           location ~ (.*)!(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 4;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }

           #宽,高 像素等比
           location ~ (.*):(w|h|m)(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 5;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }
               expires 30d;
           }
           
           #宽,高 像素等比 质量
           location ~ (.*):(w|h|m)(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 6;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }

           #宽,高 CUT
           location ~ (.*)\@(\d+)x(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 7;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }
           
           #宽,高 CUT 质量
           location ~ (.*)\@(\d+)x(\d+)-(\d+).(gif|jpg|jpeg|png)$  {
               root   /var/file/thumb/picture;

               #bucketname = picture
               set $bucketname picture;
               #原图片路径
               set $srcPath /var/file/$bucketname;
               #目录图片路径
               set $destPath /var/file/thumb/$bucketname;

               #处理类型
               set $type 8;

               if (!-f $request_filename) {
                   rewrite_by_lua_file conf/lua/picture_image_thumb.lua;
               }

               expires 30d;
           }

           location ~ .*.(gif|jpg|jpeg|png|bmp|swf|css|js|html)$ {
               expires 30d;
           }

           error_page   500 502 503 504  /50x.html;
           location = /50x.html {
               root   html;
           }
     }

配置文件有些长,主要是匹配参数的规制,如果缩略图存在,就不生成了,大约有8个规则匹配,上面的set $type 8会传给lua进行处理

如果你是初次进行nginx+lua编程,建议使用file,如rewrite_by_lua_file,不要直接在nginx的配置文件中写lua代码,因为单引,双引的问题,会出现很诡异的问题

下面我们再看一下picture_image_thumb.lua的代码

local wh = {
        {'w',170},
        {'w',200},
        {'w',224},
        {'w',365},
        {'w',150},
        {'w',237},
        {'w',420},
        {'w',450},
        {'h',32},
        {'m',600},
}

-- 定义可缩放尺寸
local xy = {
        {132,123},
        {404,250},
        {239,192},
        {415,353},
        {157,124},

        {210,131},
        {150,100},
        {110,80},
        {200,150},

        {110,110},
        {345,230},
        {164,105},
        {231,73},
}

local qualitys = {10,20,30,40,50,60,70,80,90,100}

-- 匹配模式
-- 宽, 高,
if ngx.var.type == string.format('%d', 1) and string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)%.(%w+)') then
        _, _, uriName, width, height, extName = string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)%.(%w+)')

        size = "!" .. width .. "x" .. height
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽, 高, 质量
elseif ngx.var.type == string.format('%d', 2)  and string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, height, quality, extName = string.find(ngx.var.uri, '(.*)!(%d+)x(%d+)-(%d+)%.(%w+)')

        size = "!" .. width .. "x" .. height .. '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽高相等
elseif ngx.var.type == string.format('%d', 3)  and string.find(ngx.var.uri, '(.*)!(%d+)%.(%w+)') then
        _, _, uriName, width, extName = string.find(ngx.var.uri, '(.*)!(%d+)%.(%w+)')
        size = "!" .. width
        height = width
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽高相等, 质量
elseif ngx.var.type == string.format('%d', 4)  and string.find(ngx.var.uri, '(.*)!(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, quality, extName = string.find(ngx.var.uri, '(.*)!(%d+)-(%d+)%.(%w+)')

        size = "!" .. width .. '-' .. quality
        height = width
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 等比
elseif ngx.var.type == string.format('%d', 5)  and string.find(ngx.var.uri, '(.*):([whm])(%d+)%.(%w+)') then
        _, _, uriName, sidetype, num, extName = string.find(ngx.var.uri, '(.*):([whm])(%d+)%.(%w+)')

        size = ":" .. sidetype .. num
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 等比, 质量
elseif ngx.var.type == string.format('%d', 6)  and string.find(ngx.var.uri, '(.*):([whm])(%d+)-(%d+).(%w+)') then
        _, _, uriName, sidetype, num, quality, extName = string.find(ngx.var.uri, '(.*):([whm])(%d+)-(%d+)%.(%w+)')
        size = ":" .. sidetype .. num ..  '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

-- 宽,高 CUT
elseif ngx.var.type == string.format('%d', 7)  and string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)%.(%w+)') then
        _, _, uriName, width, height, extName = string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)%.(%w+)')
        size = "@" .. width .. "x" .. height
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName

elseif ngx.var.type == string.format('%d', 8)  and string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)-(%d+)%.(%w+)') then
        _, _, uriName, width, height, quality, extName = string.find(ngx.var.uri, '(.*)@(%d+)x(%d+)-(%d+)%.(%w+)')
        size = "@" .. width .. "x" .. height .. '-' .. quality
        srcFile = ngx.var.srcPath .. uriName .. "." .. extName
end

if quality ~= nil then
        -- 检测图片质量是否在范围内
        local qualityfound = 0
        for i=1, #qualitys do
                local q = qualitys[i]
                if string.format('%d', q) == string.format('%d', quality) then
                        qualityfound = 1
                        break;
                end
        end

        if  qualityfound == 0 then
                ngx.req.set_uri(ngx.var.uri, true)
        end
end

-- 等比
if sidetype ~= nil then
        local sidetypefound=0
        local i = 1
        for i=1, #wh do
                local t = wh[i][1]
                local n = wh[i][2]
                if string.format('%s', t) == sidetype and string.format('%d', n) == num then
                        sidetypefound = 1
                        break;
                end
        end

        if  sidetypefound == 0 then
                ngx.req.set_uri(ngx.var.uri, true)
        end
else


        local found=0
        local i = 1
        for i=1, #xy do
                local x = xy[i][1]
                local y = xy[i][2]
                if string.format('%d', x) == width and string.format('%d', y) == height then
                        found = 1
                        break;
                end
        end

        if found == 0 then
        -- 显示文件
        ngx.req.set_uri(ngx.var.uri, true)
    end
end

local i = 0
local last = 0
while true do
    i = string.find(uriName,'/',i+1)
    if i == nil then break end
    last = i
end

-- 文件路径
local uriPath = string.sub(uriName, 1, last)
-- 主文件名
local mainFilename = string.sub(uriName, last+1)

-- 创建目录
local dircmd = '/bin/mkdir -p ' .. ngx.var.destPath .. uriPath
os.execute(dircmd)

-- 创建文件
local command = '/opt/scripts/makeImage.sh \\' .. size .. ' ' .. srcFile .. ' ' .. ngx.var.destPath .. uriName .. '\\' .. size .. '.' .. extName
os.execute(command)

-- 显示文件
ngx.req.set_uri(ngx.var.uri, true)

lua文件主要是进行参数的验证,具体处理图片,都交给了shell脚本,因为如果不限制宽度,那么会被攻击,产生没有必要的图片,浪费服务器资源。

下面我们看一下makeImage.sh脚本,脚本调用了conver和identify命令,请自行安装imagemagick

#/bin/sh
CONVERT=/usr/bin/convert
IDENTIFY=/usr/bin/identify
DEFAULTQUALITY=70

make1 () {
        T=`echo "$1" | cut -b2- | cut -d- -f1`
        Q=`echo "$1" | cut -b2- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY
        fi

        W=`echo "$T" | cut -dx -f1`
        H=`echo "$T" | cut -dx -f2`
        $CONVERT $2 -resize $W\!x$H! -quality $Q $3
}

make2 () {
        #是宽还是高或才是m
        C=`echo "$1" | cut -b2`
        #像素
        T=`echo "$1" | cut -b3- | cut -d- -f1`
        #质量
        Q=`echo "$1" | cut -b3- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY;
        fi
        case "$C" in
                w)
                        RESIZE=$T
                        ;;
                h)
                        RESIZE=x$T
                        ;;
                m)
                        WH=`$IDENTIFY -format "%wx%h" $2`
                        W=`echo "$WH" | cut -dx -f1`
                        H=`echo "$WH" | cut -dx -f2`
                        if test `/usr/bin/expr $T - $W` -gt 0 && test `/usr/bin/expr $T - $H` ; then
                                RESIZE=$Wx$H
                        elif test `/usr/bin/expr $W - $H` -gt 0 ; then
                                RESIZE=$T
                        else
                                RESIZE=x$T
                        fi
                        ;;
                *)
                        exit 1;
        esac

        $CONVERT $2 -resize $RESIZE -quality $Q $3
}

make3 () {
        T=`echo "$1" | cut -b2- | cut -d- -f1`
        Q=`echo "$1" | cut -b2- | cut -d- -f2`
        if [ "$Q" == "$T" ]; then
                Q=$DEFAULTQUALITY
        fi

        W=`echo "$T" | cut -dx -f1`
        H=`echo "$T" | cut -dx -f2`

        SRCWH=`$IDENTIFY -format "%wx%h" $2`
        SRCW=`echo "$SRCWH" | cut -dx -f1`
        SRCH=`echo "$SRCWH" | cut -dx -f2`

        if test `/usr/bin/expr $SRCW - $SRCH` -gt 0 ; then
                FLAG='W'
                RESIZE=x$H
        else
                FLAG='H'
                RESIZE=$W
        fi

        TMPFILE=`mktemp`
        /bin/rm -rf $TMPFILE
        $CONVERT $2 -resize $RESIZE $TMPFILE
        REWH=`$IDENTIFY -format "%wx%h" $TMPFILE`
        REW=`echo "$REWH" | cut -dx -f1`
        REH=`echo "$REWH" | cut -dx -f2`
        if test $FLAG == 'W'; then
                DESTW=`/usr/bin/expr $W / 2`
                RESIZEW=`/usr/bin/expr $REW / 2`
                OFFSETW=`/usr/bin/expr $RESIZEW - $DESTW`
                $CONVERT -crop $T+$OFFSETW+0 -quality $Q $TMPFILE $3

        else
                DESTH=`/usr/bin/expr $H / 2`
                RESIZEH=`/usr/bin/expr $REH / 2`
                OFFSETH=`/usr/bin/expr $RESIZEH - $DESTH`
                $CONVERT -crop $T+0+$OFFSETH -quality $Q $TMPFILE $3
        fi
        /bin/rm -rf $TMPFILE
}

if test ! -f $2; then
        echo "无法访问$2: 没有那个文件"
        exit 1;
fi

$IDENTIFY -format "%wx%h" $2 > /dev/null 2>&1
REVAL=`echo $?`
if [ "x$REVAL" != "x0" ]; then
        echo "$2不是图片类型文件"
        exit 1;
fi

TYPE=`echo "$1" | cut -b1`;

case "$TYPE" in
  !)
        make1 $1 $2 $3
        ;;
  :)
        make2 $1 $2 $3
        ;;
  @)
        make3 $1 $2 $3
        ;;
  *)
        echo $"Usage: $0 {:w100|!200|@300x200}"
        exit 1
esac

现在这个程序就编写完了,如果你有任何问题,欢迎与我探讨,QQ:8025628

$this->redirect ,Yii::app()->createUrl的路由设置

1.$this->redirect这里的$this是当前的controller。可能是应用程序的也
可能是模块下的
这里仅将解一下第一个参能是url,当url是一个字符串时,它会自己动跳转
如$this->redirect('/'); 会跳转到站点根,如果你的当前主机为localhost,
那么他就会跳到http://localhost/
再者$this->redirect('/books');,则会跳到http://localhost/books
在应用程序的controller中,也可以使用$this->redirect('books');
也会跳到http://localhost/books
但是当你在module中这样使用,则会出现另一种情况,
当你打开urlManager,并设置了隐藏脚本文件,输入
如果你当前的访问地址为

http://localhost/admin/default/index

当使用$this->redirect('books'); 跳转, 跳转后地址则是

http://localhost/admin/default/books

这里只是说一下,redirect的简单跳转,我个人建议,如果不是跳到其他项目,
或外站$this->redirect('http://yiibook.com');,建议都使用下面的方法

(更多…)

Yii框加中,模块(Module)的加载名(Module Id)与模块所在真实路径的处理级使用Theme时的处理。

默认情况下,使用Gii创建一个Module,会在项目的protected/modules创建一个你指定模块名称的目录
目录中的结构和文件,是Moduel相关的。
如

点击Generate生成module

gii提示你,将
<?php
return array(
    'modules'=>array(
        'admin',
    ),
    ......
);

 

加入到应用程配置文件中,这里的admin就是刚才所指的moduleId
当然,如果仅仅是这样,就不用往下说了!

(更多…)

Yii中直接使用event和在behavior里使用event的区别

本文仅讲解Event与Behavior的使用与比较,更低层的原理,请参考框架原码实现

1.使用现有的Event

现有的Event就是说Yii框架本身就为你设置好了一些事件,你只要为这些事件定义处理函数,当事件被触发,就会执行之前定义的处理函数.

我们先来了解一下CApplication类, 这个应用程序的基类,些类定义了三个事件
onBeginRequest 用户请求前处理;
processRequest: 用户请求处理;
onEndRequest: 用户请求后处理;

让我们为onBeginRequest这个事件定义一个处理函数,打开index.php,在最后一行添加如下代码:

require_once($yii);
$app = Yii::createWebApplication($config);
Yii::app()->onBeginRequest=function($event) {
	echo "Hello this onBeginRequest<br />";
};

Yii::app()->onBeginRequest=function($event) {
	echo "Hello this onBeginRequest2<br />";
};

Yii::app()->onEndRequest=function($event) {
	echo "Hello this onEndRequest<br />";
};

$app->run();

刷新页面,你将在页面的头部与低部看到了Hello this onBeginRequest,Hello this onBeginRequest2与Hello this onEndRequest
上面我们为onBeginRequest事件定义了两个处理函数,yii内部会处理函数以CList方式存储,并在事件触发时,按附加顺序执行

(更多…)