Skip to content

Commit d103104

Browse files
committed
Implement first basic implementation of protocols
1 parent d99a76c commit d103104

File tree

7 files changed

+71
-3
lines changed

7 files changed

+71
-3
lines changed

Zend/zend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,9 @@ typedef int (*zend_write_func_t)(const char *str, uint str_length);
589589
#define IS_CONSTANT_ARRAY 9
590590
#define IS_CALLABLE 10
591591

592+
/* Ugly hack to support protocol parsing */
593+
#define IS_PROTOCOL 11
594+
592595
/* Ugly hack to support constants as static array indices */
593596
#define IS_CONSTANT_TYPE_MASK 0x00f
594597
#define IS_CONSTANT_UNQUALIFIED 0x010

Zend/zend_compile.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,7 +1911,12 @@ void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, con
19111911
}
19121912
}
19131913
} else {
1914-
cur_arg_info->type_hint = IS_OBJECT;
1914+
if (class_type->u.constant.type == IS_PROTOCOL) {
1915+
cur_arg_info->type_hint = IS_PROTOCOL;
1916+
class_type->u.constant.type = IS_STRING;
1917+
} else {
1918+
cur_arg_info->type_hint = IS_OBJECT;
1919+
}
19151920
if (ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(Z_STRVAL(class_type->u.constant), Z_STRLEN(class_type->u.constant))) {
19161921
zend_resolve_class_name(class_type, opline->extended_value, 1 TSRMLS_CC);
19171922
}
@@ -3113,7 +3118,7 @@ static void do_inherit_method(zend_function *function) /* {{{ */
31133118
}
31143119
/* }}} */
31153120

3116-
static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
3121+
ZEND_API zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
31173122
{
31183123
zend_uint i;
31193124

Zend/zend_compile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ ZEND_API zend_bool zend_is_compiling(TSRMLS_D);
694694
ZEND_API char *zend_make_compiled_string_description(const char *name TSRMLS_DC);
695695
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers TSRMLS_DC);
696696
int zend_get_class_fetch_type(const char *class_name, uint class_name_len);
697+
ZEND_API zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC);
697698

698699
typedef zend_bool (*zend_auto_global_callback)(const char *name, uint name_len TSRMLS_DC);
699700
typedef struct _zend_auto_global {

Zend/zend_execute.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,11 +630,16 @@ static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zva
630630
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
631631
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "none", "" TSRMLS_CC);
632632
}
633-
if (Z_TYPE_P(arg) == IS_OBJECT) {
633+
if (cur_arg_info->type_hint == IS_OBJECT && Z_TYPE_P(arg) == IS_OBJECT) {
634634
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
635635
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {
636636
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);
637637
}
638+
} else if (cur_arg_info->type_hint == IS_PROTOCOL && Z_TYPE_P(arg) == IS_OBJECT) {
639+
ce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info->class_name_len, (fetch_type | ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC);
640+
if (!ce || !protocol_check_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {
641+
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, "look like ", (ce ? ce->name : cur_arg_info->class_name), "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);
642+
}
638643
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
639644
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
640645
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC);

Zend/zend_language_parser.y

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ optional_class_type:
538538
/* empty */ { $$.op_type = IS_UNUSED; }
539539
| T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; }
540540
| T_CALLABLE { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_CALLABLE; }
541+
| '<' fully_qualified_class_name '>' { $$ = $2; Z_TYPE($$.u.constant) = IS_PROTOCOL; }
541542
| fully_qualified_class_name { $$ = $1; }
542543
;
543544

Zend/zend_operators.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include "zend_strtod.h"
3131
#include "zend_exceptions.h"
3232
#include "zend_closures.h"
33+
#include "zend_compile.h"
34+
#include "zend_hash.h"
3335

3436
#if ZEND_USE_TOLOWER_L
3537
#include <locale.h>
@@ -1800,6 +1802,56 @@ ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSR
18001802
}
18011803
/* }}} */
18021804

1805+
static int protocol_check_function_implementation(void *function_entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
1806+
1807+
ZEND_API zend_bool protocol_check_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC) /* {{{ */
1808+
{
1809+
zend_bool result = 1;
1810+
1811+
zend_hash_apply_with_arguments(&(ce->function_table), protocol_check_function_implementation, 2, instance_ce, &result);
1812+
1813+
return result;
1814+
}
1815+
/* }}} */
1816+
1817+
static int protocol_check_function_implementation(void *function_entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
1818+
{
1819+
zend_function *child;
1820+
zend_bool *result;
1821+
zend_class_entry *ce;
1822+
zend_uint child_flags;
1823+
zend_uint protocol_flags = ((zend_function*) function_entry)->common.fn_flags;
1824+
1825+
TSRMLS_FETCH();
1826+
ce = va_arg(args, zend_class_entry*);
1827+
result = va_arg(args, zend_bool*);
1828+
1829+
if (*result == 0) {
1830+
return ZEND_HASH_APPLY_STOP;
1831+
}
1832+
1833+
if (zend_hash_quick_find(&(ce->function_table), hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child) == FAILURE) {
1834+
*result = 0;
1835+
return ZEND_HASH_APPLY_STOP;
1836+
}
1837+
child_flags = child->common.fn_flags;
1838+
if ((child_flags & ZEND_ACC_STATIC) != (protocol_flags & ZEND_ACC_STATIC)) {
1839+
*result = 0;
1840+
return ZEND_HASH_APPLY_STOP;
1841+
}
1842+
if ((child_flags & ZEND_ACC_ABSTRACT)) {
1843+
/* This shouldn't be possible, as it requires an instance, bail! */
1844+
*result = 0;
1845+
return ZEND_HASH_APPLY_STOP;
1846+
}
1847+
if (zend_do_perform_implementation_check(child, function_entry TSRMLS_DC) == 0) {
1848+
*result = 0;
1849+
return ZEND_HASH_APPLY_STOP;
1850+
}
1851+
return ZEND_HASH_APPLY_KEEP;
1852+
}
1853+
/* }}} */
1854+
18031855
ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC) /* {{{ */
18041856
{
18051857
zend_uint i;

Zend/zend_operators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSR
6868

6969
ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC);
7070
ZEND_API zend_bool instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce TSRMLS_DC);
71+
ZEND_API zend_bool protocol_check_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC);
7172
END_EXTERN_C()
7273

7374
#if ZEND_DVAL_TO_LVAL_CAST_OK

0 commit comments

Comments
 (0)