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)的操作會報錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM