PHP的方法参数类型约束

接口参数

在PHP5之后,PHP正式引入了方法参数类型约束。也就是如果指定了方法参数的类型,那么传不同类型的参数将会导致错误。在PHP手册中,方法的类型约束仅限于类、接口、数组或者callable回调函数。如果指定了默认值为NULL,那么我们也可以传递NULL作为参数。

class A{}
function testA(A $a){
    var_dump($a);
}

testA(new A());
// testA(1); 
// Fatal error: Uncaught TypeError: Argument 1 passed to testA() must be an instance of A, int given,

在这个例子中,我们定义了参数类型为A类,所以当我们传递一个标量类型时,直接就会返回错误信息。

function testB(int $a){
    var_dump($a);
}
testB(1);
testB('52aadfdf'); // 字符串强转为int了
// testB('a');
// Fatal error: Uncaught TypeError: Argument 1 passed to testB() must be of the type int, string given

function testC(string $a){
    var_dump($a);
}
testC('测试');
testC(1);  // 数字会强转为字符串
// testC(new A()); 
// Fatal error: Uncaught TypeError: Argument 1 passed to testC() must be of the type string

在手册中明确说明了标量类型是不能使用类型约束的。但其实是可以使用的,不过如果都是标量类型则会进行相互的强制转换,并不能起到很好的约束作用。比如上例中int和string类型进行了相互强制转换。指定了非标量类型,则会报错。此处是本文的重点,小伙伴们可要划个线了哦。其实说白了,如果我们想指定参数的类型为固定的标量类型的话,在参数中指定并不是一个好的选择,最好还是在方法中进行再次的类型判断。而且如果参数中进行了强转,也会导致方法内部的判断产生偏差。

最后我们再看一看接口和匿名方法的类型约束。匿名参数类型在Laravel等框架中非常常见。

// 接口类型
interface D{}
class childD implements D{}
function testD(D $d){
    var_dump($d);
}
testD(new childD());

// 回调匿名函数类型
function testE(Callable $e, string $data){
    $e($data);
}
testE(function($data){
    var_dump($data);
}, '回调函数');

引用参数

$a = 1;
function test(&$arg){
    $arg++;
}
test($a);
echo $a; // 2

为参数加上&标识,就表明这个参数是引用传递的参数。如果没有加这个标识,则所有的基本类型参数都会以值的方式进行传递。

默认参数

function testArgsC($a, $b = 2){
    echo $a+$b;
}

testArgsC(1); // 3

类型声明

function testAssignA(int $a = 0)
{
    echo $a;
}

testAssignA(1);
testAssignA("a"); // error

如果参数的类型不对,直接就会报错。在PHP7以前,只支持类、数组和匿名方法的类型声明。在PHP7之后,支持所有的普通类型,但是这里要注意的是,只支持普通类型的固定写法。

  • Class/interface name
  • self
  • array
  • callable
  • bool
  • float
  • int
  • string

固定写法是什么意思呢?

function testAssignB(integer $a = 0) // error
{
    echo $a;
}

也就是说,int只能写int,不能使用integer,bool也不能使用boolean。只能是上面列出的类型关键字。

Tips一个小技巧,如果声明了参数类型,是不能传递NULL值的,比如:

function testAssignC(string $a = '')
{
    if ($a) {
        echo__FUNCTION__ . ':' . $a;
    }
}

testAssignC(NULL); // TypeError

这时有两种方式可以解决,一是指定默认值=NULL,二是使用?操作符:

function testAssignD(string $a = NULL)
{
    if ($a == NULL) {
        echo'null';
    }
}

testAssignD(NULL); // null


function testAssignE(?string $a)
{
    if ($a == NULL) {
        echo'null';
    }
}
testAssignE(NULL); // null

可变数量参数

php中的方法可以接收可变数量的参数,比如:

function testMultiArgsA($a)
{
    var_dump(func_get_arg(2));
    var_dump(func_get_args());
    var_dump(func_num_args());
    echo $a;
}

testMultiArgsA(1, 2, 3, 4);

我们只定义了一个参数$a,但是传进去了四个参数,这时我们可以使用三个方法来获取所有的参数:

  • func_get_arg(int $arg_num),获取参数列表中的某个指定位置的参数
  • func_get_args(),获取参数列表
  • func_num_args(),获取参数数量

此外,php还提供了...操作符,用于将可变长度的参数定义到一个参数变量中,如:

function testMultiArgsB($a, ...$b)
{
    var_dump(func_get_arg(2));
    var_dump(func_get_args());
    var_dump(func_num_args());
    echo $a;
    var_dump($b); // 除$a以外的
}

testMultiArgsB(1, 2, 3, 4);

和参数默认值一样,有多个参数的情况下,...$b也不要放在前面,这样后面的参数并不会有值,所有的参数都会在$b中。不过PHP默认已经帮我们解决了这个问题,如果...参数后面还有参数的话,会直接报错。

利用这个操作符,我们还可以很方便的解包一些数组或可迭代的对象给方法参数,例如:

function testMultiArgsC($a, $b){
    echo $a, $b;
}

testMultiArgsC(...[1, 2]);

是不是很有意思,那么我们利用这个特性来合并一个数组会是什么效果呢?

$array1 = [[1],[2],[3]];
$array2 = [4];
$array3 = [[5],[6],[7]];

$result = array_merge(...$array1); // Legal, of course: $result == [1,2,3];
print_r($result);
$result = array_merge($array2, ...$array1); // $result == [4,1,2,3]
print_r($result);
$result = array_merge(...$array1, $array2); // Fatal error: Cannot use positional argument after argument unpacking.
$result = array_merge(...$array1, ...$array3); // Legal! $result == [1,2,3,5,6,7]
print_r($result);

和方法声明参数时一样,在外部使用...操作符给方法传递参数时,也不能在...后面再有其他参数,所以array_merge(...$array1, $array2)的操作会报错。

posted @ 2021-04-12 15:18  caibaotimes  阅读(1476)  评论(0编辑  收藏  举报