在 PHP 中进行类型检查的更快方法是什么?gettype() 或多个 is_*()

lig*_*ght 5 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
    }
}
Run Code Online (Sandbox Code Playgroud)

方法二

function doSomething2($param) {
    if (is_string($param)) {
        // do something
    }
    else if (is_int($param)) {
        // do something
    }
    else if (is_array($param)) {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 据我所知,从测试的角度来看,这两种方法在功能上是等效的,但是由于 PHP 有很多问题,我不得不问,如果我偏爱一种方法而不是另一种方法,我是否会错过什么?

  2. 从性能的角度来看,因为PHP 函数调用很昂贵,所以说一种方法比两种方法快是正确的吗?或者是gettype()比单个is_*()功能更昂贵的操作?

  3. 是否有任何关于此的编码习惯用法/风格指南?

更新 从我使用 PHP 7.0.4 的基准测试来看,一百万次迭代doSomething2()需要 159 毫秒,比doSomething1()315 毫秒的时间略少一半。这与传入的是字符串(第一次检查)还是数组(最后一次检查)无关。这似乎表明这gettype() 确实是一个昂贵的操作,并且比使用is_*().

任何对这可能发生的原因有更深入了解的人,都非常感谢您的帮助。

Iva*_*sev 5

让我们比较一下 C 代码gettype和 的C 代码is_string函数的 C 代码。

\n\n

gettype:

\n\n
PHP_FUNCTION(gettype)\n{\n    zval *arg;\n    zend_string *type;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n        Z_PARAM_ZVAL(arg)\n    ZEND_PARSE_PARAMETERS_END();\n\n    type = zend_zval_get_type(arg);\n    if (EXPECTED(type)) {\n        RETURN_INTERNED_STR(type);\n    } else {\n        RETURN_STRING("unknown type");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

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

我们来比较一下is_string,例如:

\n\n
PHP_FUNCTION(is_string)\n{\n    php_is_type(INTERNAL_FUNCTION_PARAM_PASSTHRU, IS_STRING);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

php_is_type

\n\n
static inline void php_is_type(INTERNAL_FUNCTION_PARAMETERS, int type)\n{\n    zval *arg;\n\n    ZEND_PARSE_PARAMETERS_START(1, 1)\n        Z_PARAM_ZVAL(arg)\n    ZEND_PARSE_PARAMETERS_END();\n\n    if (Z_TYPE_P(arg) == type) {\n        if (type == IS_RESOURCE) {\n            const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(arg));\n            if (!type_name) {\n                RETURN_FALSE;\n            }\n        }\n        RETURN_TRUE;\n    } else {\n        RETURN_FALSE;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以,这些方法的核心逻辑与 PHP 使用的 \xe2\x80\x93 完全相同Z_TYPE_P所以,这些方法的核心逻辑与 PHP 用于检测变量类型的

\n\n

但在这种情况下,gettype它还会为结果创建额外的字符串,并用常量字符串填充它,而不是在is_*函数的情况下仅返回布尔值 TRUE 或 FALSE。 \n所以,绝对is_*函数肯定更快:)

\n