由於PHP是一種解釋型語言,所以函數編寫和調用很是方便。總結一下魔術函數、嵌套函數、匿名函數、函數調用方式和函數參數使用等。
魔術函數
魔術函數是PHP中內置的語言特性,當程序執行到某種情況時,如果定義了這些魔術函數(PHP手冊中稱之為“Overloading”),則PHP會調用他們,同時也會傳入相應的參數,可以認為是PHP執行過程中的鉤子函數。常見的魔術函數有__construct, __destruct , __call, __get, __set, __isset, __unset __sleep, __wakeup, __toString, __clone 以及__autoload 。它們 可以用來自動加載包含文件,實現延遲執行(類似於.Net中的屬性訪問器)、垃圾回收、對象clone等操作,舉個__autoload的例子,其他魔術函數參見 Magic Method。
__autoload($class_name):用來自動加載包含文件,省得include,require,對性能有一定影響,但一般情況下可忽略。
下面是一個簡單的例子
function __autoload($class_name)
{
require_once $class_name . '.class.PHP';
}
把它放入common.PHP等全局包含文件中,當新建一個對象時,比如 $obj=new Class_A,如果PHP無法找到Class_A,則會把"Class_A" 作為參數$class_name,執行 __autoload函數。這樣就可以達到自動包含頭文件的目的了。
另外附贈幾個常量和函數:
__FILE__ :魔術常量,獲取當前源代碼文件的路徑(含文件名)
__CLASS__:魔術常量,獲取當前類的類名(區分大小寫的)。
array get_included_files ( void ):內置函數,返回通過include(), include_once(), require() 或require_once()包含的文件列表,但不包括通過PHP.ini配置文件中所設置的auto_prepend_file項。另外get_required_files()只是get_included_files ( void )的別名。
string dirname ( string path ):返回路徑中的目錄部分。
匿名函數
在PHP5.3以前使用匿名函數,可以通過create_function()來創建匿名函數,
$func=create_function('$a,$b', '
if ($a == $b)
{
return 0;
}
return ($a< $b) ? -1 : 1;
');
在PHP5.3中,可以直接使用lamda表達式
$func=function($a,$b)
{
if ($a == $b)
{
return 0;
}
return ($a< $b) ? -1 : 1;
}
然後可以作為參數傳入其他函數,如:usort($arr,$func); 也可以直接調用,$func(3,4);
在PHP5.3中使用閉包
function foo($arg1)
{
$var=3;
$inner=function($innerArg) use($arg1,$var)
{
return $innerArg+$arg1/$var;
};
echo $inner(5);
}
閉包被當成一個內置類,但是沒有Javascript那麼靈活,這個類不能有屬性。$inner->a=5;是非法的。
嵌套函數
嵌套函數可以在父函數體裡面定義函數,如:
function outer()
{
$out_var=1;
function inner()
{
var_dump($out_var);//輸出NULL,無法訪問到$out_var,聲明global也不行,$out_var不是全局變量
echo "call inner\n";
}
//echo "call outer\n";
inner();//如果不調用inner(),則在訪問outer()時也不會被調用
}
outer();
inner();//雖然,函數是全局的,必須先調用outer(),否則inner()調用會報錯(函數未定義)
條件函數
$debug=false;
if($debug===true)
{
function foo()
{
echo "foo";
}
}
foo();
相當於條件編譯了,從這裡我們可以猜測到,為什麼上文中outer()調用之前,inner()是無法調用的。因為inner在outer()調用之前還未被“編譯”(只是猜測,未深入研究)。
函數動態調用
函數名可以是變量名。
function foo()
{
echo "call foo";
}
$func='foo';
$func();
此方式比較危險,建議使用白名單方式將可用的函數名寫在配置文件中,或者函數名都有統一前綴,比如$func="act_".$funcName;。另外在調用之前可用function_exists ($funcName)來檢查函數是否存在。
通過內置函數調用函數,call_user_func(callback function ,[, mixed parameter [, mixed ...]] ),對於不確定的函數名,確定的參數可以使用此函數調用,如果參數也不確定可以通過call_user_func_array( callback function, array param_arr )
另外,可以通過get_defined_functions()來獲取所有已定義的函數(注意是所有,包括內置函數)
調用類的方法則傳入一個數組作為參數,此數組第一個元素是實例或類名,第二個元素是方法名:
class myclass
{
public $age=21;
function echo_age()
{
echo $this->age;
}
static function s_echo_age()
{
echo 22;
}
function i_echo_age()
{
echo 23;
}
}
$c=new myclass;
$classname = "myclass";
//調用實例方法,如果call_user_func(array($classname , 'echo_age'));則會報錯,$this未指向任何實例
call_user_func(array($c, 'echo_age'));
echo "\n";
//調用實例方法,如果沒有引用$this變量,則不會有問題
call_user_func(array($classname, 'i_echo_age'));
echo "\n";
//調用靜態方法,傳入類名或實例均可
call_user_func(array($classname, 's_echo_age'));
echo "\n";
call_user_func(array($c, 's_echo_age'));
獲取函數參數
通過此方式可以實現非常靈活的重載,但容易使邏輯變得負責,適度使用。
func_get_arg ( int arg_num )獲取第arg_num個參數(從0開始計數)
func_num_args()獲取參數總個數
func_get_args()獲取所有參數。
用三行代碼實現Gof中的一個設計模式:
function call_it($func)
{
$args=func_get_args();
array_shift($args);//去掉第一個參數,那是函數名
call_user_func_array($func,$args);
}
function add($a,$b)
{
echo $a+$b;
}
function sqr($a)
{
echo $a*$a;
}
call_it('add',1,2);
echo "\n";
call_it('sqr',2);