函数使用格式

empty

bool empty ( mixed $var )

判断变量是否为空。

isset

bool isset ( mixed $var [ , mixed $... ] )

判断变量是否被设置且不为NULL。

参数说明

对于empty,在PHP5.5版本以前,empty只支持变量参数,其他类型的参数会导致解析错误,比如函数调用的结果不能作为参数。

对于isset,如果变量被如unset的函数设为NULL,则函数会返回false。如果多个参数被传递到isset函数,那么只有所有参数都被设置isset函数才会返回true。从左到右计算,一旦遇到没被设置的变量就停止。

运行示例

$result = empty(0); // true
$result = empty(null); // true
$result = empty(false); // true
$result = empty(array()); // true
$result = empty('0'); // true
$result = empty(1); // false
$result = empty(callback function); // 报错 $a = null;
$result = isset($a); // false; $a = 1;
$result = isset($a); // true; $a = 1;$b = 2;$c = 3;
$result = isset($a, $b, $c); // true $a = 1;$b = null;$c = 3;
$result = isset($a, $b, $c); // false

找到函数的定义位置

实际上,empty不是一个函数,而是一个语言结构。语言结构是在PHP程序运行前编译好的,因此不能像之前那样简单地搜索PHP_FUNCTION emptyZEND_FUNCTION empty查看其源码。要想看empty等语言结构的源码,先要理解PHP代码执行的机制。

PHP执行代码会经过4个步骤,其流程图如下所示:

在第一个阶段,即Scanning阶段,程序会扫描zend_language_scanner.l文件将代码文件转换成语言片段。对于isset和empty函数来说,在zend_language_scanner.l文件中搜索empty和isset可以得到函数在此文件中的宏定义如下:

<ST_IN_SCRIPTING>"isset" {
return T_ISSET;
} <ST_IN_SCRIPTING>"empty" {
return T_EMPTY;
}

接下来就到了Parsing阶段,这个阶段,程序将T_ISSET和T_EMPTY等Tokens转换成有意义的表达式,此时会做语法分析,Tokens的yacc保存在zend_language_parser.y文件中,可以找到T_ISSET和T_EMPTY的定义:

internal_functions_in_yacc:
T_ISSET '(' isset_variables ')' { $$ = $3; }
| T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
| T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
| T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
| T_EVAL '(' expr ')' { zend_do_include_or_eval(ZEND_EVAL, &$$, &$3 TSRMLS_CC); }
| T_REQUIRE expr { zend_do_include_or_eval(ZEND_REQUIRE, &$$, &$2 TSRMLS_CC); }
| T_REQUIRE_ONCE expr { zend_do_include_or_eval(ZEND_REQUIRE_ONCE, &$$, &$2 TSRMLS_CC); }
;

isset和empty函数最终都执行了zend_do_isset_or_isempty函数,在源码目录中查找

grep -rn "zend_do_isset_or_isempty"

可以发现,此函数在zend_compile.c文件中定义。

函数执行步骤

1、解析参数

2、检查是否为可写变量

3、如果是变量的op_type是IS_CV(编译时期的变量),则设置其opcode为ZEND_ISSET_ISEMPTY_VAR;否则从active_op_array中获取下一个op值,根据其op值设置last_op的opcode。

4、设置了opcode之后,之后会交给zend_excute执行。

源码解读

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

对于empty函数,到了opcode的步骤后,参阅opcode处理函数,可以知道,isset和empty在excute的时候执行的是ZEND_ISSET_ISEMPTY_VAR等一系列函数,以ZEND_ISSET_ISEMPTY_VAR_SPEC_CV_VAR_HANDLER为例,找到这个函数的定义在zend_vm_execute.h。查看函数可以知道,empty函数的最终执行函数是i_zend_is_true(),而i_zend_is_true函数定义在zend_execute.h。i_zend_is_true函数的核心代码如下:



