PHP7語言結構——函數

內置函數

PHP提供了大量的內置函數,方便程序員直接使用,常見的內置函數包括數學函數、字符串函數、時間和日期函數等。

內置(內部)函數是PHP安裝後就可以直接使用的,還有一些需要在安裝時和特定的PHP擴展模塊一起編譯,才能使用。如,要使用image函數中的imagecreatetruecolor()函數,需要在編譯PHP的時候加上GD的支持。或者,要使用mysql_connect()函數,需要在編譯的時候加上MySQL支持。調用phpinfo()或者get_loaded_extensions()可以得知PHP加載了哪些擴展庫。同時還應該注意,很多擴展庫默認就是有效的。

要正確使用好內置函數,需要學會並認真閱讀函數原型,確認一個函數接收什麼樣的參數,返回什麼,以及函數是否直接作用於傳遞的參數。

閱讀函數原型

函數定義告訴我們函數返回什麼類型的值,接收什麼參數,以strlen()定義爲例:

strlen

(PHP4, PHP 5)
strlen -- 獲取字符串長度

說明
int strlen( string str )

返回給定的字符串 string 的長度。

對函數定義的解釋

組成部分 說明
strlen 函數名稱
(PHP4, PHP5) strlen()在PHP4和PHP5的所有版本中都存在。
int 該函數返回的值的類型,這裏爲整型iteger。
(string str) 第一個參數,在該函數中名爲string,且類型爲string。

函數定義的一般形式:

返回類型	函數名	(參數類型 參數名)
returned type function name (parameter type parameter name)

自定義函數

函數定義語法:

function function_name(param1, param2, ...){
	statement;
}

其中function_name是函數名,param1、param2是參數,statement是函數的具體內容。

函數名和PHP鐘的其他標識符命名規則相同。有效的函數名字以字母或下劃線打頭,後面跟字母,數字或下劃線。可以用正則表達式:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*表示。

PHP保留所有以__開頭的符號作爲魔術符號。建議用戶不要在PHP中創建以__打頭的符號,除非是要使用有文檔記載的魔術函數功能。

函數無需在調用之前被定義,除非函數是有條件被定義時,必須在調用函數之前定義:

$makefoo = true;

/* 不能在此處調用foo()函數,因爲它還不存在,但是可以調用bar()函數。*/
bar();

if($makefoo) {
	function foo() {
		echo "我不存在,除非程序執行到這裏.\n";
	}
}

/* 現在可以安全調用foo()函數了,因爲$makefoo值爲真*/
if ($makefoo) foo();

function bar() {
	echo "我從程序開始運行就存在.\n";
}
?>

還有一種情況下的函數是有條件定義的:

<?php
function foo() {
	function bar() {
		echo "我不存在,知道函數foo()被調用.\n";
	}
}
/* 現在還不能調用bar()函數,因爲它還不存在 */
foo();
/* 現在可以調用bar()函數了,因爲foo()函數的執行是的bar()函數變爲已定義的函數 */
bar();
?>

PHP 中的所有函數和類都具有全局作用域,可以定義在一個函數之內而在之外調用,反之亦然。

PHP 不支持函數重載,也不可能取消定義或者重定義已聲明的函數。

函數名是大小寫無關的,不過在調用函數的時候,使用其在定義時相同的形式是個好習慣。

函數可以遞歸調用:

<?php
function recursion($a) {
	if ($a < 20) {
		echo "$a\n";
		recursion($a + 1);
	}
}
?>

所謂遞歸,就是一個函數不斷的調用自身。但是要避免遞歸函數/方法調用超過100-200層,因爲可能會使堆棧崩潰從而使當前腳本終止。無限遞歸可視爲編程錯誤。

函數的參數

通過參數列表可以傳遞信息到函數,即以逗號作爲分隔符的表達式列表。參數是從左向右求值的。

