php - 私房课
当前位置:首页 » php

Yii中validator之scenario(即rulse中的on) On 编程语言  @2014-03-07

Yii中有个场景的概念,我们用到场景的地方主要是在model中定义rules规则的时候,可以对不同的场景使用不同的校验规则,所以,自然而然会认为所谓的场景就是对应的action的名字。其实场景和action的名字是不同的概念来的,不能混为一谈。scenario则是M的一种属性,理论上,scenario是独立于action概念的,可以在没有action的场合下使用scenario。model的scenario是通过设置它的scenario属性设置的,它和使用model的action没有任何关系。

比如:

$model=new User;
$model->scenario = ‘create’;


$model=new User(‘create’);

都是设置了执行一个action的时候对model进行校验的场景

1
2
3
4
5
6
7
8
9
10
11
12
13
public function rules() {
        // NOTE: you should only define rules for those attributes that
        // will receive user inputs.
        return array(
            array('username, email', 'required'),
            array('password, confirm_password', 'required', 'on'=>'create'),
            array('username', 'unique'),
            array('username, email', 'length', 'max'=>50),
            array('password, confirm_password', 'length', 'min'=>6, 'max'=>32),
            array('email', 'email'),
            array('confirm_password', 'compare', 'compareAttribute'=>'password', 'on'=>'create,update')
         );
}

上面的校验规则说明了初始密码和确认密码在create场景中是required的,和密码确认的compare是在create和update场景中都必须校验的。主要在action中对默认制定了对应的场景属性,在提交表单做校验的时候就能达到rules中定义的规则了。

下面是引用yii官方论坛帖子中牛人关于场景的解释:

场景的概念来自 用例驱动的方法学

用银行操作来说 存款 取款 转账 就是用例(关于用例的概念自己查阅相关资料或者google 简单点说就是里程碑式的操作 是系统级的方法
可以认为是应用级别的事务) 在面向对象中 模型层的东西会参加多个用例(一个对象有若干个方法 并不是所有方法在一次操作中都被用到
某个用例操作中只会用到一个或几个) 你可以这样认为用例等价场景 还有就是一个用例中可能不止一个模型参与其中

对AR来说 其中的若干属性只会参与一个用例(插入insert 更新update 或者搜索search) 在某种高度对象的方法和属性是一个级别概念 所以某些属性/方法 可以用用例来归组 经常在一起使用的方法或属性可以归为一个用例(场景)之中

用例可以自己定义 根据应用特征 比如User 这个模型类 在登陆操作(场景/用例/应用事务)中只会用到用户名和密码
所有这两个组合可以归为一个场景 其他属性根据业务可以自定义归组 ; 在拿改密码来说你可能需要用户输入旧密码 和新密码
那么这两个就会归属同一个用例(可以随意起名 比如changePassword等)

场景名不是yii定死的 但insert update 和search 是默认的(惯例而已) 在actionCreate actionUpdate
actionAdmin中出现的$model 默认的scenario 就分别是insert,update,search 你可以打印出来
echo $model->scenario ;die(); //在render 之前 试着调试输出;

官方链接地址:关于场景的解释

打签» ,   评论» Yii中validator之scenario(即rulse中的on)已关闭评论

php编译原理 On 编程语言  @2014-02-15

刚看到一个不错的文章,关于php编译的,分享下(忘记了地址~~):

最近要给Yahoo的同事们做一个关于PHP和Apache处理请求的内部机制的讲座,刚好写了些关于Opcodes的文字,就发上来了,这个文章基于 Sara Golemon大师的《Understanding OPcode》

Opcode是一种PHP脚本编译后的中间语言,就像Java的ByteCode,或者.NET的MSL,举个例子,比如你写下了如下的PHP代码:

1
2
3
4
5
<?php
   echo "Hello World";
   $a = 1 + 1;
   echo $a;
?>

PHP执行这段代码会经过如下4个步骤(确切的来说,应该是PHP的语言引擎Zend)

