通常,您会听到熟悉其他语言的程序员抱怨PHP的内部函数方法。他们常常得出这样的结论:它们大多是在浪费时间,因为它们不是本地的,它们没有实现闭包,并且如果外部函数执行不止一次,就会导致错误。
实际上,只要您了解它们的工作原理,它们就会非常有用,而没有它们,您的代码确实会非常混乱。
当将它们与PHP 5.3.0中添加的匿名函数放在一起时,您所获得的东西几乎(但不完全)与其他语言所提供的相等。
但是首先让我们看一下PHP函数的基础知识。
功能是全局的
PHP函数很容易定义:
function MyFunction()
{
instructions that make up the function
}
您可以包括参数和return语句。
重要的一点是,以这种方式定义的任何函数都具有全局范围。可以从整个程序中的任何位置调用它,包括从另一个函数中甚至从类或类的实例中调用。
例如,如果我们定义函数:
function MyFunction()
{
echo("My Function");
}
然后可以在一个类中使用它:
class MyClass
{
public function MyMethod()
{
MyFunction();
}
}
以及何时调用该方法
$MyObject=new MyClass();
$MyObject->MyMethod();
调用MyFunction。
作为一个PHP程序员,这对于您来说似乎是完全正常的,但是大多数其他面向对象的语言甚至都不允许您在类之外定义函数,即所有函数都是方法,因此不会出现具有全局函数的想法。
以这种方式使用函数不是一个好主意,即使可以合理地认为它可以为您创建的对象提供“辅助”函数。通常,类的功能应始终包含在类中,并且不能由全局实体使用。
您可以说字符串函数是应该是全局的实用程序函数的示例,例如count_chars是一个全局函数,该函数返回字符串中的字符数,但这仅表明您没有足够的面向对象的指示。如果函数对字符串执行某些操作,即它是一个字符串操作,则它应该是字符串类的一部分。
要查找应该具有全局性的令人信服的函数示例,您必须查看涉及多个类型的操作(该函数应属于哪个类),或者单个函数可以处理多种类型。通常使用泛型在完全面向对象的语言中处理多种类型,但是松散类型的语言(如PHP)仅允许您忽略类型问题并编写全局函数。它可以完成工作,并且避免在语言中引入复杂的想法。
内部功能
如果在函数内定义变量,则该变量在该函数中是局部的。您还可以在另一个函数中定义一个函数,从逻辑上讲,这也应该是局部变量,但要记住所有函数都是全局的规则。
在PHP中,内部函数是全局的,因此其行为与在任何包含函数之外声明的行为相同。
这意味着内部功能是无用的。
因为它们是全局的,所以它们与以通常方式声明的函数具有相同的上下文,这又意味着它们无法支持其他语言提供的任何巧妙技巧,例如闭包。
闭包是内部函数可以访问包含它的函数的局部变量的地方-即使外部函数不再存在。由于内部函数是全局函数,因此它们无权访问声明它们的函数的局部变量。同样,由于函数是全局函数,因此它们永远不会超出范围,因此永远不会被破坏。
因此,PHP函数缺少使闭包成为可能和有用功能的两件事,而这两者都是由于函数是全局的。
需要明确的是:内部函数的行为与在其包含函数之外被声明的行为完全相同。它无法访问其包含函数的局部变量,并且诸如闭包之类的概念根本不适用。
使用期限
函数具有全局性这一事实意味着它们不会超出范围,这意味着它们永远不会被破坏。
但这并不意味着每个函数都与其所包含的程序具有相同的生存期。
您只要声明为程序文本的一部分的任何函数,就可以认为该函数在程序开始运行后就存在,并且一直存在到程序结束。但是,PHP是一种动态解释型语言,这意味着它可以使用自己的文本来执行操作,而其他语言则不能。
例如,您可以有条件地声明一个函数:
if($test) {
function MyFunction()
{
echo("My Function");
}
}
这将导致MyFunction在程序中不存在,直到执行if语句且条件为true为止。一旦发生这种情况,该功能就存在并继续存在,直到程序结束。
您可以认为这是
“该功能从PHP开始阅读以来就存在”。
这是一种完全通用的机制,适用于其他PHP代码中函数的任何声明。
请注意,由于无法在PHP中重新定义函数,因此这意味着声明该函数的任何代码只能执行一次-否则,必须确保该逻辑不会再次创建该函数。
它也适用于内部函数,这意味着在调用外部函数之前,内部函数不存在。这似乎比前面给出的条件示例奇怪得多,但实际上它是相同的原理。
例如,如果定义两个函数,则一个在另一个内:
function MyFunction()
{
echo("My Function");
function MyInnerFunction()
{
echo('MyInnerFunction ');
}
}
然后,如果仅调用MyInnerFunction(),它将失败。要使其正常工作,您首先必须调用外部函数,然后才存在内部函数。那是:
MyFunction();
MyInnerFunction();
可以,但是一旦调用MyFunction,就无法再次调用它而不会产生错误。
尝试多次创建函数的问题的解决方案是利用条件声明的思想。例如:
function MyFunction()
{
echo("My Function");
if(!function_exists("MyInnerFunction")){
function MyInnerFunction()
{
echo('MyInnerFunction ');
}
}
}
现在,这种工作方式应该显而易见。if语句仅检查函数名称是否在全局范围内。如果确实如此,则不会再次声明该函数,并且不会发生错误。
您应该始终将内部函数声明包含在if语句中-这样可以使它们更安全并且花费很少。
现在我们到达了一个微妙的地步,但事实证明它具有实际意义。
您可以从包含它的函数中调用内部函数吗?
答案是肯定的,但只有在定义好之后。例如; 你不能使用
function MyFunction()
{
echo("My Function");
MyInnerFunction();
if(!function_exists("MyInnerFunction")){
function MyInnerFunction()
{
echo('MyInnerFunction ');
}
}
}
问题是,当您尝试调用MyInnerFunction时,尚未对其进行声明。但是,如果将呼叫移至if之后,则一切正常:
function MyFunction()
{
echo("My Function");
if(!function_exists("MyInnerFunction")){
function MyInnerFunction()
{
echo('MyInnerFunction ');
}
}
MyInnerFunction();
}
这很奇怪,需要一段时间才能习惯,但这是完全合乎逻辑的。
当然,第二次调用MyFunction已经定义了内部函数,并且原则上您可以在任意位置调用内部函数-因为内部函数实际上是您希望的任何地方都是全局的!