Skip to content

类型检测函数

概述

PHP 提供了一组 is_* 类型检测函数,用于判断变量的类型。这些函数返回布尔值,广泛应用于输入验证、条件分支和类型安全的代码中。与 PHP 7.0+ 的标量类型声明配合使用,可以在运行时进行灵活的类型检查。

PHP 版本说明

  • is_* 系列函数大多自 PHP 4 起可用
  • is_iterablePHP 7.1 引入
  • is_countablePHP 7.3 引入
  • get_debug_type()PHP 8.0 引入,返回更精确的类型名

基础概念

类型检测函数分类

类别函数说明
标量类型is_int, is_float, is_string, is_bool基本数据类型检查
复合类型is_array, is_object, is_callable, is_resource复合数据类型检查
特殊类型is_null, is_numeric, is_scalar特殊类型检查
PHP 7+is_iterable, is_countable接口/特性检查
综合gettype, get_debug_type获取类型名称

语法与代码

标量类型检测

php
<?php

declare(strict_types=1);

// is_int / is_integer / is_long(三者完全相同)
var_dump(is_int(42));          // true
var_dump(is_int(-10));         // true
var_dump(is_int(3.14));        // false
var_dump(is_int('42'));        // false
var_dump(is_int(PHP_INT_MAX));  // true

// is_float / is_double / is_real(三者完全相同)
var_dump(is_float(3.14));      // true
var_dump(is_float(1.0));       // true
var_dump(is_float(42));        // false
var_dump(is_float(INF));       // true
var_dump(is_float(NAN));       // true

// is_string
var_dump(is_string('hello'));       // true
var_dump(is_string(''));            // true
var_dump(is_string(42));            // false
var_dump(is_string('42'));          // true(字符串 '42',不是整数)
var_dump(is_string('3.14'));        // true

// is_bool
var_dump(is_bool(true));       // true
var_dump(is_bool(false));      // true
var_dump(is_bool(0));          // false
var_dump(is_bool('true'));     // false
var_dump(is_bool(1));          // false

复合类型检测

php
<?php

declare(strict_types=1);

// is_array
var_dump(is_array([1, 2, 3]));       // true
var_dump(is_array(['key' => 'val']));  // true
var_dump(is_array([]));                // true(空数组也是数组)
var_dump(is_array('hello'));           // false
var_dump(is_array(new stdClass()));    // false

// is_object
var_dump(is_object(new stdClass()));  // true
var_dump(is_object((object)['a'=>1])); // true(PHP 8.0 匿名对象)
var_dump(is_object([1, 2]));          // false
var_dump(is_object(null));            // false

// is_callable
var_dump(is_callable('strlen'));              // true
var_dump(is_callable(fn() => true));         // true
var_dump(is_callable(['class', 'method'])); // 取决于方法是否存在
var_dump(is_callable('nonexistent'));        // false
var_dump(is_callable(42));                    // false
var_dump(is_callable(['ArrayObject', 'count'])); // true

// is_resource
$fp = fopen('php://memory', 'r');
var_dump(is_resource($fp));  // true
fclose($fp);
var_dump(is_resource($fp));  // false(已关闭的资源)

特殊类型检测

php
<?php

declare(strict_types=1);

// is_null
var_dump(is_null(null));       // true
var_dump(is_null(0));          // false
var_dump(is_null(''));         // false
var_dump(is_null(false));      // false
var_dump(is_null($undefined)); // true(未定义变量视为 null)

// is_numeric:检查是否为数字或数字字符串
var_dump(is_numeric(42));          // true
var_dump(is_numeric(3.14));        // true
var_dump(is_numeric('42'));        // true
var_dump(is_numeric('3.14'));      // true
var_dump(is_numeric('-100'));       // true
var_dump(is_numeric('+1.5e2'));     // true(科学计数法)
var_dump(is_numeric('0x1A'));       // true(十六进制)
var_dump(is_numeric('hello'));      // false
var_dump(is_numeric('42abc'));      // false
var_dump(is_numeric(''));           // false

// is_scalar:检查是否为标量类型(int, float, string, bool)
var_dump(is_scalar(42));         // true
var_dump(is_scalar(3.14));       // true
var_dump(is_scalar('hello'));    // true
var_dump(is_scalar(true));       // true
var_dump(is_scalar(null));       // false
var_dump(is_scalar([1, 2]));      // false
var_dump(is_scalar(new stdClass())); // false

PHP 7+ 新增类型检测

php
<?php

declare(strict_types=1);

// is_iterable(PHP 7.1+):检查是否为数组或 Traversable
var_dump(is_iterable([1, 2, 3]));           // true(数组)
var_dump(is_iterable(new ArrayIterator())); // true(Traversable)
var_dump(is_iterable(new stdClass()));     // false
var_dump(is_iterable('hello'));            // false
var_dump(is_iterable(42));                 // false