1.Scanning(Lexing) ,将PHP代码转换为语言片段(Tokens)
2.Parsing, 将Tokens转换成简单而有意义的表达式
3.Compilation, 将表达式编译成Opocdes
4.Execution, 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。
题外话:现在有的Cache比如APC,可以使得PHP缓存住Opcodes,这样,每次有请求来临的时候,就不需要重复执行前面3步,从而能大幅的提高PHP的执行速度。

那什么是Lexing? 学过编译原理的同学都应该对编译原理中的词法分析步骤有所了解,Lex就是一个词法分析的依据表。 Zend/zend_language_scanner.c会根据Zend/zend_language_scanner.l(Lex文件),来输入的 PHP代码进行词法分析,从而得到一个一个的“词”,PHP4.2开始提供了一个函数叫token_get_all,这个函数就可以讲一段PHP代码 Scanning成Tokens;
如果用这个函数处理我们开头提到的PHP代码,将会得到如下结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Array
(
    [0] => Array
        (
           [0] => 367
           [1] => Array
        (
            [0] => 316
            [1] => echo
        )
    [2] => Array
        (
            [0] => 370
            [1] =>
        )
    [3] => Array
        (
            [0] => 315
            [1] => "Hello World"
        )
    [4] => ;
    [5] => Array
        (
            [0] => 370
            [1] =>
        )
    [6] => =
    [7] => Array
        (
            [0] => 370
            [1] =>
        )
    [8] => Array
        (
            [0] => 305
            [1] => 1
        )
    [9] => Array
        (
            [0] => 370
            [1] =>
        )
    [10] => +
    [11] => Array
        (
            [0] => 370
            [1] =>
        )
    [12] => Array
        (
            [0] => 305
            [1] => 1
        )
    [13] => ;
    [14] => Array
        (
            [0] => 370
            [1] =>
        )
    [15] => Array
        (
            [0] => 316
            [1] => echo
        )
    [16] => Array
        (
            [0] => 370
            [1] =>
        )
    [17] => ;
)

分析这个返回结果我们可以发现,源码中的字符串,字符,空格,都会原样返回。每个源代码中的字符,都会出现在相应的顺序处。而,其他的比如标签,操作符,语句,都会被转换成一个包含俩部分的Array: Token ID (也就是在Zend内部的改Token的对应码,比如,T_ECHO,T_STRING),和源码中的原来的内容。
接下来,就是Parsing阶段了,Parsing首先会丢弃Tokens Array中的多于的空格,然后将剩余的Tokens转换成一个一个的简单的表达式

1.echo a constant string
2.add two numbers together
3.store the result of the prior expression to a variable
4.echo a variable

然后就改Compilation阶段了,它会把Tokens编译成一个个op_array, 每个op_arrayd包含如下5个部分:

1.Opcode数字的标识,指明了每个op_array的操作类型,比如add , echo
2.结果 存放Opcode结果
3.操作数1 给Opcode的操作数
4.操作数2
5.扩展值 1个整形用来区别被重载的操作符
比如,我们的PHP代码会被Parsing成:

* ZEND_ECHO ‘Hello World’
* ZEND_ADD ~0 1 1
* ZEND_ASSIGN !0 ~0
* ZEND_ECHO !0

呵呵,你可能会问了,我们的$a去那里了?
恩,这个要介绍操作数了,每个操作数都是由以下俩个部分组成:

a)op_type : 为IS_CONST, IS_TMP_VAR, IS_VAR, IS_UNUSED, or IS_CV

b)u,一个联合体,根据op_type的不同,分别用不同的类型保存了这个操作数的值(const)或者左值(var)
而对于var来说,每个var也不一样

IS_TMP_VAR, 顾名思义,这个是一个临时变量,保存一些op_array的结果,以便接下来的op_array使用,这种的操作数的u保存着一个指向变量表的一个句柄(整数),这种操作数一般用~开头,比如~0,表示变量表的0号未知的临时变量

IS_VAR 这种就是我们一般意义上的变量了,他们以$开头表示