PHP 支持按值傳遞參數(默認),通過引用傳遞參數以及默認參數。也支持可變長度參數列表。

  1. 按值傳遞

    <?php
    function sum($a, $b){	// 聲明自定義函數
    	return $a + $b;		// 函數返回值
    }
    $a = 1;					// 聲明全局變量
    $b = 3;
    echo sum($a, $b);		// 通過變量傳遞參數
    echo sum(12, 13);		// 直接傳遞參數值
    ?>
    
  2. 引用傳遞
    向函數傳遞參數引用,其實就是向函數傳遞變量引用。參數引用一定是變量引用,靜態數值沒有引用一說。
    默認情況下,函數參數通過值傳遞(因而即使在函數內部改變參數的值,它並不會改變函數外部的值)。如果希望允許函數修改它的參數值,必須通過引用傳遞參數。
    如果想要函數的一個參數總是通過引用傳遞,可以在函數定義中該參數的前面加上符號 &:

    <?php
    // 聲明自定義函數,參數前多了&,表示按引用傳遞
    function add_some_extra(&$string) {
    	// 改變形參的值,實參的值也會發生改變
    	$string .= 'and something extra.';
    }
    $str = 'This is a string, ';
    add_some_txtra($str);
    echo $str;		// 輸出'This is a string, and something extra.'
    ?>
    
  3. 默認參數
    函數可以定義 C++ 風格的標量參數默認值,如下所示:

    <?php
    function makecoffee($type="cappuccino") {
    	return "Making a cup of $type.\n";
    }
    echo makecoffee();		// 未指定參數$type,使用默認值
    echo makecoffee(null);  // $type值爲null
    echo makecoffee("espresso");  // $type值爲espresso
    ?>
    

    PHP還允許使用數組array和特殊類型NULL作爲默認參數:

    function makeconffee($types=array("cappuccino"), $coffeeMaker = NULL) {
    	$device = is_null($coffeeMake)?"hands":$coffeeMaker;
    	return "Making a cup of ".join(",", $types)." with $device.\n";
    }
    echo makecoffee();
    echo makecoffee(array("cappuccino", "lavazza"), "teepot");
    ?>
    

    默認值必須是常量表達式,不能是變量、類成員,或者函數調用等。
    當使用默認參數時,任何默認參數必須放在任何非默認參數的右側;否則,函數將不會按照預期的情況工作。

自PHP5起,傳遞引用的參數也可以有默認值。

類型聲明

在PHP5中,類型聲明也被稱爲類型提示。
類型聲明允許函數在調用時要求參數爲特定類型。如果給出的值類型不對,那麼將會產生一個錯誤:在PHP5中,這將是一個可恢復的致命錯誤,而在PHP7中將會拋出一個TypeError異常。
爲了制定一個類型聲明,類型應該加在參數名前,這個聲明可以通過將參數的默認值設爲NULL來實現允許傳遞NULL。

<?php
class C {}
class D extends C {}
// this doesn't extend C
class E {}

function f(C $c) {				// 指定接收參數的類型爲C
	echo get_class($c)."\n";
}

f(new C);
f(new D);
f(new E);	// 參數類型不是C,將出現錯誤
?>

以上代碼將得到如下輸出:

C D
Fatal error: Uncaught TypeError: Argument 1 passed to f() must be an instance of C, instance of E given, called in D:\phpstudy_pro\WWW\test.php on line 13 and defined in D:\phpstudy_pro\WWW\test.php:7 Stack trace: #0 D:\phpstudy_pro\WWW\test.php(13): f(Object(E)) #1 {main} thrown in D:\phpstudy_pro\WWW\test.php on line 7

嚴格類型

默認情況下,如果能做到的話,PHP將會強迫錯誤類型的值轉爲函數期望的標量類型。例如,一個函數的一個參數期望是string,但傳入的是integer,最終函數得到的將會是一個string類型的值。

可以基於每一個文件開啓嚴格模式。在嚴格模式中,只有一個與類型聲明完全相符的變量纔會被接受,否則將會拋出一個TypeError。唯一的一個例外是可以將integer傳給一個期望float的函數。

使用declare語句和strict_types聲明來啓用嚴格模式:
declare(strict_types=1);
啓用嚴格模式同時也會影響返回值類型聲明.

嚴格類型適用於在啓用嚴格模式的文件內的函數調用,而不是在那個文件內聲明的函數。一個沒有啓用嚴格模式的文件內調用了一個在啓用嚴格模式的文件中定義的函數,那麼將會遵循調用者的偏好(弱類型),而這個值將會被轉換。

嚴格類型僅用於標量類型聲明,也正是因爲如此,這需要PHP7.0.0 或更新版本,因爲標量類型聲明也是在那個版本中添加的。

可變數量的參數列表

PHP 在用戶自定義函數中支持可變數量的參數列表。在PHP5.6及以上的版本中,由...語法實現;在PHP5.5及更早版本中,使用函數func_num_args()func_get_arg(),和func_get_args()

<?php
function sum(...$numbers) {
    $acc = 0;
    foreach ($numbers as $n) {
        $acc += $n;
    }
    return $acc;
}

echo sum(1, 2, 3, 4);
?> 

返回值

