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:

<?php
  class Date { }
  class Article {
    public function setCreated_at(Date $date) { 
      echo __CLASS__, '::', __FUNCTION__, ' called with ';
      var_export($date);
      echo "\n";
    }

    public function setLastchange([Date] $date) { 
      echo __CLASS__, '::', __FUNCTION__, ' called with ';
      var_export($date);
      echo "\n";
    }
  }
  
  $a= new Article();
  $a->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:
---------------------------------------------------------------------
[EMAIL PROTECTED]:~/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
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:

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to