IS_CV 表示ZE2.1/PHP5.1以后的编译器使用的一种cache机制,这种变量保存着被它引用的变量的地址,当一个变量第一次被引用的时候,就会被CV起来,以后对这个变量的引用就不需要再次去查找active符号表了,CV变量以!开头表示。

这么看来,我们的$a被优化成!0了。

打签»   评论» php编译原理已关闭评论

分享一个国内身份证号码验证程序 On 编程语言  @2013-10-21

本程序主要用于国内身份证号码的验证,支持15位与最新的18位身份证号码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
function is_idcard( $id )
{
    $id = strtoupper($id);
    $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/";
    $arr_split = array();
    if(!preg_match($regx, $id))
    {
        return FALSE;
    }
    if(15==strlen($id)) //检查15位
    {
        $regx = "/^(\d{6})+(\d{2})+(\d{2})+(\d{2})+(\d{3})$/";
 
        @preg_match($regx, $id, $arr_split);
        //检查生日日期是否正确
        $dtm_birth = "19".$arr_split[2] . '/' . $arr_split[3]. '/' .$arr_split[4];
        if(!strtotime($dtm_birth))
        {
            return FALSE;
        } else {
            return TRUE;
        }
    }
    else           //检查18位
    {
        $regx = "/^(\d{6})+(\d{4})+(\d{2})+(\d{2})+(\d{3})([0-9]|X)$/";
        @preg_match($regx, $id, $arr_split);
        $dtm_birth = $arr_split[2] . '/' . $arr_split[3]. '/' .$arr_split[4];
        if(!strtotime($dtm_birth))  //检查生日日期是否正确
        {
            return FALSE;
        }
        else
        {
            //检验18位身份证的校验码是否正确。
            //校验位按照ISO 7064:1983.MOD 11-2的规定生成,X可以认为是数字10。
            $arr_int = array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);
            $arr_ch = array('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');
            $sign = 0;
            for ( $i = 0; $i < 17; $i++ )
            {
                $b = (int) $id{$i};
                $w = $arr_int[$i];
                $sign += $b * $w;
            }
            $n  = $sign % 11;
            $val_num = $arr_ch[$n];
            if ($val_num != substr($id,17, 1))
            {
                return FALSE;
            }
            else
            {
                return TRUE;
            }
        }
    }
 
}
打签»   评论» 分享一个国内身份证号码验证程序已关闭评论

新鲜出炉LNMP安装包,快来下载呀 On 编程语言  @2013-10-18

DebianLNMP是一个用Linux Shell编写的可以为Debian/Ubuntu VPS(VDS)或独立主机安装LNMP(Nginx、MySQL、PHP、phpMyAdmin)生产环境的Shell程序。
DebianLNMP 是以 Debian + Linux + Nginx + MySQL + PHP 的安装顺序命名;
DebianLNMP一键安装包是很不错的安装脚本,用在低配置VPS上最合适不过啦,安装方便快捷,轻轻松松全自动安装LNMP(Nginx、MySQL、PHP、phpMyAdmin)可直接用再生产环境。

1
2
root@DebianLNMP-Jimmyli:~# wget http://sourceforge.net/projects/debian-lnmp/files/DebianLNMP/DebianLNMP-Jimmyli.sh
root@DebianLNMP-Jimmyli:~# sh DebianLNMP-Jimmyli.sh

下载地址:
http://sourceforge.net/projects/debian-lnmp/files/DebianLNMP/

打签» , , , , , ,   评论» 新鲜出炉LNMP安装包,快来下载呀已关闭评论

php常见的几种排序算法 On 编程语言  @2013-10-10

以下是应用示例,在这几种算法中,我分别对3000个数组元素做排序操作,其中插入排序、选择排序、冒泡排序花费的时间大约在60秒左右(应不同电脑配置而定),而快速排序相对要快得多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?php
//插入排序(一维数组)
function insert_sort($arr){
    $count = count($arr);
    for($i=1; $i<$count; $i++){
        $tmp = $arr[$i];
        $j = $i - 1;
        while($arr[$j] > $tmp){
            $arr[$j+1] = $arr[$j];
            $arr[$j] = $tmp;
            $j--;
        }
    }
    return $arr;
}


