(Isso é mais longo do que eu pretendia; por favor, tenha paciência.)
A maioria das linguagens é composta de algo chamado de "sintaxe": a linguagem é composta de várias palavras-chave bem definidas e a gama completa de expressões que você pode construir nessa linguagem é construída a partir dessa sintaxe.
Por exemplo, digamos que você tenha uma "linguagem" aritmética simples de quatro funções que aceita apenas inteiros de um dígito como entrada e ignora completamente a ordem das operações (eu disse que era uma linguagem simples). Essa linguagem pode ser definida pela sintaxe:
// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /
A partir dessas três regras, você pode construir qualquer número de expressões aritméticas de entrada de um dígito. Você pode então escrever um analisador para esta sintaxe que quebra qualquer entrada válida para seus tipos de componentes ( $expression
, $number
ou $operator
) e lida com o resultado. Por exemplo, a expressão 3 + 4 * 5
pode ser dividida da seguinte forma:
// 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
Agora temos uma sintaxe totalmente analisada, em nossa linguagem definida, para a expressão original. Assim que tivermos isso, podemos seguir e escrever um analisador para encontrar os resultados de todas as combinações de $number $operator $number
e cuspir um resultado quando tivermos apenas um $number
.
Observe que não há $expression
construções restantes na versão final analisada de nossa expressão original. Isso porque $expression
sempre pode ser reduzido a uma combinação de outras coisas em nossa língua.
PHP é praticamente o mesmo: construções de linguagem são reconhecidas como equivalentes a nosso $number
ou $operator
. Eles não podem ser reduzidos a outras construções de linguagem ; em vez disso, eles são as unidades básicas a partir das quais a linguagem é construída. A principal diferença entre funções e construções de linguagem é esta: o analisador lida diretamente com construções de linguagem. Ele simplifica funções em construções de linguagem.
O motivo pelo qual as construções de linguagem podem ou não exigir parênteses e o motivo pelo qual alguns têm valores de retorno enquanto outros não dependem inteiramente dos detalhes técnicos específicos da implementação do analisador PHP. Não sou muito versado em como o analisador funciona, então não posso responder a essas questões especificamente, mas imagine por um segundo uma linguagem que começa com isto:
$expression := ($expression) | ...
Efetivamente, esta linguagem é livre para pegar qualquer expressão que encontrar e se livrar dos parênteses circundantes. PHP (e aqui estou empregando pura suposição) pode empregar algo semelhante para suas construções de linguagem: print("Hello")
pode ser reduzido para print "Hello"
antes de ser analisado, ou vice-versa (as definições de linguagem podem adicionar parênteses e também eliminá-los).
Esta é a raiz da razão pela qual você não pode redefinir construções de linguagem como echo
ou print
: eles são efetivamente codificados no analisador, enquanto as funções são mapeadas para um conjunto de construções de linguagem e o analisador permite que você altere esse mapeamento em tempo de compilação ou execução para substitua seu próprio conjunto de construções ou expressões de linguagem.
No final do dia, a diferença interna entre construções e expressões é esta: construções de linguagem são compreendidas e tratadas pelo analisador. As funções integradas, embora fornecidas pela linguagem, são mapeadas e simplificadas para um conjunto de construções de linguagem antes da análise.
Mais informações:
Edit: Lendo algumas das outras respostas, as pessoas fazem bons pontos. Entre eles:
- Uma linguagem embutida é mais rápida de chamar do que uma função. Isso é verdade, mesmo que apenas marginalmente, porque o interpretador PHP não precisa mapear essa função para seus equivalentes integrados na linguagem antes de analisar. Em uma máquina moderna, entretanto, a diferença é bastante insignificante.
- Uma linguagem embutida ignora a verificação de erros. Isso pode ou não ser verdade, dependendo da implementação interna do PHP para cada integrado. Certamente é verdade que, na maioria das vezes, as funções terão verificação de erros mais avançada e outras funcionalidades que os internos não têm.
- Construções de linguagem não podem ser usadas como retornos de chamada de função. Isso é verdade, porque uma construção não é uma função . Eles são entidades separadas. Quando você codifica um builtin, não está codificando uma função que recebe argumentos - a sintaxe do builtin é tratada diretamente pelo analisador e é reconhecida como um builtin, em vez de uma função. (Isso pode ser mais fácil de entender se você considerar linguagens com funções de primeira classe: efetivamente, você pode passar funções como objetos. Você não pode fazer isso com builtins.)