PHP方法參數的那點事兒

  • 2020 年 1 月 13 日
  • 筆記

PHP方法參數的那點事兒

在所有的程式語言中,方法或者函數,都可以傳遞一些參數進來進行業務邏輯的處理或者計算。這沒什麼可說的,但是在PHP中,方法的參數還有許多非常有意思的能力,下面我們就來說說這方面的內容。

引用參數

涉及到值傳遞和引用傳遞的問題。在正常情況下,我們使用值傳遞的時候,變數是進行了拷貝,方法內外的變數不會共享記憶體。也就是說,在方法體中修改了變數的值,方法外部的變數不會產生變化。而引用傳遞則是傳遞的變數的記憶體地值。方法內外的變數可以看做是同一個變數,比如:

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

為參數加上&標識,就表明這個參數是引用傳遞的參數。如果沒有加這個標識,則所有的基本類型參數都會以值的方式進行傳遞。為什麼要強調基本類型呢?下面我們用類當參數來測試一下:

  class A  {      public $a = 1;  }  function testA($obj)  {      $obj->a++;  }    $o = new A();  testA($o);  echo $o->a; // 2    

在這個例子中,我們並沒有使用&標識來表明參數$obj是引用類型的,但如果傳遞的參數是對象的話,那麼它默認就是進行的引用傳遞。如果想讓對象也是值傳遞呢?抱歉,在方法參數中是沒辦法實現的,只能在方法體中使用clone方式對對象參數進行克隆。

  class A  {      public $a = 1;  }  function testA($obj)  {      $o = clone $obj;      $o->a++;  }  $o = new A();  testA($o);  echo $o->a; // 1    

關於值和引用的問題,可以參考設計模式中原型模式的講解:PHP設計模式之原型模式

默認參數

參數是可以有默認值的,這個我想大家都應該很清楚了。但是在使用的時候也需要注意,那就是默認參數不要放在前面,否則很容易出錯,比如:

  function testArgsA($a = 1, $b){      echo $a+$b;  }    testArgs(); // error    function testArgsB($a = 1, $b = 2){      echo $a+$b;  }    testArgsB(); // 3    function testArgsC($a, $b = 2){      echo $a+$b;  }    testArgsC(1); // 3    

在複雜的函數或者緊急的業務開發中,很有可能一個不小心就會漏寫參數,這時候testArgsA就會返回錯誤了。當然,這種粗心類的錯誤是我們應該盡量避免的。

當指定默認值的時候,我們應該根據參數的類型進行指定,比如字元串就指定為'',數字就指定為數字類型。當不確定參數是什麼類型時,建議使用NULL做為默認參數。

  function testArgsD($a = NULL)  {      if ($a) {          echo $a;      }  }    testArgsD(1);  testArgsD('a');    

類型聲明

類型聲明是在PHP5之後添加的功能,就像java一樣,參數前面加上參數的類型,比如:

  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。只能是上面列出的類型關鍵字。

類型聲明的好處是什麼呢?其實就是Java這種靜態語言和PHP這種動態語言之間的差別。動態類型語言的好處就是變數靈活,不用指定類型,方便快速開發迭代。但問題也在於靈活,為了靈活,動態語言往往會在比較或者計算時對變數進行自動類型轉換。如果你對變數類型轉換的理解不清晰的話,很容易就會出現各種類型的BUG。同時,靜態類型的語言一般都會有編譯打包,而動態類型則是在執行時確定變數類型,所以很少會進行編譯打包,相對來說運行效率也就不如Java之類的編譯後語言了。

關於PHP的類型轉換問題,可以參考此前的文章:PHP中的強制類型轉換

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

測試程式碼:https://github.com/zhangyue0503/dev-blog/blob/master/php/201911/source/PHP%E6%96%B9%E6%B3%95%E5%8F%82%E6%95%B0%E7%9A%84%E9%82%A3%E7%82%B9%E4%BA%8B%E5%84%BF.php

參考文檔:https://www.php.net/manual/zh/functions.arguments.phphttps://www.php.net/manual/zh/functions.arguments.php#121579https://www.php.net/manual/zh/functions.arguments.php#120580