Heim  >  Artikel  >  Backend-Entwicklung  >  PHP-Abschlussfunktion

PHP-Abschlussfunktion

巴扎黑
巴扎黑Original
2016-11-11 13:41:151517Durchsuche

Anonyme Funktionen tauchten relativ früh in Programmiersprachen auf. Später wurden viele Programmiersprachen mit dieser Funktion ausgestattet.

Derzeit sind Javascript, C# und PHP weit verbreitet. 5.3 hat erst begonnen, anonyme Funktionen wirklich zu unterstützen, und der neue Standard von C, C 0x, hat ebenfalls damit begonnen, 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 wir von anonymen Funktionen sprechen, müssen wir erwähnen, dass „Closure“ eine Funktion ist, die auf freie Variablen verweist, auch wenn diese Funktion vorhanden ist Es verlässt die Umgebung, in der es erstellt wurde. Daher kann man sich einen Abschluss auch als eine Einheit vorstellen, 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 „anonyme“ Funktionen zu erstellen.
Wie bereits erwähnt, wurden anonyme Funktionen nur in PHP5.3 offiziell unterstützt. An dieser Stelle haben aufmerksame Leser möglicherweise Meinungen, da es eine Funktion gibt Anonyme Funktionen generieren: Funktion create_function, diese Funktion finden Sie in PHP4.1 und PHP5 im Handbuch. Diese Funktion kann normalerweise auch als anonyme Callback-Funktion verwendet werden, beispielsweise wie folgt:

[php] Klarkopie anzeigen

$array = array(1, 2, 3, 4);
array_walk($array, create_function('$value', 'echo $value') ) ;

Dieser Code gibt die Werte im Array nur der Reihe nach aus, kann aber natürlich auch mehr. 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 die folgende aufrufen:
[php] view plaincopy

function a() {
echo 'function a'; 🎜>Wir können diese Methode auch bei der Implementierung von Rückruffunktionen verwenden, zum Beispiel:

[php] view plaincopy

function do_something($callback) {
/ / doing
# ...

// done $callback();
}

Auf diese Weise kann die Ausführung der Funktion do_something() dann abgeschlossen werden Rufen Sie die durch $callback angegebene Funktion auf. 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] view plaincopy
$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. Dies ist etwas schwierig Wie stellt PHP dies sicher? Scheint Lambda_1 ein sehr häufiger Funktionsname zu sein? Wir wissen, wie man den entsprechenden Funktionsnamen findet, aber was ist, wenn wir nach create_function eine Funktion namens lambda_1 definieren? Diese Implementierung ist möglicherweise nicht die beste Methode Das Definieren einer Funktion namens lambda_1 wird das erwähnte Problem nicht verursachen. 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] view plaincopy
$func = create_function('', 'echo "Hallo";');

$my_func_name = 'lambda_1';
debug_zval_dump($ func); // string(9) „lambda_1“ refcount(2)
debug_zval_dump($my_func_name); // string(8) „lambda_1“ refcount(2)

Sehen Sie, sie sind die Längen Unterschiedliche Längen bedeuten, dass es sich nicht um dieselbe Funktion handelt. Schauen wir uns also einfach an, was die Funktion „create_function“ tut. Siehe die Implementierung: $PHP_SRC/Zend/zend_builtin_functions.c

[php] view plaincopy
#define LAMBDA_TEMP_FUNCNAME "__lambda_func"