switch (Z_TYPE_P(op)) {
case IS_NULL:
result = 0;
break;
case IS_LONG:
case IS_BOOL:
case IS_RESOURCE:
// empty参数为整数时非0的话就为false
result = (Z_LVAL_P(op)?1:0);
break;
case IS_DOUBLE:
result = (Z_DVAL_P(op) ? 1 : 0);
break;
case IS_STRING:
if (Z_STRLEN_P(op) == 0
|| (Z_STRLEN_P(op)==1 && Z_STRVAL_P(op)[0]=='0')) {
// empty("0") == true
result = 0;
} else {
result = 1;
}
break;
case IS_ARRAY:
// empty(array) 是根据数组的数量来判断
result = (zend_hash_num_elements(Z_ARRVAL_P(op))?1:0);
break;
case IS_OBJECT:
if(IS_ZEND_STD_OBJECT(*op)) {
TSRMLS_FETCH(); if (Z_OBJ_HT_P(op)->cast_object) {
zval tmp;
if (Z_OBJ_HT_P(op)->cast_object(op, &tmp, IS_BOOL TSRMLS_CC) == SUCCESS) {
result = Z_LVAL(tmp);
break;
}
} else if (Z_OBJ_HT_P(op)->get) {
zval *tmp = Z_OBJ_HT_P(op)->get(op TSRMLS_CC);
if(Z_TYPE_P(tmp) != IS_OBJECT) {
/* for safety - avoid loop */
convert_to_boolean(tmp);
result = Z_LVAL_P(tmp);
zval_ptr_dtor(&tmp);
break;
}
}
}
result = 1;
break;
default:
result = 0;
break;
}

这段代码比较直观,函数没有对检测值做任何的转换,通过这段代码来进一步分析示例中的empty函数做分析:
empty(null),到IS_NULL分支,result=0,i_zend_is_true() == 0,!i_zend_is_true() == 1,因此返回true。

empty(false),到IS_BOOL分支,result = ZLVAL_P(false) = 0,i_zend_is_true() == 0,!i_zend_is_true() == 1,因此返回true。

empty(array()),到IS_ARRAY分支,result = zend_hash_num_elements(Z_ARRVAL_P(op)) ? 1 : 0),zend_hash_num_elements返回数组元素的数量,array为空,因此result为0,i_zend_is_true() == 0,!i_zend_is_true() == 1,因此返回true。

empty('0'),到IS_STRING分支,因为Z_STRLENP(op) == 1 且 Z_STRVAL_P(op)[0] == '0',因此result为0,i_zend_is_true() == 0,!i_zend_is_true() == 1,因此返回true。

empty(1),到IS_LONG分支,result = Z_LVAL_P(op) = 1,i_zend_is_true == 1,!i_zend_is_true() == 0,因此返回false。

对于isset函数,最终实现判断的代码是:

if (isset && Z_TYPE_PP(value) != IS_NULL) {
ZVAL_BOOL(&EX_T(opline->result.var).tmp_var, 1);
} else {
ZVAL_BOOL(&EX_T(opline->result.var).tmp_var, 0);
}

只要value被设置了且不为NULL,isset函数就返回true。

