[iefreer] hat einen Artikel nachgedruckt, der die PHP-Closure-Syntax ausführlich erklärt, und wird später auch einen Artikel darüber veröffentlichen, wie man diese neuen Syntaxen geschickt anwendet.
Anonyme Funktionen tauchten relativ früh in Programmiersprachen auf, zuerst in der Lisp-Sprache, und dann begannen viele Programmiersprachen, diese Funktion zu haben
Es ist derzeit weit verbreitetes Javascript. C# und PHP unterstützten anonyme Funktionen bis 5.3 nicht wirklich, und der neue C++-Standard C++0x begann ebenfalls, sie zu unterstützen.
Eine anonyme Funktion ist eine Art Funktion oder Unterroutine, die ohne Angabe eines Bezeichners aufgerufen werden kann. Anonyme Funktionen können bequem als Parameter an andere Funktionen übergeben werden.
Closure
Wenn es um anonyme Funktionen geht, müssen wir „Closures“ für „Lexical Closures“ nennen und beziehen uns auf freie Variablen Wenn es die Umgebung verlässt, in der es erstellt wurde, kann ein Abschluss auch als eine Einheit betrachtet werden, die aus einer Funktion und den zugehörigen Referenzen besteht. In einigen Sprachen kann es beim Definieren einer anderen Funktion innerhalb einer Funktion zu einem Abschluss kommen, wenn die innere Funktion auf eine Variable der äußeren Funktion verweist. Beim Ausführen einer externen Funktion wird ein Abschluss gebildet. Das Wort
kann leicht mit anonymen Funktionen verwechselt werden. Tatsächlich handelt es sich um zwei verschiedene Konzepte. Dies kann daran liegen, dass viele Sprachen die Bildung von Abschlüssen bei der Implementierung anonymer Funktionen ermöglichen.
Verwenden Sie create_function(), um eine „anonyme“ Funktion zu erstellen
Wie bereits erwähnt, wurden anonyme Funktionen nur in PHP5.3 offiziell unterstützt. Zu diesem Zeitpunkt haben aufmerksame Leser möglicherweise Meinungen, weil dort Eine Funktion kann eine anonyme Funktion generieren: Funktion „create_function“ Sie finden diese Funktion in PHP4.1 und PHP5 im Handbuch. Diese Funktion kann normalerweise auch als anonyme Callback-Funktion verwendet werden, beispielsweise wie folgt:
[php]
Klartext anzeigen
- >$array
- = array(1, 2, 3, 4); array_walk
- ($array, create_function('$value', 'echo $value')); Weitere Dinge zu tun. Warum handelt es sich also nicht um eine echte anonyme Funktion? Schauen wir uns zunächst den Rückgabewert dieser Funktion an. Normalerweise können wir eine Funktion wie diese aufrufen.
[php]
Klartext anzeigen
>Funktion
- a() {
- echo
- 'function a' ; 🎜>} $a
= - 'a'; 🎜>$a();
- Wir können diese Methode auch bei der Implementierung der Callback-Funktion verwenden, z Beispiel:
- [php]
Klartext anzeigen
- >Funktion
do_something(- $callback) {
// doing. 🎜>// fertig
$callback
- ();
} - Auf diese Weise kann die durch $callback angegebene Funktion aufgerufen werden, nachdem die Funktion do_something() ausgeführt wurde. Zurück zum Rückgabewert der Funktion „create_function“: Die Funktion gibt einen eindeutigen String-Funktionsnamen oder FALSE zurück, wenn ein Fehler auftritt. Diese Funktion erstellt also nur dynamisch eine Funktion, und diese Funktion hat einen Funktionsnamen, was bedeutet, dass sie nicht wirklich anonym ist. Es wird lediglich eine weltweit einzigartige Funktion erstellt. [php]
Klartext anzeigen
- $func = create_function('', 'echo „Funktion erstellt dynamisch“;');
- echo $func; // lambda_1
-
- $func(); // Funktion erstellt dynamisch
-
- $my_func = 'lambda_1' ;
- $my_func(); // Diese Funktion existiert nicht
- lambda_1(); // Diese Funktion existiert nicht
Der erste Teil des obigen Codes ist leicht zu verstehen. So wird die Funktion „create_function“ verwendet. Der spätere Aufruf über den Funktionsnamen ist jedoch etwas schwierig zu verstehen Ist global eindeutig? Das hört sich nach einem sehr gebräuchlichen Funktionsnamen an. Was wäre, wenn wir hier zuerst eine Funktion namens lambda_2 definieren? Es findet jedoch den entsprechenden Funktionsnamen. Wenn wir nach der Funktion „create_function“ eine Funktion namens „lambda_1“ definieren, ist dies wahrscheinlich nicht der beste Weg Die von mir genannten Probleme werden nicht auftreten. Was ist los? Die letzten beiden Zeilen des obigen Codes veranschaulichen dieses Problem. Tatsächlich ist keine Funktion namens lambda_1 definiert.
Mit anderen Worten, unser Lambda_1 und das von create_function zurückgegebene Lambda_1 sind nicht dasselbe! Wenn wir echo, wird Lambda_1 ausgegeben und unser lambda_1 wird von uns selbst eingegeben. Schauen wir uns das mit der Funktion debug_zval_dump an.
[php]
Klartext anzeigen
- $func = create_function('', 'echo "Hallo";'
- $my_func_name = 'lambda_1';
debug_zval_dump(- $func); 🎜>debug_zval_dump($my_func_name);
// string(8) "lambda_1" refcount(2)- Du Sehen Sie, ihre Längen sind tatsächlich unterschiedlich. Unterschiedliche Längen bedeuten, dass es sich nicht um dieselbe Funktion handelt. Natürlich existiert die Funktion, die wir aufgerufen haben, nicht. Schauen wir uns einfach an, was die Funktion create_function tut. Siehe die Implementierung: $PHP_SRC/Zend/zend_builtin_functions.c
[php]
Klartext anzeigen
- #define LAMBDA_TEMP_FUNCNAME "__lambda_func"
-
- ZEND_FUNCTION(create_function)
- {
- // ... 省去无关代码
- function_name = (char *) emalloc(sizeof("0lambda_")+MAX_LENGTH_OF_LONG);
- function_name[0] = ' '; // <--- 这里
- tun {
- function_name_length = 1 + sprintf(function_name + 1, "lambda_%d", ++EG(lambda_count));
- } while (zend_hash_add(EG(function_table), function_name, function_name_length+1, &new_function, sizeof(zend_function), NULL)= =FEHLER);
- zend_hash_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME));
- RETURN_STRINGL(function_name, function_name_length, 0);
- }
该函数在定义了一个函数之后,给函数起了个名字它将函数名的第一个字符变为了' '也就是空字符,然后在函数表中查找是否已经定义了这个函数,如果已经有了则生成新的函数名, 第一个字符为空字符的定义方式比较特殊, 因为在用户代码中无法定义出这样的函数, 也就不存在命名冲突的问题了,这也算是种取巧(knifflig)的做法, 在了解到这个特殊的函数之后, 我们其实还是可以调用到这个函数的, 只要我们在函数名前加一个空字符就可以了, chr()函数可以帮我们生成这样的字符串, 例如前面创建的函数可以通过如下的方式访问到:
[php]
Klartext anzeigen
-
- $my_func = chr(0) . "lambda_1";
- $my_func(); // Hallo
这种创建"匿名函数"的方式有些缺点:
- 函数的定义是通过字符串动态eval的, 这就无法进行基本的语法检查;
- 这类函数和普通函数没有本质区别, 无法实现闭包的效果.“得讲讲: 新引入的__invoke
魔幻方法.
__invoke魔幻方法这个魔幻方法被调用的时机是: 当一个对象当做函数调用的时候, 如果对象定义了__invoke魔幻方法则这个函数会被调用,这和C++中的操作符重载有些类似, 例如可以像下面这样使用:[php]
Klartext anzeigen- Klasse
Callme { - public
- function __invoke($phone_num) { echo
- "Hallo: $phone_num" ; }
- }
-
- $call
= - new Callme(); $call
(13810688888); - // "Hallo: 13810688888 匿名函数的实现
前面介绍了将对象作为函数调用的方法, 聪明的你可能想到在PHP实现匿名函数的方法了, PHP中的名函数就的确是通过这种方式实现的.我们先来验证一下:
[php]
Klartext anzeigen- $func = Funktion () {
- echo "Hallo, anonyme Funktion";
- }
-
- gettype($func); >echo get_class($func );
// Schließung - Es stellt sich heraus, dass die anonyme Funktion gerecht ist eine gewöhnliche Klasse. Schüler, die mit Javascript vertraut sind, sind mit der Verwendung anonymer Funktionen bestens vertraut. Da es sich bei der anonymen Funktion tatsächlich um eine Klasseninstanz handelt, ist die Syntax ähnlich wie bei Javascript Verstehen Sie, dass es kopiert werden kann. In Javascript können Sie der Eigenschaft eines Objekts eine anonyme Funktion zuweisen, zum Beispiel: [php]
Klartext anzeigen
var a = {}; a.call = Funktion- () {alert("aufgerufen");}
- a .call(); // Alarm aufgerufen
- Dies ist in Javascript sehr häufig, in PHP jedoch so Das Kopieren der Eigenschaften eines Objekts ist nicht möglich. Diese Verwendung führt dazu, dass die Klasse nach in der Klasse definierten Methoden sucht. Dies wird durch das PHP-Klassenmodell bestimmt Es wurde entschieden, dass PHP in dieser Hinsicht natürlich verbessert werden kann und nachfolgende Versionen solche Aufrufe ermöglichen könnten, was die flexible Implementierung einiger Funktionen erleichtern wird. Derzeit gibt es eine Möglichkeit, diesen Effekt zu erzielen: Verwenden Sie eine andere magische Methode, __call(). Wie Sie dies erreichen, überlasse ich den Lesern als Übung. Die Verwendung von AbschlüssenPHP verwendet Abschlüsse (Closure), um anonyme Funktionen zu implementieren, falls erforderlich Um beim Definieren einer anonymen Funktion Variablen außerhalb des Gültigkeitsbereichs zu verwenden, müssen Sie die folgende Syntax verwenden:
[php]
Klartext anzeigen
$name = - 'TIPI Team'; >function
() - use($name ) {
- echo "Hallo, $name"; }
- $func (); // Hallo TIPI-Team
- Diese Verwendungsanweisung sieht ziemlich umständlich aus , insbesondere im Vergleich zu Javascript, aber dies sollte auch PHP-Core sein. Die verwendete Syntax basiert auf umfassenden Überlegungen. Da sich der Umfang von PHP von dem von Javascript unterscheidet, sind in PHP-Funktionen definierte Variablen standardmäßig lokale Variablen, während dies in Javascript der Fall ist Ist das Gegenteil der Fall, handelt es sich um lokale Variablen. Wenn PHP mutiert, kann nicht festgestellt werden, ob es sich um eine lokale Variable oder eine Variable im oberen Bereich handelt zur Kompilierungszeit, aber dies wird einen großen Einfluss auf die Effizienz und Komplexität der Sprache haben.
- Diese Syntax ist relativ einfach. Wenn Sie auf Variablen im oberen Bereich zugreifen müssen, müssen Sie sie mit der use-Anweisung deklarieren. Apropos, das ist auch möglich Mit use erzielen Sie ähnliche Effekte wie mit der globalen Anweisung. Die anonyme Funktion kann bei jeder Ausführung auf die Variablen im oberen Bereich zugreifen. Diese Variablen speichern immer ihren eigenen Status, bevor die anonyme Funktion zerstört wird, wie im folgenden Beispiel:
- [php]
Klartext anzeigen
- Funktion getCounter() {
- $i = 0;
- return Funktion() Verwendung($i) { // Wenn Sie hier eine Referenz verwenden, um Variablen zu übergeben: use(&$i)
- echo ++$i;
$counter- = getCounter() ;
$counter- (); >(); // 1
- unterscheidet sich von Javascript, hier sind zwei. Dieser Funktionsaufruf erhöht die Variable $i nicht. Standardmäßig übergibt PHP die obere-. Wenn Sie den Wert der Variablen der oberen Ebene ändern müssen, müssen Sie sie als Referenz übergeben. Der obige Code gibt also nicht , sondern aus. Implementierung von Abschlüssen
Wie bereits erwähnt, werden anonyme Funktionen durch Abschlüsse implementiert. Jetzt beginnen wir zu sehen, wie Abschlüsse (Klassen) implementiert werden. Es gibt keinen Unterschied zwischen anonymen Funktionen und gewöhnlichen Funktionen, außer ob sie Variablennamen haben. Der Implementierungscode des Abschlusses befindet sich in $PHP_SRC/Zend/zend_closure.c. Das Problem der „Objektivierung“ anonymer Funktionen wurde durch Closure gelöst. Wie greife ich beim Erstellen der anonymen Funktion auf die Variablen zu? - Zum Beispiel der folgende Code: [php]
Klartext anzeigen
1, 2
1,1
$i
= 100; >use
(
$i
) {
debug_zval_dump(- $i );
- }; Zähler
(); - Verwenden Sie VLD, um zu sehen, welche Art von Opcode diese Codierung kompiliert hat [php]
Klartext anzeigen
- $ php -dvld.active=1 closure.php
-
- vars: !0 = $i, !1 = $counter
- # * op fetch ext return Operanden
- ----- -------------------------------------------------- --
- 0 > Zuweisen! 0, 100
- 1 Zend_declare_lambda_function '{Closeur
- 2 zuweisen! 1, ~ 1
- 3 INIT_FCALL_BY_NAME !1
- 4 DO_FCALL_BY_NAME 0
- 5 > RETURN 1
-
- Funktion Name: {Abschluss}
- Anzahl der Operationen: 5
- kompilierte Variablen: !0 = $i
- Zeile # * op fetch ext return Operanden
- ---- -------------------------------------------------- -------------------------
- 3 0 > FETCH_R statisch > 1 ASSIGN !0, $0
- 4 2 SEND_VAR !0 3 DO_FCALL 1 🎜> 5 4 > RÜCKKEHR null 无关的输出, 从上到下, 第1开始将100赋值给!0也就是变量$i, Klicken Sie auf ZEND_DECLARE_LAMBDA_FUNCTION, um den Opcode anzuzeigen位于$PHP_SRC/Zend/zend_vm_execute.h中:
- [ php]
Klartext anzeigen
- statisch int ZEND_FASTCALL ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
- {
zend_op *opline = EX(opline);
zend_function *op_array;
- if (zend_hash_quick_find(EG(function_table), Z_STRVAL(opline->op1. u.constant), Z_STRLEN(opline->op1.u.constant), Z_LVAL(opline->op2.u.constant), (void *) &op_arra
-
y) == FAILURE || .
-
op_array->type != ZEND_USER_FUNCTION) {
-
zend_error_noreturn(E_ERROR,
" Basis-Lambda-Funktion für den Abschluss nicht gefunden"- );
-
}
-
zend_create_closure(&EX_T(opline->result. u.
var- ).tmp_var, op_array TSRMLS_CC);
-
ZEND_VM_NEXT_OPCODE();
-
}
-
Diese Funktion ruft die Funktion zend_create_closure() auf, um ein Abschlussobjekt zu erstellen. Schauen wir uns also weiter an, was die Funktion zend_create_closure() in $PHP_SRC/Zend/zend_closures.c tut.
[php]
Klarkopie anzeigen
- ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC)
- {
- zend_closure *closure;
-
- object_init_ex(res, zend_ce_closure); 🎜>
Schließung = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC); -
Schließung->func = *func; - if ( Schließung-> func.type == ZEND_USER_FUNCTION) { // Wenn es sich um eine benutzerdefinierte anonyme Funktion handelt
- if (closure->func.op_array.static_variables) {
HashTable *static_variables = Schließung->func.op_array.static_variables; -
-
>
- _HASHTABLE(closure->func.op_array.static_variables); zend_hash_init(closure->func.op_array. static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
-
-
>
zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, close->func.op_array.static_variables); - }
(*closure->func.op_array.refcount)++; } close->func.common.scope = NULL; } -
- Wie in den Kommentaren oben erwähnt, schauen Sie sich weiterhin die Implementierung der Funktion zval_copy_static_var() an:
- [ php]
Klartext anzeigen
- statisch int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, zend_hash_key *key)
- {
- HashTable *target = va_arg(args, HashTable*);
- zend_bool is_ref
-
- // Führen Sie Wertoperationen nur für statische Variablen vom Typ „use“ aus, andernfalls wirken sich auch die statischen Variablen im anonymen Funktionskörper auf den Bereich aus. Externe Variablen
- if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
- is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF;
-
- 🎜>if (!EG(active_symbol_table)) {
zend_rebuild_symbol_table(TSRMLS_C); } - //Wenn nein solche Variable im aktuellen Bereich
-
if (zend_hash_quick_find(EG(active_symbol_table), key->arKey , key->nKeyLength, key->h, (void **) &p) == FAILURE) {
-
🎜>
// Wenn es sich um eine Referenzvariable handelt, erstellen Sie eine temporäre Variable, während Sie die Variable nach der Definition der anonymen Funktion bedienen 🎜>- > zend_hash_quick_add(EG (active_symbol_table), key ->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), (void**)&p); > >
-
🎜>, key-> ;arKey );
} -
} else {
- 🎜>
- }else if (Z_ISREF_PP(p)) {
- SEPARATE_ZVAL(p);
- (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p , sizeof(zval*), NULL) == SUCCESS) {
Z_ADDREF_PP(p); } - > ZEND_HASH_APPLY_KEEP;
- } >Funktion, jedes Mal, wenn der Wert in der Hash-Tabelle gelesen wird, wird er von dieser verarbeitet Funktion, und diese Funktion weist die durch alle use-Anweisungen definierten Variablenwerte den statischen Variablen dieser anonymen Funktion zu, sodass die anonyme Funktion auf die use-Variablen zugreifen kann.
Originallink:- http://www.php-internals.com/book/?p=chapt04/04-04-anonymous-function
Referenzlektüre:http://php.net/manual/zh/functions.anonymous.php
Das Obige stellt anonyme PHP-Funktionen und -Abschlüsse vor, einschließlich Aspekten des Inhalts. Ich hoffe, dass es für Freunde hilfreich ist, die sich für PHP-Tutorials interessieren. -