// is_countable(PHP 7.3+):检查是否可使用 count()
var_dump(is_countable([1, 2, 3]));             // true
var_dump(is_countable(new ArrayIterator()));   // true
var_dump(is_countable('hello'));               // false
var_dump(is_countable(new stdClass()));         // false(PHP 7.2 前为 true)

gettype 与 get_debug_type

php
<?php

declare(strict_types=1);

// gettype:返回通用类型名(PHP 所有版本)
echo gettype(42) . "\n";           // integer
echo gettype(3.14) . "\n";         // double
echo gettype('hello') . "\n";       // string
echo gettype(true) . "\n";          // boolean
echo gettype(null) . "\n";          // NULL
echo gettype([1, 2]) . "\n";       // array
echo gettype(new stdClass()) . "\n"; // object
echo gettype(fn() => true) . "\n";  // object

// get_debug_type(PHP 8.0+):返回更精确的类型名
echo get_debug_type(42) . "\n";              // int
echo get_debug_type(3.14) . "\n";            // float
echo get_debug_type(true) . "\n";            // bool
echo get_debug_type(null) . "\n";            // null
echo get_debug_type([1, 2]) . "\n";           // array
echo get_debug_type(new stdClass()) . "\n";   // stdClass
echo get_debug_type(fn() => true) . "\n";     // Closure
echo get_debug_type(new ArrayIterator()) . "\n"; // ArrayIterator
echo get_debug_type(InvalidArgumentException::class) . "\n"; // string

get_debug_type 优势

get_debug_type() 返回的类型名更精确:int vs integerfloat vs doubleClosure vs object。推荐在 PHP 8.0+ 中优先使用。

详细说明

is_numeric vs is_int 的区别

php
<?php

declare(strict_types=1);

$input = '42';

var_dump(is_numeric($input)); // true('42' 是数字字符串)
var_dump(is_int($input));     // false('42' 是字符串,不是整数)

// 实际使用场景
function parseInt(string $input): ?int
{
    if (!is_numeric($input)) {
        return null;
    }

    return (int) $input;
}

echo parseInt('42') . "\n";    // 42
echo parseInt('3.14') . "\n";  // 3
var_dump(parseInt('abc'));     // NULL

类型检测与严格类型

php
<?php

declare(strict_types=1);

function processNumber(int $number): int
{
    return $number * 2;
}

// 严格模式下,传入非 int 会抛出 TypeError
// processNumber('42'); // TypeError

// 运行时检查
function safeProcess(mixed $input): int
{
    if (!is_int($input) && !is_numeric($input)) {
        throw new InvalidArgumentException('Expected a number');
    }

    return (int) $input * 2;
}

echo safeProcess(42) . "\n";     // 84
echo safeProcess('42') . "\n";   // 84

is_callable 的详细用法

php
<?php

declare(strict_types=1);

// 检查各种可调用结构
var_dump(is_callable('strlen'));                    // true(函数名)
var_dump(is_callable(fn() => true));               // true(闭包)
var_dump(is_callable([ArrayObject::class, 'count'])); // true(静态方法)
var_dump(is_callable([new ArrayObject(), 'count'])); // true(实例方法)
var_dump(is_callable('nonexistent_function'));     // false

// is_callable 的第二个参数:获取可调用名称
$callableName = null;
var_dump(is_callable('strlen', false, $callableName)); // true
echo $callableName . "\n"; // strlen

// 仅检查语法(不检查函数是否存在)
var_dump(is_callable('someUnknownFunction', true)); // true(语法合法)
var_dump(is_callable('someUnknownFunction', false)); // false(函数不存在)

实战示例

输入验证器

php
<?php

declare(strict_types=1);

class Validator
{
    private array $rules = [];
    private array $errors = [];

    public function string(string $field): self
    {
        $this->rules[$field] = fn(mixed $v): bool => is_string($v);
        return $this;
    }

    public function integer(string $field): self
    {
        $this->rules[$field] = fn(mixed $v): bool => is_int($v);
        return $this;
    }

    public function numeric(string $field): self
    {
        $this->rules[$field] = fn(mixed $v): bool => is_numeric($v);
        return $this;
    }

    public function email(string $field): self
    {
        $this->rules[$field] = fn(mixed $v): bool => is_string($v) && filter_var($v, FILTER_VALIDATE_EMAIL) !== false;
        return $this;
    }