php中empty和isset函数的更多相关文章

  1. php中empty(), is_null(), isset()函数区别

    empty(), is_null(), isset()真值表(区别) 我们先来看看这3个函数的功能描述 www.111cn.net isset 判断变量是否已存在,如果变量存在则返回 TRUE,否则返 ...

  2. php中empty(), is_null(), isset()函数区别(转)

    empty(), is_null(), isset()真值表(区别)   我们先来看看这3个函数的功能描述 www.111cn.net isset 判断变量是否已存在,如果变量存在则返回 TRUE,否 ...

  3. [PHP源码阅读]empty和isset函数

    近日被问到PHP中empty和isset函数时怎么判断变量的,刚开始我是一脸懵逼的,因为我自己也只是一知半解,为了弄懂其真正的原理,赶紧翻开源码研究研究.经过分析可发现两个函数调用的都是同一个函数,因 ...

  4. PHP中empty、isset和is_null的使用区别

    关于PHP中empty().isset() 和 is_null() 这三个函数的区别,之前记得专门总结过,上次又被问到,网上已经很多,就用几个例子来说明: 测试用例选取: <?php $a;$b ...

  5. php中empty()、isset()、is_null()和变量本身的布尔判断区别(转)

    在php脚本中,我们经常要去判断一个变量是否已定义或者是否为空,就需要用到这些函数empty().isset().is_null()和其本身作为参数,下面小段程序做个简要比较 <?php//预定 ...

  6. php中empty(),isset(),is_null(),==,===区别

    有关 PHP 的 empty(),isset() 还有 is_null() 这三个函数的用法讨论得已经很多了,而且很多资料也未必能说得很清楚.这里再重复一次,但不是从概念去说,直接用程序例子来说话,应 ...

  7. empty和isset函数详解

    1.empty函数 用途:检测变量是否为空 若变量不存在则返回 TRUE 若变量存在且其值为"".0."0".NULL..FALSE.array().var $ ...

  8. ***PHP中empty()和isset()的区别

    对于初学php的人来说,empty()和和isset()用法的区别是很难搞清楚的,他们的用法的差别不仔细去琢磨的话确实很难弄清楚. 先说一下他们的共同点: 都可以判定一个变量是否为空: 都返回bool ...

  9. php中empty和isset的区别

    比如: $_POST[ 'test']= ' ';那么: isset($_POST[ 'test']) ===> true; empty($_POST[ 'test']) ===> tru ...

随机推荐

  1. 《编写高质量代码:改善JavaScript程序的188个建议》学习小记(二)

    建议3:减少全局变量污染 1.把多个全局变量都追加在一个名称空间下,将显著降低与其他应用程序产生冲突的概率,应用程序也会变得更容易阅读. var My = {}; My.name = { " ...

  2. Java写的数据库连接池

    原文地址: http://lgscofield.iteye.com/blog/1820521 import java.sql.*; import java.util.Enumeration; impo ...

  3. 一个愚蠢的python逻辑语法错误

    这个事情再次佐证了一个莫名其妙的现象背后一定会有一个愚蠢到无以复加的错误的真理. 写python单元测试的时候发现一个莫名其妙的问题: def xmlStandander(self,s): retur ...

  4. 61.volatile关键字

    volatile作用 volatile的作用是可以保持共享变量的可见性,即一个线程修改一个共享变量后,另一个线程能够读取到这个修改后的值. 先来看一个问题: 定义一个Task类 package com ...

  5. JS简介——(一)

    0.结构

  6. innobackupex 相关语法讲解【转】

    innobackupex 相关语法讲解 连接服务器 The database user used to connect to the server and its password are speci ...

  7. 使用pandas把mysql的数据导入MongoDB。

    使用pandas把mysql的数据导入MongoDB. 首先说下我的需求,我需要把mysql的70万条数据导入到mongodb并去重, 同时在第二列加入一个url字段,字段的值和第三列的值一样,代码如 ...

  8. IIS错误:在唯一密钥属性“fileExtension”设置为“.mp4”时,无法添加类型为“mimeMap”的重复集合项

    之前的IIS配置中,.mp4类型的文件默认无法访问,所以个人在IIS7.5的mime类型中增加了.mp4的类型的支持,但问题是需要每台机器中增加这些设置,不利于团队协作,于是,只好将这些修改体现在代码 ...

  9. 17 Go Slices: usage and internals GO语言切片: 使用和内部

    Go Slices: usage and internals  GO语言切片: 使用和内部 5 January 2011 Introduction Go's slice type provides a ...

  10. 使用Scrapy命令行工具【导出JSON文件】时编码设置

    Windows 10家庭中文版,Python 3.6.4,virtualenv 16.0.0,Scrapy 1.5.0, 使用scrapy命令行工具建立了爬虫项目(startproject),并使用s ...