在PHP中哪种方法可以更快地进行类型检查? gettype()或多个是_ *()

时间:2016-04-08 07:35:15

标签: php performance types dynamic-typing

在动态类型的PHP中,我们可以创建可以接受多种数据类型作为参数的函数。然后,我们可以根据变量的类型对数据进行操作。有两种方法可以做到这一点:

接近一个

function doSomething1($param) {
    $type = gettype($param);
    if ($type === 'string') {
        // do something
    }
    else if ($type === 'integer') {
        // do something
    }
    else if ($type === 'array') {
        // do something
    }
}

接近两个

function doSomething2($param) {
    if (is_string($param)) {
        // do something
    }
    else if (is_int($param)) {
        // do something
    }
    else if (is_array($param)) {
        // do something
    }
}
  1. 据我所知,从测试的角度来看,这两种方法在功能上是等价的,但由于PHP有很多问题,所以如果我赞成采用另一种方法,我会问我是否有什么可以错过的?

  2. 从绩效的角度来看,将方法一比两个更快是正确的,因为PHP function calls are expensive?或者gettype()比单个is_*()函数更昂贵的操作?

  3. 是否有关于此的编码习语/风格指南?

  4. 更新 根据我使用PHP 7.0.4的基准测试,一百万次doSomething2()次迭代耗时159ms,略低于doSomething1()的一半时间,为315ms。无论是否传入字符串(第一次检查)或数组(最后一次检查),都是如此。这似乎表明gettype()确实是一项昂贵的操作,并且比使用is_*()的多个函数调用更昂贵

    如果有人更深入了解这可能是什么原因,我们将非常感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

让我们比较gettypeis_string函数的C代码。

gettype

PHP_FUNCTION(gettype)
{
    zval *arg;
    zend_string *type;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_ZVAL(arg)
    ZEND_PARSE_PARAMETERS_END();

    type = zend_zval_get_type(arg);
    if (EXPECTED(type)) {
        RETURN_INTERNED_STR(type);
    } else {
        RETURN_STRING("unknown type");
    }
}

因此,它将创建字符串type,并通过调用函数zend_zval_get_type的结果来填充它,即:

ZEND_API zend_string *zend_zval_get_type(const zval *arg) /* {{{ */
{
    switch (Z_TYPE_P(arg)) {
        case IS_NULL:
            return ZSTR_KNOWN(ZEND_STR_NULL);
        case IS_FALSE:
        case IS_TRUE:
            return ZSTR_KNOWN(ZEND_STR_BOOLEAN);
        case IS_LONG:
            return ZSTR_KNOWN(ZEND_STR_INTEGER);
        case IS_DOUBLE:
            return ZSTR_KNOWN(ZEND_STR_DOUBLE);
        case IS_STRING:
            return ZSTR_KNOWN(ZEND_STR_STRING);
        case IS_ARRAY:
            return ZSTR_KNOWN(ZEND_STR_ARRAY);
        case IS_OBJECT:
            return ZSTR_KNOWN(ZEND_STR_OBJECT);
        case IS_RESOURCE:
            if (zend_rsrc_list_get_rsrc_type(Z_RES_P(arg))) {
                return ZSTR_KNOWN(ZEND_STR_RESOURCE);
            } else {
                return ZSTR_KNOWN(ZEND_STR_CLOSED_RESOURCE);
            }
        default:
            return NULL;
    }
}

让我们与is_string进行比较,例如:

PHP_FUNCTION(is_string)
{
    php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_STRING);
}

转到php_is_type

static inline void php_is_type(INTERNAL_FUNCTION_PARAMETERS, int type)
{
    zval *arg;

    ZEND_PARSE_PARAMETERS_START(1, 1)
        Z_PARAM_ZVAL(arg)
    ZEND_PARSE_PARAMETERS_END();

    if (Z_TYPE_P(arg) == type) {
        if (type == IS_RESOURCE) {
            const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg));
            if (!type_name) {
                RETURN_FALSE;
            }
        }
        RETURN_TRUE;
    } else {
        RETURN_FALSE;
    }
}

因此,这些方法的核心逻辑是完全相同的– PHP使用Z_TYPE_P来检测变量的类型。

但是在gettype的情况下,它还会为结果创建附加字符串,并用常量字符串填充它,而不是在is_*函数的情况下仅返回布尔TRUE或FALSE。 因此,肯定is_*函数会更快:)