¿Cuál es la diferencia entre una construcción de lenguaje y una función "incorporada" en PHP?

92

Sé que include, isset, require, print, echo, y algunos otros que no son funciones, pero las construcciones del lenguaje.

Algunas de estas construcciones del lenguaje necesitan paréntesis, otras no.

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

Algunos tienen un valor de retorno, otros no.

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

Entonces, ¿cuál es la diferencia interna entre una construcción de lenguaje y una función incorporada?

Philippe Gerber
fuente

Respuestas:

131

(Esto es más largo de lo que pretendía; tenga paciencia conmigo).

La mayoría de los lenguajes se componen de algo llamado "sintaxis": el lenguaje se compone de varias palabras clave bien definidas, y la gama completa de expresiones que puede construir en ese lenguaje se construye a partir de esa sintaxis.

Por ejemplo, digamos que tiene un "lenguaje" aritmético simple de cuatro funciones que solo toma enteros de un solo dígito como entrada e ignora por completo el orden de las operaciones (le dije que era un lenguaje simple). Ese lenguaje podría definirse por la sintaxis:

// 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 de estas tres reglas, puede crear cualquier número de expresiones aritméticas de entrada de un solo dígito. A continuación, puede escribir un parser para este sintaxis que rompe cualquier entrada válida en sus tipos de componentes ( $expression, $numbero $operator) y se ocupa con el resultado. Por ejemplo, la expresión 3 + 4 * 5se puede dividir de la siguiente manera:

// 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

Ahora tenemos una sintaxis completamente analizada, en nuestro lenguaje definido, para la expresión original. Una vez que tengamos esto, podemos revisar y escribir un analizador para encontrar los resultados de todas las combinaciones de $number $operator $numbery escupir un resultado cuando solo nos $numberquede una .

Tenga en cuenta que no quedan $expressionconstrucciones en la versión final analizada de nuestra expresión original. Eso es porque $expressionsiempre se puede reducir a una combinación de otras cosas en nuestro idioma.

PHP es muy similar: las construcciones del lenguaje se reconocen como equivalentes a nuestro $numbero $operator. No se pueden reducir a otras construcciones del lenguaje ; en cambio, son las unidades base a partir de las cuales se construye el lenguaje. La diferencia clave entre funciones y construcciones del lenguaje es la siguiente: el analizador se ocupa directamente de las construcciones del lenguaje. Simplifica funciones en construcciones de lenguaje.

La razón por la que las construcciones del lenguaje pueden requerir o no paréntesis y la razón por la que algunas tienen valores de retorno mientras que otras no dependen completamente de los detalles técnicos específicos de la implementación del analizador PHP. No estoy tan bien versado en cómo funciona el analizador, por lo que no puedo abordar estas preguntas específicamente, pero imagina por un segundo un idioma que comienza con esto:

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

Efectivamente, este lenguaje es libre de tomar cualquier expresión que encuentre y deshacerse de los paréntesis que lo rodean. PHP (y aquí estoy empleando conjeturas puras) puede emplear algo similar para sus construcciones de lenguaje: print("Hello")puede reducirse a print "Hello"antes de ser analizado, o viceversa (las definiciones de lenguaje pueden agregar paréntesis y deshacerse de ellos).

Esta es la raíz de por qué no puede redefinir construcciones de lenguaje como echoo print: están efectivamente codificadas en el analizador, mientras que las funciones están asignadas a un conjunto de construcciones de lenguaje y el analizador le permite cambiar esa asignación en tiempo de compilación o ejecución a sustituya su propio conjunto de expresiones o construcciones del lenguaje.

Al final del día, la diferencia interna entre construcciones y expresiones es la siguiente: el analizador comprende y trata las construcciones del lenguaje. Las funciones integradas, aunque proporcionadas por el lenguaje, se asignan y simplifican a un conjunto de construcciones de lenguaje antes del análisis.

Más información:

  • Forma Backus-Naur , la sintaxis utilizada para definir lenguajes formales (yacc utiliza esta forma)