//选择排序(一维数组)
function select_sort($arr){
    $count = count($arr);
    for($i=0; $i<$count; $i++){
        $k = $i;
        for($j=$i+1; $j<$count; $j++){
            if ($arr[$k] > $arr[$j])
                $k = $j;
            if ($k != $i){
                $tmp = $arr[$i];
                $arr[$i] = $arr[$k];
                $arr[$k] = $tmp;
            }
        }
    }
    return $arr;
}

//冒泡排序(一维数组) 
function bubble_sort($array){ 
    $count = count($array); 
    if ($count <= 0) return false; 
    
    for($i=0; $i<$count; $i++){ 
        for($j=$count-1; $j>$i; $j--){ 
            if ($array[$j] < $array[$j-1]){ 
                $tmp = $array[$j]; 
                $array[$j] = $array[$j-1]; 
                $array[$j-1] = $tmp; 
            } 
        } 
    } 
    return $array; 
} 

//快速排序(一维数组) 
function quick_sort($array){ 
    if (count($array) <= 1) return $array; 

    $key = $array[0]; 
    $left_arr = array(); 
    $right_arr = array(); 
    for ($i=1; $i<count($array); $i++) {
        if($array[$i] <= $key) {
            $left_arr[] = $array[$i]; 
        } else {
            $right_arr[] = $array[$i];
        }
    }
    $left_arr = quick_sort($left_arr); 
    $right_arr = quick_sort($right_arr); 
    
    return array_merge($left_arr, array($key), $right_arr); 
} 

?>
打签» ,   评论» php常见的几种排序算法已关闭评论

使用PHPExcel类读取excel文件中的内容 On 编程语言  @2013-01-11

