(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ą:
$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
, $number
lub $operator
) i zajmuje się wyniku. Na przykład wyrażenie 3 + 4 * 5
można podzielić w następujący sposób:
$expression = 3 + 4 * 5
= $expression $operator (4 * 5)
= $number $operator $expression
= $number $operator $expression $operator $expression
= $number $operator $number $operator $number
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 $number
i wypluć wynik, gdy zostanie nam tylko jeden $number
.
Zwróć uwagę, że $expression
w ostatecznej przeanalizowanej wersji naszego oryginalnego wyrażenia nie pozostały żadne konstrukcje. Dzieje się tak, ponieważ $expression
zawsze można je zredukować do kombinacji innych rzeczy w naszym języku.
PHP jest bardzo podobny: konstrukcje językowe są rozpoznawane jako odpowiedniki naszego $number
lub $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 echo
lub 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:
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).