Jaka jest różnica między konstrukcją językową a funkcją „wbudowaną” w PHP?

92

Wiem, że include, isset, require, print, echo, i niektóre inne funkcje nie są jednak konstrukcje językowe.

Niektóre z tych konstrukcji językowych wymagają nawiasów, inne nie.

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

Niektóre mają wartość zwracaną, inne nie.

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

Jaka jest więc wewnętrzna różnica między konstrukcją językową a funkcją wbudowaną?

Philippe Gerber
źródło

Odpowiedzi:

131

(To trwa dłużej niż zamierzałem; proszę o wyrozumiałość).

Większość języków składa się z czegoś, co nazywa się „składnią”: język składa się z kilku dobrze zdefiniowanych słów kluczowych, a pełny zakres wyrażeń, które można skonstruować w tym języku, jest budowany na podstawie tej składni.

Na przykład, powiedzmy, że masz prosty czterofunkcyjny „język” arytmetyczny, który przyjmuje tylko jednocyfrowe liczby całkowite jako dane wejściowe i całkowicie ignoruje kolejność operacji (mówiłem, że to prosty język). Język ten można zdefiniować składnią:

// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /

Z tych trzech reguł można zbudować dowolną liczbę jednocyfrowych wyrażeń arytmetycznych. Następnie można napisać parser dla tej składni, który rozkłada dowolnego ważnego wkładu w jego skład typów ( $expression, $numberlub $operator) i zajmuje się wyniku. Na przykład wyrażenie 3 + 4 * 5można podzielić w następujący sposób:

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

Teraz mamy w pełni przeanalizowaną składnię oryginalnego wyrażenia w naszym zdefiniowanym języku. Kiedy już to zrobimy, możemy napisać parser, aby znaleźć wyniki wszystkich kombinacji $number $operator $numberi wypluć wynik, gdy zostanie nam tylko jeden $number.

Zwróć uwagę, że $expressionw ostatecznej przeanalizowanej wersji naszego oryginalnego wyrażenia nie pozostały żadne konstrukcje. Dzieje się tak, ponieważ $expressionzawsze można je zredukować do kombinacji innych rzeczy w naszym języku.

PHP jest bardzo podobny: konstrukcje językowe są rozpoznawane jako odpowiedniki naszego $numberlub $operator. Nie można ich zredukować do innych konstrukcji językowych ; zamiast tego są podstawowymi jednostkami, z których zbudowany jest język. Kluczowa różnica między funkcjami a konstrukcjami językowymi jest taka: parser zajmuje się bezpośrednio konstrukcjami językowymi. Upraszcza funkcje do konstrukcji językowych.

Powód, dla którego konstrukcje językowe mogą wymagać nawiasów lub nie, oraz powód, dla którego niektóre zwracają wartości, podczas gdy inne nie zależą całkowicie od konkretnych szczegółów technicznych implementacji parsera PHP. Nie jestem zbyt dobrze zorientowany w działaniu parsera, więc nie mogę konkretnie odpowiedzieć na te pytania, ale wyobraź sobie przez chwilę język, który zaczyna się od tego:

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

W efekcie ten język może przyjmować dowolne znalezione wyrażenia i pozbywać się otaczających nawiasów. PHP (i tutaj używam czystego domysłów) może wykorzystywać coś podobnego do swoich konstrukcji językowych: print("Hello")może zostać zredukowane do poziomu print "Hello"przed przetworzeniem lub odwrotnie (definicje języka mogą dodawać nawiasy, a także się ich pozbyć).

To jest główna przyczyna, dla której nie można przedefiniować konstrukcji językowych, takich jak echolub print: są one skutecznie zakodowane na stałe w parserze, podczas gdy funkcje są mapowane na zestaw konstrukcji językowych, a parser umożliwia zmianę tego mapowania w czasie kompilacji lub działania na zastąpić własny zestaw konstrukcji językowych lub wyrażeń.

Ostatecznie wewnętrzna różnica między konstrukcjami a wyrażeniami jest taka: konstrukcje językowe są rozumiane i obsługiwane przez parser. Funkcje wbudowane, choć dostarczane przez język, są mapowane i upraszczane do zestawu konstrukcji językowych przed analizą.

Więcej informacji:

  • Formularz Backus-Naur , składnia używana do definiowania języków formalnych (yacc używa tej formy)

