Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: php/php-src
base: master
...
head fork: ircmaxell/php-src
compare: protocol_proof_of_concept
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 12 files changed
  • 0 commit comments
  • 1 contributor
View
20 Zend/tests/protocols/basic_errors.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Protocol Basic Error
+--FILE--
+<?php
+
+class Foo {
+ public function bar($abc) {}
+}
+interface Bar {
+ public function bar();
+}
+
+function foo(<Bar> $bar) {
+ var_dump($bar);
+}
+
+foo(new Foo);
+?>
+--EXPECTF--
+Catchable fatal error: Argument 1 passed to foo() must look like Bar, instance of Foo given, called in %s/basic_errors.php on line %d and defined in %s/basic_errors.php on line %d
View
27 Zend/tests/protocols/basic_functionality.phpt
@@ -0,0 +1,27 @@
+--TEST--
+Protocol Basic Functionality
+--FILE--
+<?php
+
+class Foo implements Bar {
+ public function bar() {}
+}
+class Baz {
+ public function bar() {}
+}
+interface Bar {
+ public function bar();
+}
+
+function foo(<Bar> $bar) {
+ var_dump($bar);
+}
+
+foo(new Foo);
+foo(new Baz);
+?>
+--EXPECT--
+object(Foo)#1 (0) {
+}
+object(Baz)#1 (0) {
+}
View
23 Zend/tests/protocols/protected_private_methods_ignored.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Protocol Test That Protected And Private Methods Are Ignored
+--FILE--
+<?php
+
+class Foo {
+ public function bar($abc) {}
+}
+class Bar {
+ public function bar($abc) {}
+ protected function bar2() {}
+ private function bar3() {}
+}
+
+function foo(<Bar> $bar) {
+ var_dump($bar);
+}
+
+foo(new Foo);
+?>
+--EXPECT--
+object(Foo)#1 (0) {
+}
View
5 Zend/zend.c
@@ -593,6 +593,11 @@ static void executor_globals_dtor(zend_executor_globals *executor_globals TSRMLS
zend_hash_destroy(executor_globals->zend_constants);
free(executor_globals->zend_constants);
}
+ if (executor_globals->protocol_cache) {
+ zend_hash_destroy(executor_globals->protocol_cache);
+ free(executor_globals->protocol_cache);
+ }
+
}
/* }}} */
View
4 Zend/zend.h
@@ -481,6 +481,7 @@ struct _zend_class_entry {
int refcount;
zend_uint ce_flags;
+ HashTable *protocol_cache;
HashTable function_table;
HashTable properties_info;
zval **default_properties_table;
@@ -589,6 +590,9 @@ typedef int (*zend_write_func_t)(const char *str, uint str_length);
#define IS_CONSTANT_ARRAY 9
#define IS_CALLABLE 10
+/* Ugly hack to support protocol parsing */
+#define IS_PROTOCOL 11
+
/* Ugly hack to support constants as static array indices */
#define IS_CONSTANT_TYPE_MASK 0x00f
#define IS_CONSTANT_UNQUALIFIED 0x010
View
9 Zend/zend_compile.c
@@ -1911,7 +1911,12 @@ void zend_do_receive_arg(zend_uchar op, znode *varname, const znode *offset, con
}
}
} else {
- cur_arg_info->type_hint = IS_OBJECT;
+ if (class_type->u.constant.type == IS_PROTOCOL) {
+ cur_arg_info->type_hint = IS_PROTOCOL;
+ class_type->u.constant.type = IS_STRING;
+ } else {
+ cur_arg_info->type_hint = IS_OBJECT;
+ }
if (ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(Z_STRVAL(class_type->u.constant), Z_STRLEN(class_type->u.constant))) {
zend_resolve_class_name(class_type, opline->extended_value, 1 TSRMLS_CC);
}
@@ -3113,7 +3118,7 @@ static void do_inherit_method(zend_function *function) /* {{{ */
}
/* }}} */
-static zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
+ZEND_API zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC) /* {{{ */
{
zend_uint i;
View
1  Zend/zend_compile.h
@@ -694,6 +694,7 @@ ZEND_API zend_bool zend_is_compiling(TSRMLS_D);
ZEND_API char *zend_make_compiled_string_description(const char *name TSRMLS_DC);
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers TSRMLS_DC);
int zend_get_class_fetch_type(const char *class_name, uint class_name_len);
+ZEND_API zend_bool zend_do_perform_implementation_check(const zend_function *fe, const zend_function *proto TSRMLS_DC);
typedef zend_bool (*zend_auto_global_callback)(const char *name, uint name_len TSRMLS_DC);
typedef struct _zend_auto_global {
View
7 Zend/zend_execute.c
@@ -630,11 +630,16 @@ static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zva
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "none", "" TSRMLS_CC);
}
- if (Z_TYPE_P(arg) == IS_OBJECT) {
+ if (cur_arg_info->type_hint == IS_OBJECT && Z_TYPE_P(arg) == IS_OBJECT) {
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);
}
+ } else if (cur_arg_info->type_hint == IS_PROTOCOL && Z_TYPE_P(arg) == IS_OBJECT) {
+ 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);
+ if (!ce || !protocol_check_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {
+ 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);
+ }
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);
return zend_verify_arg_error(E_RECOVERABLE_ERROR, zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC);
View
2  Zend/zend_globals.h
@@ -187,6 +187,8 @@ struct _zend_executor_globals {
zend_op_array *active_op_array;
+ HashTable *protocol_cache; /* protocol cache hash table */
+
HashTable *function_table; /* function symbol table */
HashTable *class_table; /* class table */
HashTable *zend_constants; /* constants table */
View
1  Zend/zend_language_parser.y
@@ -538,6 +538,7 @@ optional_class_type:
/* empty */ { $$.op_type = IS_UNUSED; }
| T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; }
| T_CALLABLE { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_CALLABLE; }
+ | '<' fully_qualified_class_name '>' { $$ = $2; Z_TYPE($$.u.constant) = IS_PROTOCOL; }
| fully_qualified_class_name { $$ = $1; }
;
View
95 Zend/zend_operators.c
@@ -30,6 +30,8 @@
#include "zend_strtod.h"
#include "zend_exceptions.h"
#include "zend_closures.h"
+#include "zend_compile.h"
+#include "zend_hash.h"
#if ZEND_USE_TOLOWER_L
#include <locale.h>
@@ -1800,6 +1802,99 @@ ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSR
}
/* }}} */
+static int protocol_check_function_implementation(void *function_entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key);
+
+static void protocol_cache_dtor(void *data) /* {{{ */
+{
+ zend_hash_destroy((HashTable *) data);
+}
+/* }}} */
+
+ZEND_API zend_bool protocol_check_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC) /* {{{ */
+{
+ zend_bool result = 1;
+ HashTable *cache_bucket = NULL;
+ zend_bool *cache_result = NULL;
+
+ if (!instance_ce->protocol_cache) {
+ HashTable **cache_bucket_ext = NULL;
+
+ if (!EG(protocol_cache)) {
+ EG(protocol_cache) = (HashTable*) malloc(sizeof(HashTable));
+ zend_hash_init(EG(protocol_cache), 16, NULL, protocol_cache_dtor, 1);
+ }
+
+ if (zend_hash_find(EG(protocol_cache), instance_ce->name, instance_ce->name_length, (void **) &cache_bucket_ext) == FAILURE) {
+ cache_bucket = (HashTable*) malloc(sizeof(HashTable));
+ zend_hash_init(cache_bucket, 16, NULL, NULL, 1);
+ zend_hash_add(EG(protocol_cache), instance_ce->name, instance_ce->name_length, (void *) &cache_bucket, sizeof(void *), NULL);
+ } else {
+ cache_bucket = *cache_bucket_ext;
+ }
+ instance_ce->protocol_cache = cache_bucket;
+ } else {
+ cache_bucket = instance_ce->protocol_cache;
+ }
+
+ if (zend_hash_find(cache_bucket, ce->name, ce->name_length, (void **) &cache_result) != FAILURE) {
+ return *cache_result;
+ }
+
+ if (0 == instanceof_function(instance_ce, ce)) {
+ /* Short-circuit if types match */
+ zend_hash_apply_with_arguments(&(ce->function_table), protocol_check_function_implementation, 2, instance_ce, &result);
+ }
+
+ zend_hash_add(cache_bucket, ce->name, ce->name_length, (void *) &result, sizeof(zend_bool), NULL);
+
+ return result;
+}
+/* }}} */
+
+static int protocol_check_function_implementation(void *function_entry TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
+{
+ zend_function *child;
+ zend_bool *result;
+ zend_class_entry *ce;
+ zend_uint child_flags;
+ zend_uint protocol_flags = ((zend_function*) function_entry)->common.fn_flags;
+
+ if (protocol_flags & (ZEND_ACC_PRIVATE | ZEND_ACC_PROTECTED)) {
+ /* Skip the non-public API */
+ return ZEND_HASH_APPLY_KEEP;
+ }
+
+ TSRMLS_FETCH();
+ ce = va_arg(args, zend_class_entry*);
+ result = va_arg(args, zend_bool*);
+
+ if (*result == 0) {
+ return ZEND_HASH_APPLY_STOP;
+ }
+
+ if (zend_hash_quick_find(&(ce->function_table), hash_key->arKey, hash_key->nKeyLength, hash_key->h, (void **) &child) == FAILURE) {
+ *result = 0;
+ return ZEND_HASH_APPLY_STOP;
+ }
+ child_flags = child->common.fn_flags;
+
+ if ((child_flags & ZEND_ACC_STATIC) != (protocol_flags & ZEND_ACC_STATIC)) {
+ *result = 0;
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if ((child_flags & ZEND_ACC_ABSTRACT)) {
+ /* This shouldn't be possible, as it requires an instance, bail! */
+ *result = 0;
+ return ZEND_HASH_APPLY_STOP;
+ }
+ if (zend_do_perform_implementation_check(child, function_entry TSRMLS_DC) == 0) {
+ *result = 0;
+ return ZEND_HASH_APPLY_STOP;
+ }
+ return ZEND_HASH_APPLY_KEEP;
+}
+/* }}} */
+
ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC) /* {{{ */
{
zend_uint i;
View
1  Zend/zend_operators.h
@@ -68,6 +68,7 @@ ZEND_API int is_smaller_or_equal_function(zval *result, zval *op1, zval *op2 TSR
ZEND_API zend_bool instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only TSRMLS_DC);
ZEND_API zend_bool instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce TSRMLS_DC);
+ZEND_API zend_bool protocol_check_function(zend_class_entry *instance_ce, zend_class_entry *ce TSRMLS_DC);
END_EXTERN_C()
#if ZEND_DVAL_TO_LVAL_CAST_OK

No commit comments for this range

Something went wrong with that request. Please try again.