Skip to content

Commit 4ff60bb

Browse files
committed
Implement ZEND_ARRAY_KEY_EXISTS opcode to speed up array_key_exists()
1 parent 8872357 commit 4ff60bb

File tree

9 files changed

+1289
-534
lines changed

9 files changed

+1289
-534
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ PHP NEWS
106106
logging. (Philip Prindeville)
107107
. Fixed bug #63217 (Constant numeric strings become integers when used as
108108
ArrayAccess offset). (Rudi Theunissen, Dmitry)
109+
. Implement ZEND_ARRAY_KEY_EXISTS opcode to speed up array_key_exists().
110+
(Majkl578)
109111

110112
- DOM:
111113
. Fixed bug #76285 (DOMDocument::formatOutput attribute sometimes ignored).

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ Core:
179179
may be converted in the future.
180180
. Trailing commas in function and method calls are now allowed.
181181
(RFC: https://wiki.php.net/rfc/trailing-comma-function-calls)
182+
. The array_key_exists() function is now optimized into a specialized
183+
ZEND_ARRAY_KEY_EXISTS opcode.
182184

183185
BCMath:
184186
. bcscale() can now also be used as getter to retrieve the current scale in use.

Zend/zend_compile.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3862,6 +3862,22 @@ int zend_compile_func_get_args(znode *result, zend_ast_list *args) /* {{{ */
38623862
}
38633863
/* }}} */
38643864

3865+
int zend_compile_func_array_key_exists(znode *result, zend_ast_list *args) /* {{{ */
3866+
{
3867+
if (args->children != 2 || args->child[0]->kind == ZEND_AST_UNPACK) {
3868+
return FAILURE;
3869+
}
3870+
3871+
znode subject, needle;
3872+
3873+
zend_compile_expr(&needle, args->child[0]);
3874+
zend_compile_expr(&subject, args->child[1]);
3875+
3876+
zend_emit_op_tmp(result, ZEND_ARRAY_KEY_EXISTS, &needle, &subject);
3877+
return SUCCESS;
3878+
}
3879+
/* }}} */
3880+
38653881
int zend_compile_func_array_slice(znode *result, zend_ast_list *args) /* {{{ */
38663882
{
38673883
if (CG(active_op_array)->function_name
@@ -3969,6 +3985,8 @@ int zend_try_compile_special_func(znode *result, zend_string *lcname, zend_ast_l
39693985
return zend_compile_func_get_args(result, args);
39703986
} else if (zend_string_equals_literal(lcname, "array_slice")) {
39713987
return zend_compile_func_array_slice(result, args);
3988+
} else if (zend_string_equals_literal(lcname, "array_key_exists")) {
3989+
return zend_compile_func_array_key_exists(result, args);
39723990
} else {
39733991
return FAILURE;
39743992
}

Zend/zend_vm_def.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6405,6 +6405,69 @@ ZEND_VM_C_LABEL(isset_no_object):
64056405
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
64066406
}
64076407

6408+
ZEND_VM_HANDLER(199, ZEND_ARRAY_KEY_EXISTS, CV|TMPVAR|CONST, CV|TMPVAR|CONST)
6409+
{
6410+
USE_OPLINE
6411+
6412+
zend_free_op free_op1, free_op2;
6413+
zval *key, *subject;
6414+
HashTable* ht;
6415+
int result;
6416+
6417+
SAVE_OPLINE();
6418+
6419+
key = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R);
6420+
subject = GET_OP2_ZVAL_PTR_UNDEF(BP_VAR_R);
6421+
6422+
ZEND_VM_C_LABEL(try_again):
6423+
6424+
if (UNEXPECTED(Z_ISREF_P(subject)) || UNEXPECTED(Z_ISREF_P(key))) {
6425+
if (Z_ISREF_P(subject)) {
6426+
subject = Z_REFVAL_P(subject);
6427+
}
6428+
if (Z_ISREF_P(key)) {
6429+
key = Z_REFVAL_P(key);
6430+
}
6431+
ZEND_VM_C_GOTO(try_again);
6432+
}
6433+
6434+
if (EXPECTED(Z_TYPE_P(subject) == IS_ARRAY)) {
6435+
ht = Z_ARRVAL_P(subject);
6436+
} else if (UNEXPECTED(Z_TYPE_P(subject) == IS_OBJECT)) {
6437+
ht = Z_OBJPROP_P(subject);
6438+
} else {
6439+
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(key) == IS_UNDEF)) {
6440+
key = GET_OP1_UNDEF_CV(key, BP_VAR_R);
6441+
}
6442+
if (OP2_TYPE == IS_CV && UNEXPECTED(Z_TYPE_INFO_P(subject) == IS_UNDEF)) {
6443+
subject = GET_OP2_UNDEF_CV(subject, BP_VAR_R);
6444+
}
6445+
zend_internal_type_error(EX_USES_STRICT_TYPES(), "array_key_exists() expects parameter 2 to be array, %s given", zend_get_type_by_const(Z_TYPE_P(subject)));
6446+
FREE_OP2();
6447+
FREE_OP1();
6448+
ZEND_VM_SMART_BRANCH(result, 0);
6449+
ZVAL_NULL(EX_VAR(opline->result.var));
6450+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
6451+
}
6452+
6453+
if (EXPECTED(Z_TYPE_P(key) == IS_STRING)) {
6454+
result = zend_symtable_exists_ind(ht, Z_STR_P(key));
6455+
} else if (EXPECTED(Z_TYPE_P(key) == IS_LONG)) {
6456+
result = zend_hash_index_exists(ht, Z_LVAL_P(key));
6457+
} else if (UNEXPECTED(Z_TYPE_P(key) == IS_NULL)) {
6458+
result = zend_symtable_exists_ind(ht, ZSTR_EMPTY_ALLOC());
6459+
} else {
6460+
zend_error(E_WARNING, "array_key_exists(): The first argument should be either a string or an integer");
6461+
result = 0;
6462+
}
6463+
6464+
FREE_OP2();
6465+
FREE_OP1();
6466+
ZEND_VM_SMART_BRANCH(result, 1);
6467+
ZVAL_BOOL(EX_VAR(opline->result.var), result);
6468+
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
6469+
}
6470+
64086471
ZEND_VM_COLD_HANDLER(79, ZEND_EXIT, CONST|TMPVAR|UNUSED|CV, ANY)
64096472
{
64106473
USE_OPLINE

0 commit comments

Comments
 (0)