Editar: Al leer algunas de las otras respuestas, la gente hace buenos puntos. Entre ellos:

  • Un lenguaje incorporado es más rápido de llamar que una función. Esto es cierto, aunque solo sea marginalmente, porque el intérprete de PHP no necesita asignar esa función a sus equivalentes incorporados en el lenguaje antes de analizar. En una máquina moderna, sin embargo, la diferencia es bastante insignificante.
  • Un lenguaje incorporado evita la verificación de errores. Esto puede ser cierto o no, dependiendo de la implementación interna de PHP para cada incorporado. Ciertamente es cierto que la mayoría de las veces, las funciones tendrán una verificación de errores más avanzada y otras funciones que las incorporadas no tienen.
  • Las construcciones de lenguaje no se pueden usar como devoluciones de llamada de funciones. Esto es cierto, porque una construcción no es una función . Son entidades separadas. Cuando codifica una función incorporada, no está codificando una función que acepta argumentos: el analizador maneja directamente la sintaxis de la función incorporada y se reconoce como una función incorporada, en lugar de una función. (Esto puede ser más fácil de entender si considera lenguajes con funciones de primera clase: efectivamente, puede pasar funciones como objetos. No puede hacer eso con incorporaciones).
Tim
fuente
2
Excelente respuesta que es lo suficientemente abierta como para aplicarse a muchos lenguajes, no solo a PHP. ¡Gracias!
Levi Botelho
15

Las construcciones del lenguaje son proporcionadas por el lenguaje mismo (como instrucciones como "si", "mientras", ...); de ahí su nombre.

Una consecuencia de eso es que son más rápidos de invocar que las funciones predefinidas o definidas por el usuario (o eso lo he escuchado / leído varias veces)

No tengo idea de cómo se hace, pero una cosa que pueden hacer (debido a que están integrados directamente en el idioma) es "omitir" algún tipo de mecanismo de manejo de errores. Por ejemplo, isset () se puede usar con variables inexistentes sin causar ningún aviso, advertencia o error.

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

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

* Tenga en cuenta que no es el caso de las construcciones de todos los lenguajes.

Otra diferencia entre las funciones y las construcciones del lenguaje es que algunas de ellas se pueden llamar sin paréntesis, como una palabra clave.

Por ejemplo :

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

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

Aquí tampoco es el caso de todas las construcciones del lenguaje.

Supongo que no hay absolutamente ninguna forma de "deshabilitar" una construcción del lenguaje porque es parte del lenguaje mismo. Por otro lado, muchas funciones PHP "integradas" no están realmente integradas porque son proporcionadas por extensiones de manera que siempre están activas (pero no todas).

Otra diferencia es que las construcciones del lenguaje no se pueden usar como "punteros de función" (quiero decir, devoluciones de llamada, por ejemplo):

$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

No se me viene a la cabeza ninguna otra idea en este momento ... y no sé mucho sobre los componentes internos de PHP ... Así que eso será todo ahora ^^

Si no obtiene muchas respuestas aquí, tal vez podría preguntarle esto a los internos de la lista de correo (consulte http://www.php.net/mailing-lists.php ), donde hay muchos desarrolladores principales de PHP; ellos son los que probablemente sabrían sobre esas cosas ^^

(Y estoy realmente interesado por las otras respuestas, por cierto ^^)

Como referencia: lista de palabras clave y construcciones de lenguaje en PHP

Pascal MARTIN
fuente
Puede tener una función que acepte una variable no establecida sin generar un aviso tomando la variable por referencia. Esto no se limita a construcciones de lenguaje como isset ().
Tom Haigh
Oh, no pensé en eso :-( ¡Gracias!
Pascal MARTIN
4

Después de leer el código, descubrí que php analiza algunas de las declaraciones en un archivo yacc. Entonces son casos especiales.

(ver Zend / zend_language_parser.y)

Aparte de eso, no creo que haya otras diferencias.

término
fuente
1

Puede anular las funciones integradas . Las palabras clave son para siempre.

Jason S
fuente
Esa no es una función incorporada. Se define en la extensión APD (Advanced PHP Debugger).
Ionuț G. Stan
sobre las funciones anuladas, podría tener un botín en la extensión runkit (tampoco es central, es una extensión, por lo que no responde al OP, sino solo a esta respuesta); es realmente poderoso y más reciente que APD (y creo que escuché hace algún tiempo que algunas personas todavía estaban trabajando en él, incluso si no se muestra en pecl.php.net)
Pascal MARTIN