Qual é a diferença entre uma construção de linguagem e uma função “embutida” em PHP?

92

Eu sei que include, isset, require, print, echo, e alguns outros não são funções, mas construções de linguagem.

Algumas dessas construções de linguagem precisam de parênteses, outras não.

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

Alguns têm um valor de retorno, outros não.

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

Então, qual é a diferença interna entre uma construção de linguagem e uma função embutida?

Philippe Gerber
fonte

Respostas:

131

(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, $numberou $operator) e lida com o resultado. Por exemplo, a expressão 3 + 4 * 5pode 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 $numbere cuspir um resultado quando tivermos apenas um $number.

Observe que não há $expressionconstruções restantes na versão final analisada de nossa expressão original. Isso porque $expressionsempre 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 $numberou $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 echoou 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.)
Tim
fonte
2
Ótima resposta que é aberta o suficiente para ser aplicada a muitas linguagens, não apenas a PHP. Obrigado!
Levi Botelho
15

As construções de linguagem são fornecidas pela própria linguagem (como instruções como "if", "while", ...); daí seu nome.

Uma consequência disso é que eles são mais rápidos de serem chamados do que funções predefinidas ou definidas pelo usuário (ou então eu ouvi / li várias vezes)

Não tenho ideia de como isso é feito, mas uma coisa que eles podem fazer (por estarem integrados diretamente ao idioma) é "ignorar" algum tipo de mecanismo de tratamento de erros. Por exemplo, isset () pode ser usado com variáveis ​​não existentes sem causar qualquer aviso, aviso ou erro.

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

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

* Observe que não é o caso para as construções de todas as linguagens.

Outra diferença entre funções e construções de linguagem é que algumas delas podem ser chamadas sem parênteses, como uma palavra-chave.

Por exemplo :

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

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

Também aqui, não é o caso para todas as construções de linguagem.

Suponho que não haja absolutamente nenhuma maneira de "desabilitar" uma construção de linguagem porque ela faz parte da própria linguagem. Por outro lado, muitas funções PHP "integradas" não são realmente integradas porque são fornecidas por extensões de forma que estão sempre ativas (mas nem todas)

Outra diferença é que as construções de linguagem não podem ser usadas como "ponteiros de função" (quero dizer, retornos de chamada, por exemplo):

$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

Não tenho nenhuma outra ideia vindo à minha mente agora ... e eu não sei muito sobre a parte interna do PHP ... Então é isso agora ^^

Se você não obtiver muitas respostas aqui, talvez possa perguntar isso para os internos da lista de discussão (consulte http://www.php.net/mailing-lists.php ), onde há muitos desenvolvedores principais de PHP; são eles que provavelmente sabem sobre essas coisas ^^

(E estou realmente interessado nas outras respostas, btw ^^)

Como referência: lista de palavras-chave e construções de linguagem em PHP

Pascal MARTIN
fonte
Você pode ter uma função que aceita uma variável não definida sem gerar um aviso, tomando a variável por referência. Isso não se limita a construções de linguagem como isset ().
Tom Haigh
Oh, não pensei sobre isso :-( Obrigado!
Pascal MARTIN
4

Depois de examinar o código, descobri que o php analisa algumas das instruções em um arquivo yacc. Portanto, são casos especiais.

(veja Zend / zend_language_parser.y)

Além disso, não creio que haja outras diferenças.

término
fonte
1

Você pode substituir as funções integradas . Palavras-chave são para sempre.

Jason S
fonte
Essa não é uma função embutida. É definido na extensão APD (Advanced PHP Debugger).
Ionuț G. Stan
sobre a substituição de funções, você pode ter um saque na extensão do runkit (não é central também, é uma extensão, portanto, não responde ao OP, mas apenas a esta resposta); é realmente poderoso e mais recente do que o APD (e acredito que ouvi há algum tempo que algumas pessoas ainda estavam trabalhando nele, mesmo que não seja mostrado em pecl.php.net)
Pascal MARTIN