Edycja: czytając niektóre inne odpowiedzi, ludzie mają rację. Pomiędzy nimi:

  • Wbudowany język jest szybszy do wywołania niż funkcja. Jest to prawda, choćby marginalnie, ponieważ interpreter PHP nie musi mapować tej funkcji na jej odpowiedniki wbudowane w język przed analizą. Jednak na nowoczesnej maszynie różnica jest dość znikoma.
  • Wbudowany język omija sprawdzanie błędów. Może to być prawdą lub nie, w zależności od wewnętrznej implementacji PHP dla każdego wbudowanego. Z pewnością prawdą jest, że najczęściej funkcje będą miały bardziej zaawansowane sprawdzanie błędów i inne funkcje, których nie mają wbudowane.
  • Konstrukcje językowe nie mogą być używane jako wywołania zwrotne funkcji. To prawda, ponieważ konstrukcja nie jest funkcją . Są oddzielnymi bytami. Kiedy kodujesz wbudowane, nie kodujesz funkcji, która pobiera argumenty - składnia wbudowanego jest obsługiwana bezpośrednio przez parser i jest rozpoznawana jako funkcja wbudowana, a nie funkcja. (Może to być łatwiejsze do zrozumienia, jeśli weźmiesz pod uwagę języki z funkcjami pierwszej klasy: w rzeczywistości możesz przekazywać funkcje jako obiekty. Nie możesz tego zrobić za pomocą wbudowanych).
Tim
źródło
2
Świetna odpowiedź, która jest na tyle otwarta, że ​​można ją zastosować w wielu językach, nie tylko w PHP. Dziękuję Ci!
Levi Botelho
15

Konstrukcje językowe są dostarczane przez sam język (jak instrukcje typu „if”, „while”, ...); stąd ich nazwa.

Jedną z konsekwencji tego jest szybsze wywoływanie niż funkcje predefiniowane lub zdefiniowane przez użytkownika (lub tak słyszałem / czytałem kilka razy)

Nie mam pojęcia, jak to się robi, ale jedną rzeczą, którą mogą zrobić (ponieważ są bezpośrednio zintegrowani z językiem), jest „obejście” pewnego rodzaju mechanizmu obsługi błędów. Na przykład isset () może być używana z nieistniejącymi zmiennymi bez powodowania żadnego powiadomienia, ostrzeżenia czy błędu.

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

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

* Uwaga: nie dotyczy to konstrukcji wszystkich języków.

Inną różnicą między funkcjami a konstrukcjami językowymi jest to, że niektóre z nich można wywołać bez nawiasów, jak słowo kluczowe.

Na przykład :

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

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

Tutaj również nie dotyczy to wszystkich konstrukcji językowych.

Przypuszczam, że nie ma absolutnie żadnego sposobu na „wyłączenie” konstrukcji językowej, ponieważ jest ona częścią samego języka. Z drugiej strony, wiele „wbudowanych” funkcji PHP nie jest tak naprawdę wbudowanych, ponieważ są one dostarczane przez rozszerzenia, dzięki czemu są zawsze aktywne (ale nie wszystkie)

Inną różnicą jest to, że konstrukcje językowe nie mogą być używane jako „wskaźniki funkcji” (mam na myśli na przykład wywołania zwrotne):

$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

W tej chwili nie przychodzi mi do głowy żaden inny pomysł ... i nie wiem zbyt wiele o wewnętrznych funkcjach PHP ... Więc to wszystko teraz ^^

Jeśli nie dostaniesz tu zbyt wielu odpowiedzi, może mógłbyś zapytać o to wewnętrzną listę dyskusyjną (zobacz http://www.php.net/mailing-lists.php ), gdzie jest wielu rdzennych programistów PHP; to oni prawdopodobnie wiedzieliby o tym ^^

(I naprawdę interesują mnie inne odpowiedzi, przy okazji ^^)

Jako odniesienie: lista słów kluczowych i konstrukcji językowych w PHP

Pascal MARTIN
źródło
Możesz mieć funkcję, która akceptuje nieustawioną zmienną bez generowania powiadomienia, biorąc zmienną przez odniesienie. Nie jest to ograniczone do konstrukcji językowych, takich jak isset ().
Tom Haigh
Och, nie myślałem o tym :-( Dzięki!
Pascal MARTIN
4

Po przejrzeniu kodu odkryłem, że php analizuje niektóre instrukcje w pliku yacc. Są to więc szczególne przypadki.

(patrz Zend / zend_language_parser.y)

Poza tym nie sądzę, żeby były inne różnice.

stacja końcowa
źródło
1

Możesz przesłonić funkcje wbudowane . Słowa kluczowe są na zawsze.

Jason S.
źródło
To nie jest funkcja wbudowana. Jest zdefiniowany w rozszerzeniu APD (Advanced PHP Debugger).
Ionuț G. Stan
o nadpisywaniu funkcji, możesz mieć łup w rozszerzeniu runkit (to też nie jest rdzeń, jest to rozszerzenie, więc nie odpowiada na OP, ale tylko na tę odpowiedź); jest naprawdę potężny i nowszy niż APD (i wydaje mi się, że słyszałem jakiś czas temu, że niektórzy wciąż nad nim pracują, nawet jeśli nie jest to pokazane na pecl.php.net)
Pascal MARTIN