@@ -1860,7 +1860,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
18601860{
18611861 USE_OPLINE
18621862 zval *args;
1863- int arg_num;
1863+ uint32_t arg_num;
18641864
18651865 SAVE_OPLINE();
18661866 args = get_zval_ptr_undef(opline->op1_type, opline->op1, BP_VAR_R);
@@ -1871,34 +1871,55 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
18711871 HashTable *ht = Z_ARRVAL_P(args);
18721872 zval *arg, *top;
18731873 zend_string *name;
1874+ zend_bool have_named_params = 0;
18741875
18751876 zend_vm_stack_extend_call_frame(&EX(call), arg_num - 1, zend_hash_num_elements(ht));
18761877
1878+ // TODO: Speed this up using a flag that specifies whether there are any ref parameters.
18771879 if ((opline->op1_type & (IS_VAR|IS_CV)) && Z_REFCOUNT_P(args) > 1) {
1878- uint32_t i ;
1880+ uint32_t tmp_arg_num = arg_num ;
18791881 int separate = 0;
18801882
18811883 /* check if any of arguments are going to be passed by reference */
1882- for (i = 0; i < zend_hash_num_elements(ht); i++) {
1883- if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num + i)) {
1884+ ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
1885+ if (UNEXPECTED(name)) {
1886+ void *cache_slot[2] = {NULL, NULL};
1887+ tmp_arg_num = zend_get_arg_offset_by_name(
1888+ EX(call)->func, name, cache_slot) + 1;
1889+ }
1890+ if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, tmp_arg_num)) {
18841891 separate = 1;
18851892 break;
18861893 }
1887- }
1894+ tmp_arg_num++;
1895+ } ZEND_HASH_FOREACH_END();
18881896 if (separate) {
18891897 SEPARATE_ARRAY(args);
18901898 ht = Z_ARRVAL_P(args);
18911899 }
18921900 }
18931901
18941902 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
1895- if (name) {
1896- zend_throw_error(NULL, "Cannot unpack array with string keys");
1897- FREE_OP(opline->op1_type, opline->op1.var);
1898- HANDLE_EXCEPTION();
1903+ if (UNEXPECTED(name)) {
1904+ void *cache_slot[2] = {NULL, NULL};
1905+ have_named_params = 1;
1906+ top = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
1907+ if (UNEXPECTED(!top)) {
1908+ FREE_OP(opline->op1_type, opline->op1.var);
1909+ HANDLE_EXCEPTION();
1910+ }
1911+ } else {
1912+ if (have_named_params) {
1913+ zend_throw_error(NULL,
1914+ "Cannot use positional argument after named argument during unpacking");
1915+ FREE_OP(opline->op1_type, opline->op1.var);
1916+ HANDLE_EXCEPTION();
1917+ }
1918+
1919+ top = ZEND_CALL_ARG(EX(call), arg_num);
1920+ ZEND_CALL_NUM_ARGS(EX(call))++;
18991921 }
19001922
1901- top = ZEND_CALL_ARG(EX(call), arg_num);
19021923 if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
19031924 if (Z_ISREF_P(arg)) {
19041925 Z_ADDREF_P(arg);
@@ -1915,7 +1936,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
19151936 ZVAL_COPY_DEREF(top, arg);
19161937 }
19171938
1918- ZEND_CALL_NUM_ARGS(EX(call))++;
19191939 arg_num++;
19201940 } ZEND_HASH_FOREACH_END();
19211941
@@ -1961,6 +1981,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_
19611981 break;
19621982 }
19631983
1984+ // TODO: Support named params.
19641985 if (UNEXPECTED(Z_TYPE(key) != IS_LONG)) {
19651986 zend_throw_error(NULL,
19661987 (Z_TYPE(key) == IS_STRING) ?
0 commit comments