值通過使用可選的返回語句返回。可以返回包括數組和對象的任意類型。返回語句會立即中止函數的運行,並且將控制權交回調用該函數的代碼行。

如果省略了 return,則返回值爲 NULL。

<?php
function square($num)
{
    return $num * $num;
}
echo square(4);   // 輸出 '16'.
?>

函數不能返回多個值,但可以通過返回一個數組來得到類似的效果。

<?php
function small_numbers()
{
    return array (0, 1, 2);
}
list ($zero, $one, $two) = small_numbers();
?>

從函數返回一個引用,必須在函數聲明和指派返回值給一個變量時都使用引用運算符 &:

<?php
function &returns_reference()
{
    return $someref;
}

$newref =& returns_reference();
?> 

對於不需要引用的函數,可以做取消操作。取消引用使用unset()函數來完成,目的是斷開變量名和變量內容之間的綁定,此時並沒有銷燬變量內容。

返回值類型聲明

PHP7增加了對返回值類型聲明的支持。同類型聲明一樣, 返回值類型聲明將指定該函數返回值的類型。同樣,返回值類型聲明也與有效類型中可用的參數類型聲明一致。

當覆蓋一個父類方法時,子類方法的返回值類型聲明必須與父類一致。如果父類方法沒有定義返回類型,那麼子類方法可以定義任意的返回值類型聲明。

在默認的弱模式中,如果返回值與返回值的類型不一致,則會被強制轉換爲返回值聲明的類型。在強模式中,返回值的類型必須正確,否則將會拋出一個TypeError異常.

可變函數

PHP支持可變函數的概念。這意味着如果一個變量名後有圓括號,PHP將尋找與變量的值同名的函數,並且嘗試執行它。可變函數可以用來實現包括回調函數,函數表在內的一些用途。

可變函數不能用於例如echo,print,unset(),isset(),empty(),include,require 以及類似的語言結構。需要使用自己的包裝函數來將這些結構用作可變函數。

<?php
function foo() {
	echo "進入foo()函數<br />\n";
}

function bar($arg='') {
	echo "進入bar()函數;傳遞的參數值爲'$arg'.<br />\n";
}

// 使用echo的包裝函數
function echoit($string){
	echo $string;
}

$func = 'foo';
$func();		// 調用foo()函數
$func = 'bar';
$func('test');		// 調用bar()函數
$func = 'echoit';
$func('test');		//調用echoit()函數
?> 

也可以用可變函數的語法來調用一個對象的方法。

<?php
class Foo {
	function Variable() {
		$name = 'Bar';
		$this->$name();		// 調用類的Bar()方法
	}
	
	function Bar() {
		echo "This is Bar";
	}
}
$foo = new Foo();
$funcname = "Variable";
$foo->$funcname();		// 調用$foo->Variable()
?>

匿名函數

匿名函數(Anonymous functions),也叫閉包函數(closures),允許臨時創建一個沒有指定名稱的函數。最經常用作回調函數(callback)參數的值。

匿名函數目前是通過 Closure 類來實現的。

<?php
echo preg_replace_callback('~-([a-z])~',function($match){
	return strtoupper($match[1]);
}, 'hello-world');	//輸出helloWorld
?>

閉包函數也可以作爲變量的值來使用。PHP 會自動把此種表達式轉換成內置類 Closure 的對象實例。把一個 closure 對象賦值給一個變量的方式與普通變量賦值的語法是一樣的,最後也要加上分號:

<?php
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?> 

閉包可以從父作用域中繼承變量。任何此類變量都應該用 use 語言結構傳遞進去。 PHP7.1起,不能傳入此類變量: superglobals、 $this 或者和參數重名。
密碼,,,,,,

<?php
$message = 'hello';

// 沒有 "use"
$example = function () {
    var_dump($message);		
};
echo $example();			// 此次將拋出Undefined variable異常

// 繼承 $message
$example = function () use ($message) {
    var_dump($message);
};
echo $example();		// 輸出string(5) "hello"

// 繼承變量的值是從定義函數時開始的,而不是從調用開始
$message = 'world';
echo $example();		// 輸出string(5) "hello"

// 重置變量$message的值
$message = 'hello';

// 繼承變量引用
$example = function () use (&$message) {
    var_dump($message);
};
echo $example();		// 輸出string(5) "hello"

// 父作用域中更改的值反映在函數調用中
$message = 'world';
echo $example();		// 輸出string(5) "world"

// 閉包還可以接受常規參數
$example = function ($arg) use ($message) {
    var_dump($arg . ' ' . $message);
};
$example("hello");		// 輸出string(11) "hello world"
?>  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章