    public function validate(array $data): bool
    {
        $this->errors = [];

        foreach ($this->rules as $field => $rule) {
            $value = $data[$field] ?? null;

            if (!$rule($value)) {
                $type = get_debug_type($value);
                $this->errors[$field] = "Field '{$field}' expected valid type, got {$type}";
            }
        }

        return empty($this->errors);
    }

    public function getErrors(): array
    {
        return $this->errors;
    }
}

$validator = (new Validator())
    ->string('name')
    ->integer('age')
    ->email('email');

$data = ['name' => 'Alice', 'age' => '30', 'email' => 'not-an-email'];
$isValid = $validator->validate($data);

if (!$isValid) {
    print_r($validator->getErrors());
    // Array ( [age] => Field 'age' expected valid type, got string [email] => ... )
}

类型安全的数组处理

php
<?php

declare(strict_types=1);

function ensureIntArray(array $items): array
{
    foreach ($items as $key => $item) {
        if (!is_int($item)) {
            throw new TypeError("Item at index {$key} must be int, " . get_debug_type($item) . " given");
        }
    }

    return $items;
}

function ensureStringArray(array $items): array
{
    return array_map(function (mixed $item, int $key): string {
        if (!is_string($item) && !is_numeric($item)) {
            throw new TypeError("Item at index {$key} must be string");
        }

        return (string) $item;
    }, $items, array_keys($items));
}

// 使用示例
try {
    $ints = ensureIntArray([1, 2, 3, 'four']); // TypeError
} catch (TypeError $e) {
    echo $e->getMessage() . "\n";
}

$strings = ensureStringArray([1, 2, 'hello', 3.14]);
print_r($strings); // Array ( [0] => 1 [1] => 2 [2] => hello [3] => 3.14 )

注意事项

常见陷阱

  1. is_numeric 包含十六进制和科学计数法
php
<?php

declare(strict_types=1);

var_dump(is_numeric('0xFF'));    // true(十六进制)
var_dump(is_numeric('1e10'));    // true(科学计数法)
var_dump(is_numeric('+42'));     // true
var_dump(is_numeric('-3.14'));   // true

// 如果只需要十进制数字
function isDecimalNumber(string $input): bool
{
    return preg_match('/^-?\d+(\.\d+)?$/', $input) === 1;
}

var_dump(isDecimalNumber('42'));      // true
var_dump(isDecimalNumber('0xFF'));    // false
var_dump(isDecimalNumber('1e10'));    // false
  1. is_scalar 不包含 null
php
<?php

declare(strict_types=1);

var_dump(is_scalar(null));   // false
var_dump(is_scalar(42));     // true
var_dump(is_scalar('abc'));  // true
var_dump(is_scalar(true));   // true

// null 不被视为标量
  1. 对象类型需要 get_class
php
<?php

declare(strict_types=1);

$obj = new ArrayIterator();

// is_object 只知道它是对象,不知道具体类
var_dump(is_object($obj));       // true

// get_class 获取具体类名
echo get_class($obj) . "\n";     // ArrayIterator

// instanceof 检查继承关系
var_dump($obj instanceof ArrayIterator);  // true
var_dump($obj instanceof Traversable);   // true
var_dump($obj instanceof IteratorAggregate); // true

最佳实践

1. 优先使用类型声明而非 is_* 检查

php
<?php

declare(strict_types=1);

// 推荐:使用类型声明
function process(int $value): int
{
    return $value * 2;
}

// 不推荐:手动检查
function processManual(mixed $value): int
{
    if (!is_int($value)) {
        throw new TypeError('Expected int');
    }

    return $value * 2;
}

2. 混合类型使用 match + get_debug_type

php
<?php

declare(strict_types=1);

function describeType(mixed $value): string
{
    return match (true) {
        is_int($value) => "integer ({$value})",
        is_float($value) => "float ({$value})",
        is_string($value) => "string (length: " . strlen($value) . ")",
        is_bool($value) => "boolean (" . ($value ? 'true' : 'false') . ")",
        is_array($value) => "array (" . count($value) . " items)",
        is_object($value) => "object (" . get_class($value) . ")",
        is_null($value) => "null",
        is_resource($value) => "resource",
        default => "unknown type (" . get_debug_type($value) . ")",
    };
}

echo describeType(42) . "\n";          // integer (42)
echo describeType([1, 2, 3]) . "\n";   // array (3 items)

3. 使用 PHP 8.0+ 的 get_debug_type

php
<?php

declare(strict_types=1);

// 旧方式(gettype 返回不一致的名称)
echo gettype(fn() => true) . "\n"; // object

// 新方式(PHP 8.0+)
echo get_debug_type(fn() => true) . "\n"; // Closure

// 旧方式
echo gettype(42) . "\n"; // integer

// 新方式
echo get_debug_type(42) . "\n"; // int

参考链接