//引入PHPExcel类库
require_once 'PHPExcel.php';
/**对excel里的日期进行格式转化*/
function GetData($val){
    $jd = GregorianToJD(1, 1, 1970);
    $gregorian = JDToGregorian($jd+intval($val)-25569);
    return $gregorian;/**显示格式为  “月/日/年”  */
}

			$filePath = 'test.xlsx';
			$phpExcel = new PHPExcel ();
			$PHPReader = new PHPExcel_Reader_Excel2007();
			if (! $PHPReader->canRead ( $filePath )) {
				$PHPReader = new PHPExcel_Reader_Excel5 ();
				if (! $PHPReader->canRead ( $filePath )) {
					echo '不是EXCEL文件!';exit;
				}
			}
			$PHPExcel = $PHPReader->load($filePath);
			//读表第一个工作表
			$currentSheet = $PHPExcel->getSheet(0);
			//取得最大的列号
			$allColumn = $currentSheet->getHighestColumn();
			//取得总行数
			$allRow = $currentSheet->getHighestRow();
			//从第二行开始输出,因为第一行为列名
			for($currentRow = 2;$currentRow <= $allRow;$currentRow++){
				//从A列开始读取数据
				for($currentColumn= 'A';$currentColumn<= $allColumn; $currentColumn++){
					//ord()将字符转为十进制数
					$val = $currentSheet->getCellByColumnAndRow(ord($currentColumn) - 65,$currentRow)->getValue();
					if($currentColumn=="F") { //假设F列为日期格式
						echo GetData($val);
					}
					else {
						//防止乱码,使用iconv函数转码输出
					    echo iconv('utf-8','gb2312', $val)."\t";
					}
				}
				echo "
"; }

PHPExcel下载地址:http://phpexcel.codeplex.com/releases/view/96183

打签» ,   评论» 使用PHPExcel类读取excel文件中的内容已关闭评论

在IE下完美解决history.go(-1)后表单数据丢失 On 编程语言  @2012-10-23

有时候我们需要对用户提交的表单数据进行后端验证,验证的结果需要反馈给用户。常见的方式有ajax,另一个方式就是提交到目标页进行验证,当验证失败后提示相应的错误信息然后需要返回到表单页让用户重新修改。这时我们可以使用JS返回:

<script >
   window.history.go(-1);
</script >

有时候这段代码正常,可是在IE下当表单页开启了session_start()目标页返回的时候表单的数据全部消失了。下面给出两个解决方案:
方案一:
表单所在页面所使用的缓冲控制方法:
当session_cache_limiter(‘private’)时,用处是让表单history.go(-1)的时候,填写内容不丢失!就避免页面失效的警告!
方案二:
这个会话与header(‘cache-control:private,must_revalidate’);效果相同

打签» ,   评论» 在IE下完美解决history.go(-1)后表单数据丢失已关闭评论

swfupload+php超大文件上传 On 编程语言  @2012-09-17

swfupload是一个不错的文件上传组件,采用flash+js开发。本文主要介绍采用swfupload上传文件时经常出现的错误及解决方法。
1.第一种错误(如果你的WEB服务器为nginx)
Server (IO) Error
打开debug查看到
SWF DEBUG: Event: uploadError : IO Error : File ID: swfupload_0_0. IO Error: Error #2038
另外一种表现形式
Upload Error: 413

解决方案
修改nginx.conf的http段,如没有下面这行,可增加
client_max_body_size 100m;
结果如下:

http
{
  ....这里省略
  client_max_body_size 100m;
  ....这里省略
}
/etc/init.d/nginx restart

2.第二种错误
如果$_FILES为空
解决方案
修改php.ini文件 post_max_size 设定 POST 数据所允许的最大大小。此设定也影响到文件上传。
另外要说明的是,post_max_size 大于 upload_max_filesize 为佳

3.备注:
如果你还有其它错误码,请查看php.ini文件的 upload_max_filesize = 100M 值是不是够大.
有网友说要改max_execution_time = 0 在swfupload里其实是不必要的,如果不行也可以改改看.

打签» ,   评论» swfupload+php超大文件上传已关闭评论

php中escapeshellarg的简单使用 On 编程语言  @2012-09-17

有些时候我们需要使用php的system(), exec(), passthru(), popen()函数去执行相应的命令,但往往不成功或存在一些安全问题。这个时候我们需要使用escapeshellarg()这个函数对输入的命令进行转义。

这个函数的语法:escapeshellarg($string)
$string所在处是用于过滤的输入,返回值是过滤后的字符。执行时,这个函数将在字符两边添加单引号,并转义原来字符串中的单引号(在其前边加上\)。

$cmp_name = escapeshellarg($cmp_name);
通过上面这个示例,我们就能通过确保传递给系统调用的参数已经处理,是一个没有其他意图的用户输入,以规避这样的安全风险。

小提示:
在php中,escapeshellcmd()和escapeshellarg()类似,只是它只转义对底层操作系统有特殊意义的字符。和escapeshellarg()不同,escapeshellcmd()不会处理内容中的空白格。举个实例,当使用escapeshellcmd()转义时,字符

$string = “‘hello, world!’;evilcommand”
将变为:

\’hello, world\’\;evilcommand
如果这个字符串用作系统调用的参数它将仍然不能得到正确的结果,因为shell将会把它分别解释为两个分离的参数: \’hello 和 world\’\;evilcommand。如果用户输入用于系统调用的参数列表部分,escapeshellarg()是一个更好的选择。

打签»   评论» php中escapeshellarg的简单使用已关闭评论

php抓取远程指定页面内容(函数) On 编程语言  @2012-05-25

这个函数是从HDwiki系统中截取出来的,感觉使用比较方便,所以就放了上来。可以用来抓取远程页面中的内容、图片等。具体可参考这个代码!

 8192 ? 8192 : $limit));
				$return .= $data;
				if($limit) {
					$limit -= strlen($data);
					$stop = $limit <= 0;
				}
			}
		}
		@fclose($fp);
		return $return;
	}
}
?>
打签» ,   评论» php抓取远程指定页面内容(函数)已关闭评论