PHP中语言结构和“内置”函数有什么区别?

93

我知道includeissetrequireprintecho和其他一些语言结构不是函数。

其中有些语言结构需要括号,而另一些则不需要。

require 'file.php';
isset($x);

有些函数有返回值,而其他函数没有。

print 'foo'; //1
echo  'foo'; //no return value

那么语言结构和内置函数之间的内在区别是什么?

4个回答

132
(这比我预期的要长,请耐心等待。)
大多数编程语言由所谓的“语法”构成:语言由几个明确定义的关键字组成,你可以在该语言中构建的完整表达式范围是由该语法构建的。
例如,假设你有一个简单的四则运算“语言”,只接受单个数字作为输入,并完全忽略运算顺序(我告诉过你这是一种简单的语言)。该语言可以由以下语法定义:
// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /

从这三条规则中,您可以构建任意数量的一位数输入算术表达式。然后,您可以编写一个解析器来分解任何有效输入为其组成类型($expression、$number或$operator),并处理结果。例如,表达式3 + 4 * 5可以分解如下:
// Parentheses used for ease of explanation; they have no true syntactical meaning
$expression = 3 + 4 * 5
            = $expression $operator (4 * 5) // Expand into $exp $op $exp
            = $number $operator $expression // Rewrite: $exp -> $num
            = $number $operator $expression $operator $expression // Expand again
            = $number $operator $number $operator $number // Rewrite again

现在我们使用定义的语言完全解析了原始表达式的语法。一旦我们有了这个,我们就可以遍历所有$number $operator $number组合的结果并输出一个结果,当我们只剩下一个$number时。

注意,在我们原始表达式的最终解析版本中,没有任何$expression结构。那是因为$expression总是可以被简化成我们语言中的其他组合。

PHP也是如此:语言结构被认为是我们的$number$operator等价物。它们不能被简化为其他语言结构,而是构建语言的基本单元。函数和语言结构之间的关键区别在于:解析器直接处理语言结构。它将函数简化为语言结构。

语言结构是否需要括号以及为什么有些具有返回值而其他一些则没有,取决于PHP解析器实现的具体技术细节。我对解析器的工作方式不是很熟悉,所以无法具体回答这些问题,但请想象一下一个从这里开始的语言:

$expression := ($expression) | ...

有效地说,这种语言可以自由地接受任何表达式并摆脱周围的括号。PHP(我在这里只是猜测)可能会使用类似的东西来构建它的语言结构:在解析之前,print("Hello") 可能会被缩减为 print "Hello",或者反过来(语言定义也可以添加括号,也可以去掉它们)。
这就是为什么你不能重新定义像 echoprint 这样的语言结构的根本原因:它们实际上是硬编码到解析器中的,而函数则映射到一组语言结构,并且解析器允许您在编译时或运行时更改该映射以替换您自己的语言结构或表达式。
归根结底,结构和表达式之间的内部区别在于:语言结构由解析器理解和处理。内置函数虽然由语言提供,但在解析之前会被映射和简化为一组语言结构。
更多信息:
  • Backus-Naur form,用于定义形式语言的语法(yacc 使用此格式)

编辑:阅读其他答案时,有些人提出了很好的观点,其中包括:

  • 语言内置函数比函数调用更快。这是真的,即使只是稍微快一点,因为PHP解释器在解析之前不需要将该函数映射到其语言内置等效项。但在现代计算机上,差异相当小。
  • 语言内置函数绕过错误检查。这取决于每个内置函数的PHP内部实现,可能是真的,也可能不是。但通常情况下,函数会具有比内置函数更高级的错误检查和其他功能。
  • 语言结构不能用作函数回调。这是真的,因为结构不是函数。它们是独立的实体。当您编写内置函数时,您不是编写一个带参数的函数 - 内置函数的语法直接由解析器处理,并被识别为内置函数,而不是函数。(如果您考虑具有一流函数的语言,这可能更容易理解:实际上,您可以将函数传递为对象。您无法使用内置函数做到这一点。)

2
非常好的答案,开放性足以适用于许多编程语言,而不仅仅是 PHP。谢谢! - Levi Botelho

16

语言结构是由语言本身提供的(比如像“if”、“while”这样的指令);因此它们被称为语言结构。

这种结构的一个结果是,它们的调用速度比预定义或用户定义的函数更快(或者我已经听说/读过好几次了)

我不知道具体怎么做,但它们可以做一些事情(由于直接集成到语言中),例如“绕过”某种错误处理机制。例如,isset()可以用于不存在的变量而不会导致任何通知、警告或错误。

function test($param) {}
if (test($a)) {
    // Notice: Undefined variable: a
}

if (isset($b)) {
    // No notice
}

*注意,并非所有语言的构造都是如此。

函数和语言构造之间的另一个区别是,一些语言构造可以像关键字一样无需使用括号进行调用。

例如:

echo 'test'; // language construct => OK

function my_function($param) {}
my_function 'test'; // function => Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING

同样的,在许多语言构造中并非如此。

我想,绝对没有办法“禁用”语言构造,因为它们是语言本身的一部分。另一方面,许多“内置”的PHP函数实际上不是内置的,因为它们是由扩展程序提供的,因此它们始终处于活动状态(但不是所有的函数)

另一个区别是语言构造不能用作“函数指针”(我的意思是回调函数,例如):

$a = array(10, 20);

function test($param) {echo $param . '<br />';}
array_map('test', $a);  // OK (function)

array_map('echo', $a);  // Warning: array_map() expects parameter 1 to be a valid callback, function 'echo' not found or invalid function name

目前我脑海中没有其他想法... 而且我对PHP的内部机制不是很了解... 所以就先这样吧 ^^

如果你在这里得不到太多回答,也许你可以向内部邮件列表询问(请参见http://www.php.net/mailing-lists.php),那里有许多PHP核心开发人员;他们可能是了解这些问题的人^^

(顺便说一句,我真的很期待其他的答案^^)

参考文献:PHP中的关键字和语言结构列表


你可以通过引用变量来接受一个未设置的变量,而不会生成通知的函数。这不仅限于像isset()这样的语言结构。 - Tom Haigh
所以,用户定义的函数不是语言结构...但函数本身的语言特性会成为结构吗?我是否错误地认为“结构”是语言内置的构建块,这些构建块可能(或可能不)在其他语言中可用。例如,大多数语言都将具有条件表达式结构,但并非所有语言都可能具有类似Python的海象运算符(又称赋值表达式)的结构。 - mrwonderfulness

4

在深入阅读代码后,我发现php解析了yacc文件中的一些语句。因此它们是特殊情况。

(请参见Zend / zend_language_parser.y)

除此之外,我认为没有其他区别。


1

这不是内置函数。它定义在APD(高级PHP调试器)扩展中。 - Ionuț G. Stan
关于覆盖函数,你可以看一下runkit扩展(它也不是核心,而是一个扩展,因此不是回答OP,而是回答这个答案);它非常强大,比APD更新(我相信我听说有些人仍在开发它,即使它没有显示在pecl.php.net上)。 - Pascal MARTIN

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接