Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:368 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 10585 invoked from network); 27 Mar 2003 16:16:10 -0000 Received: from unknown (HELO moutng.kundenserver.de) (212.227.126.177) by pb1.pair.com with SMTP; 27 Mar 2003 16:16:10 -0000 Received: from [212.227.126.162] (helo=mrelayng.kundenserver.de) by moutng.kundenserver.de with esmtp (Exim 3.35 #1) id 18ya2y-0004BC-00; Thu, 27 Mar 2003 17:16:08 +0100 Received: from [217.80.180.194] (helo=[217.80.180.194]) by mrelayng.kundenserver.de with asmtp (Exim 3.35 #1) id 18ya2y-0003uU-00; Thu, 27 Mar 2003 17:16:08 +0100 To: engine2@lists.zend.com Cc: internals@lists.php.net Content-Type: multipart/mixed; boundary="=-7w7gBRR+pFqt2EQN6mSN" Organization: Message-ID: <1048781764.25691.593.camel@localhost> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.2.1 Date: 27 Mar 2003 17:16:04 +0100 Subject: Type hints revisited [IllegalArgumentException instead of E_ERROR] From: thekid@thekid.de (Timm Friebe) --=-7w7gBRR+pFqt2EQN6mSN Content-Type: text/plain Content-Transfer-Encoding: 7bit I've implemented an additional feature for type hints that will throw an exception instead of bailing out in case an incorrect type is passed. Test script: setLastchange(new Date()); $a->setLastchange(NULL); // Passes $a->setCreated_at(new Date()); try { $a->setCreated_at(NULL); // Fails } catch (IllegalArgumentException $e) { echo "Caught: "; var_dump($e); } $a->setCreated_at(1); // Fails echo "Alive"; // Will not show up ?> Output: --------------------------------------------------------------------- thekid@friebes:~/devel/php > ./php5/sapi/cli/php hints.php article::setlastchange called with class date { } article::setlastchange called with NULL article::setcreated_at called with class date { } Caught: object(illegalargumentexception)#2 (3) { ["message"]=> string(38) "Argument 1 must be an instance of date" ["file"]=> string(36) "/usr/home/thekid/devel/php/hints.php" ["line"]=> int(4) } Fatal error: Uncaught exception! in Unknown on line 0 --------------------------------------------------------------------- A unified diff is attached. - Timm --=-7w7gBRR+pFqt2EQN6mSN Content-Disposition: attachment; filename=zend_hints.diff Content-Type: text/x-patch; name=zend_hints.diff; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Index: Zend/ZEND_CHANGES =================================================================== RCS file: /repository/ZendEngine2/ZEND_CHANGES,v retrieving revision 1.55 diff -u -r1.55 ZEND_CHANGES --- Zend/ZEND_CHANGES 7 Mar 2003 16:45:41 -0000 1.55 +++ Zend/ZEND_CHANGES 27 Mar 2003 16:11:06 -0000 @@ -177,6 +177,10 @@ function b(Bar $bar) { // ... } + + function c([Bar] $bar) { + // ... + } } $a = new FooBar; @@ -184,10 +188,14 @@ $a->a($b); $a->b($b); + $a->c($b); + $a->c(NULL); ?> These class type hints are not checked upon compilation, as would - be the case in a typed language, but during runtime. + be the case in a typed language, but during runtime. Type hints in + square brackets (as seen in function "c") denote passing of NULL is + also allowed. This means that @@ -202,6 +210,21 @@ die('Argument 1 must be an instance of ClassName'); } } + + and + + function foo([ClassName] $object) { + // ... + } + + is equivalent to + + function foo($object) { + if (!is_null($object) && !($object instanceof ClassName)) { + die('Argument 1 must be an instance of ClassName'); + } + } + . This syntax only applies to objects/classes, not built-in types. Index: Zend/zend_default_classes.c =================================================================== RCS file: /repository/ZendEngine2/zend_default_classes.c,v retrieving revision 1.3 diff -u -r1.3 zend_default_classes.c --- Zend/zend_default_classes.c 23 Mar 2003 17:18:31 -0000 1.3 +++ Zend/zend_default_classes.c 27 Mar 2003 16:11:07 -0000 @@ -22,6 +22,7 @@ #include "zend_API.h" zend_class_entry *default_exception_ptr; +zend_class_entry *illegalargument_exception_ptr; ZEND_FUNCTION(exception) { @@ -113,17 +114,23 @@ {NULL, NULL, NULL} }; -static void zend_register_default_exception(TSRMLS_D) +static void zend_register_default_exceptions(TSRMLS_D) { - zend_class_entry default_exception; + zend_class_entry default_exception, illegalargument_exception; INIT_CLASS_ENTRY(default_exception, "exception", default_exception_functions); default_exception_ptr = zend_register_internal_class(&default_exception TSRMLS_CC); + + INIT_CLASS_ENTRY(illegalargument_exception, "illegalargumentexception", NULL); + illegalargument_exception_ptr = zend_register_internal_class_ex( + &illegalargument_exception, + default_exception_ptr, + NULL TSRMLS_CC); } ZEND_API void zend_register_default_classes(TSRMLS_D) { - zend_register_default_exception(TSRMLS_C); + zend_register_default_exceptions(TSRMLS_C); } /* Index: Zend/zend_execute.c =================================================================== RCS file: /repository/ZendEngine2/zend_execute.c,v retrieving revision 1.448 diff -u -r1.448 zend_execute.c --- Zend/zend_execute.c 26 Mar 2003 20:42:23 -0000 1.448 +++ Zend/zend_execute.c 27 Mar 2003 16:11:08 -0000 @@ -4011,17 +4011,52 @@ { zval *arg = get_zval_ptr(&EX(opline)->op2, EX(Ts), &EG(free_op2), BP_VAR_R); zend_class_entry *ce = EX_T(EX(opline)->op1.u.var).EA.class_entry; + int allowed; + + switch (Z_TYPE_P(arg)) { + case IS_OBJECT: + allowed= instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC); + break; + + case IS_NULL: + allowed= (EX(opline)->op1.op_type == IS_VAR); + break; + + default: + allowed= 0; + } - if ((Z_TYPE_P(arg) != IS_OBJECT) - || !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) { + if (!allowed) { char *error_msg; + zend_class_entry **exception_ce = NULL; + zval *exception; if (ce->ce_flags & ZEND_ACC_INTERFACE) { error_msg = "implement interface"; } else { error_msg = "be an instance of"; } - zend_error(E_ERROR, "Argument %d must %s %s", EX(opline)->extended_value, error_msg, ce->name); + + if (zend_lookup_class("illegalargumentexception", sizeof("illegalargumentexception")- 1, &exception_ce TSRMLS_CC) == FAILURE) { + zend_error(E_ERROR, "Argument %d must %s %s", EX(opline)->extended_value, error_msg, ce->name); + + /* Bails out */ + } else { + char* message; + + message = (char *) emalloc(1024); + snprintf(message, 1024, "Argument %d must %s %s", EX(opline)->extended_value, error_msg, ce->name); + + ALLOC_ZVAL(exception); + INIT_PZVAL(exception); + object_init_ex(exception, *exception_ce); + add_property_string_ex(exception, "message", sizeof("message"), message, 0); + add_property_string_ex(exception, "file", sizeof("file"), zend_get_executed_filename(TSRMLS_C), 0); + add_property_long_ex(exception, "line", sizeof("line"), zend_get_executed_lineno(TSRMLS_C)); + + EG(exception) = exception; + RETURN_FROM_EXECUTE_LOOP(execute_data); + } } NEXT_OPCODE(); Index: Zend/zend_language_parser.y =================================================================== RCS file: /repository/ZendEngine2/zend_language_parser.y,v retrieving revision 1.105 diff -u -r1.105 zend_language_parser.y --- Zend/zend_language_parser.y 26 Mar 2003 20:42:23 -0000 1.105 +++ Zend/zend_language_parser.y 27 Mar 2003 16:11:09 -0000 @@ -453,7 +453,8 @@ optional_class_type: /* empty */ { $$.op_type = IS_UNUSED; } - | fully_qualified_class_name { $$ = $1; } + | fully_qualified_class_name { $$ = $1; $$.op_type = IS_CONST; } + | '[' fully_qualified_class_name ']' { $$ = $2; $$.op_type = IS_VAR; } ; function_call_parameter_list: --=-7w7gBRR+pFqt2EQN6mSN--