ZEND_FUNCTION(create_function)
{
/ / ... Speichern Sie irrelevanten Code
function_name = (char *) emalloc(sizeof("0lambda_") MAX_LENGTH_OF_LONG
function_name[0] = 'Dies ist in Javascript sehr häufig, aber in PHP ist das Kopieren der Eigenschaften eines Objekts nicht möglich. Eine solche Verwendung führt dazu, dass die Klasse nach den in der Klasse definierten Methoden sucht Name ist Dies wird durch das Klassenmodell von PHP bestimmt. Nachfolgende Versionen können solche Aufrufe ermöglichen, was die flexible Implementierung einiger Funktionen erleichtert. 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üssen
PHP verwendet Abschlüsse (Closure), um anonyme Funktionen zu implementieren. Die leistungsstärkste Funktion anonymer Funktionen sind einige der dynamischen Funktionen und Abschlusseffekte, die anonyme Funktionen bieten Um bei der Definition Variablen außerhalb des Gültigkeitsbereichs zu verwenden, müssen Sie die folgende Syntax verwenden:

[php] view plaincopy
$name = 'TIPI Team'
$func = function() use($name) {
echo „Hallo, $name“; Die Anweisung sieht ziemlich umständlich aus, insbesondere im Vergleich zu Javascript, aber dies sollte nach umfassender Überlegung die von PHP-Core verwendete Syntax sein, da im Gegensatz zum Umfang von Javascript in PHP-Funktionen definierte Variablen standardmäßig lokale Variablen sind. Im Gegenteil, in Javascript. Mit Ausnahme der explizit definierten lokalen Variablen kann PHP beim Mutieren nicht feststellen, ob es sich bei der Variablen um eine lokale Variable oder eine Variable im oberen Bereich handelt. Natürlich gibt es möglicherweise eine Möglichkeit, sie zur Kompilierungszeit zu bestimmen, aber das ist nicht gut Für die Sprache werden Effizienz und Komplexität einen großen Einfluss haben.

Diese Syntax ist relativ einfach. Wenn Sie auf Variablen im oberen Bereich zugreifen müssen, müssen Sie sie an dieser Stelle auch einfach und leicht lesen Verwenden Sie use, um einen ähnlichen Effekt wie die globale Anweisung zu erzielen.

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] view plaincopy
function getCounter() {
$i = 0;
return function() use($i) { // Wenn Sie eine Referenz verwenden, um Variablen zu übergeben hier: use(&$i)
echo $i;
}
$counter = getCounter();
$counter(); >$ counter(); // 1

unterscheidet sich von Javascript. Die beiden Funktionsaufrufe hier erhöhen die Variable $i standardmäßig durch Kopieren Wenn es geändert werden muss, muss der Wert der Variablen der oberen Ebene als Referenz übergeben werden. Der obige Code gibt also nicht 1, 2, sondern 1, 1 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] view plaincopy
$i=100;
$counter = function() use($i) {
debug_zval_dump($i);

$ counter();

Verwenden Sie VLD, um zu sehen, welche Art von Opcode durch diese Codierung kompiliert wird
[php] view plaincopy
$ php -dvld.active=1 close .php

vars: !0 = $i, !1 = $counter
# * op --------------------------- --------- ------
0 > ASSIGN                                                                 '2 Zuweisung! 1, ~ 1
3 init_fcall_by_name! 1
4 do_fcall_by_name 0
5> return 1

Funktionsname: {CloShure}
Anzahl der Ops: 5
kompilierte Variablen:  !0 = $i  
line     # *  op                           fetch          ext  return  Operanden   
------------- -------------------------------------------------- -
3 0> fetch_r static $ 0 'i'
1 zuweisen! 0, $ 0
4 2 send_var! 0
3 do_fcall 1 'debug_zval_dump'
5 4> return null

上面根据情况去掉了一些无关的输出, 从上到下, 第1开始将100赋值给!0也就是变量$i, 随后执行ZEND_DECLARE_LAMBDA_FUNCTION, 那我们去相关的opcode执行函数中看看这里是怎么执行的,这个opcode的处理函数位于$PHP_SRC/Zend/zend_vm_execute.h中: 

[php] view plaincopy 
static 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 Abschluss nicht gefunden");  
    }  
   
zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array TSRMLS_CC);

ZEND_VM_NEXT_OPCODE(); schauen wir uns dann weiter an, was die Funktion zend_create_closure() gefunden hat in $PHP_SRC/Zend/zend_closures.c funktioniert das.

[Php] View Plaincopy
Zend_api Void Zend_create_closure (Zval *Res, Zend_function *Func TSRMLS_DC) URE;

Object_init_ex (res, zend_ce_closure) ;

Schließung = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC);

close->func = *func;
if (closure->func.type == ZEND_USER_FUNCTION ) { // Wenn es sich um einen Benutzer handelt -definierte anonyme Funktion
if (closure->func.op_array.static_variables) {
HashTable *static_variables = Schließung->func.op_array.static_variables;

Beantragen Sie den Hash-Tabellenbereich zum Speichern statischer Daten Variablen für die Funktion. ALLOC_HASHTABLE(closure->func.op_array.static_variables);
, ZVAL_PTR_DTOR, 0 );

// Schleife durch die aktuelle statische Variablenliste und verwende die Methode zval_copy_static_var, um sie zu verarbeiten.
>func.op_array.static_variables);
}  
(*closure->func.op_array.refcount) ;
}

close->func.common.scope = NULL ;
}

Wie in den obigen Codekommentaren Schauen Sie sich jedoch weiterhin die Implementierung der Funktion zval_copy_static_var() an:

[php] view plaincopy
static 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 nur Wertoperationen für statische Variablen aus, die durch use-Anweisungen eingegeben wurden, andernfalls wirken sich die statischen Variablen im anonymen Funktionskörper auch auf Variablen außerhalb des Gültigkeitsbereichs
aus, wenn (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 es keine solche Variable im aktuellen gibt Scope
if (zend_hash_quick_find(EG(active_symbol_ table), key->arKey, key->nKeyLength , key->h, (void **) &p) == FAILURE) {                                                                                                 Die Variablenseite bearbeitet die Variable nach dem anonyme Funktion ist definiert. key->arKey, key->nKeyLength, key- >h, &tmp, sizeof(zval*), (void**)&p);// Wenn es sich nicht um eine Referenz handelt, bedeutet dies, dass die Variable nicht vorhanden ist. } else {
// Wenn diese Variable vorhanden ist, referenzieren oder kopieren Sie die Variable, je nachdem, ob es sich um eine Referenz handelt.
if (is_ref) {
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
         } else if (Z_ISREF_PP(p )) {
SEPARATE_ZVAL(p);
} }
}
if (zend_hash_quick_add(target, key->arKey, key-> nKeyLength, key->h, p, sizeof (zval*), NULL) == SUCCESS) {
Z_ADDREF_PP(p);
}
return ZEND_HASH_APPLY_KEEP;
}

Diese Funktion wird als Rückruffunktion an zend_hash_apply_with_ar übergeben. Jedes Mal, wenn der Wert in der Hash-Tabelle gelesen wird, wird er von dieser Funktion verarbeitet und von dieser Funktion zugewiesen Die durch alle use-Anweisungen definierten Variablenwerte werden den statischen Variablen dieser anonymen Funktion zugewiesen, sodass die anonyme Funktion auf die use-Variablen zugreifen kann.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn