From 2ba15bd7683bca7e4ed40c74cb4d3c7db6085d8c Mon Sep 17 00:00:00 2001
From: Davey Shafik <me@daveyshafik.com>
Date: Wed, 28 Mar 2012 15:40:01 -0400
Subject: [PATCH] Add support for checking trait usage to class_uses()

---
 ext/spl/php_spl.c                    | 29 +++++++++--
 ext/spl/tests/class_uses_basic3.phpt | 30 ++++++++++++
 ext/spl/tests/class_uses_basic4.phpt | 72 ++++++++++++++++++++++++++++
 3 files changed, 126 insertions(+), 5 deletions(-)
 create mode 100644 ext/spl/tests/class_uses_basic3.phpt
 create mode 100644 ext/spl/tests/class_uses_basic4.phpt

diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c
index cd0086626fc7d..35df18dc14737 100755
--- a/ext/spl/php_spl.c
+++ b/ext/spl/php_spl.c
@@ -156,9 +156,13 @@ PHP_FUNCTION(class_uses)
 {
 	zval *obj;
 	zend_bool autoload = 1;
+	char *trait_name = NULL;
+	int trait_name_len;
+	int i;
 	zend_class_entry *ce;
-	
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &obj, &autoload) == FAILURE) {
+	zend_class_entry **trait_ce;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|bs", &obj, &autoload, &trait_name, &trait_name_len) == FAILURE) {
 		RETURN_FALSE;
 	}
 	if (Z_TYPE_P(obj) != IS_OBJECT && Z_TYPE_P(obj) != IS_STRING) {
@@ -173,9 +177,24 @@ PHP_FUNCTION(class_uses)
 	} else {
 		ce = Z_OBJCE_P(obj);
 	}
-	
-	array_init(return_value);
-	spl_add_traits(return_value, ce, 1, ZEND_ACC_TRAIT TSRMLS_CC);
+
+	if (trait_name == NULL) {
+		array_init(return_value);
+		spl_add_traits(return_value, ce, 1, ZEND_ACC_TRAIT TSRMLS_CC);
+	} else {
+		if (zend_lookup_class_ex(trait_name, trait_name_len, NULL, 0, &trait_ce TSRMLS_CC) != FAILURE) {
+			while (ce) {
+				for (i=0; i < ce->num_traits; i++) {
+					if (ce->traits[i] == *trait_ce) {
+						RETURN_TRUE;
+					}
+				}
+				ce = ce->parent;
+			}
+		}
+
+		RETURN_FALSE;
+	}
 }
 /* }}} */
 
diff --git a/ext/spl/tests/class_uses_basic3.phpt b/ext/spl/tests/class_uses_basic3.phpt
new file mode 100644
index 0000000000000..a49e905cac81e
--- /dev/null
+++ b/ext/spl/tests/class_uses_basic3.phpt
@@ -0,0 +1,30 @@
+--TEST--
+SPL: Test class_implements() function : basic
+--FILE--
+<?php
+/* Prototype  : array class_uses(mixed what [, bool autoload ], [, string trait_name])
+ * Description: Return all traits used by a class or check if a trait is used
+ * Source code: ext/spl/php_spl.c
+ * Alias to functions:
+ */
+
+echo "*** Testing class_uses() : basic ***\n";
+
+
+trait foo { }
+class bar { use foo; }
+
+var_dump(class_uses(new bar, false, 'foo'));
+var_dump(class_uses('bar', false, 'foo'));
+var_dump(class_uses(new bar, false, 'bar'));
+var_dump(class_uses(new bar, false, 'bat'));
+
+?>
+===DONE===
+--EXPECT--
+*** Testing class_uses() : basic ***
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+===DONE===
diff --git a/ext/spl/tests/class_uses_basic4.phpt b/ext/spl/tests/class_uses_basic4.phpt
new file mode 100644
index 0000000000000..b6213dcaa2d14
--- /dev/null
+++ b/ext/spl/tests/class_uses_basic4.phpt
@@ -0,0 +1,72 @@
+--TEST--
+SPL: Test class_uses() function : basic
+--FILE--
+<?php
+/* Prototype  : array class_uses(mixed what [, bool autoload ], [, string trait_name])
+ * Description: Return all traits used by a class or check if a trait is used
+ * Source code: ext/spl/php_spl.c
+ * Alias to functions:
+ */
+
+echo "*** Testing class_uses() : basic ***\n";
+
+
+trait foo { }
+class fooUser { use foo; }
+
+trait bar { }
+class barUser { use bar; }
+
+class foobarUser { use foo, bar; }
+
+/** There is no semantics for traits in the inheritance chain.
+    Traits are flattend into a class, and that semantics is nothing
+    like a type, or interface, and thus, not propergated. */
+class fooViaBarUser extends barUser { use foo; }
+
+class fooExtended extends fooUser {}
+
+var_dump(class_uses(new foobarUser, false, 'foo'));
+var_dump(class_uses(new foobarUser, false, 'bar'));
+var_dump(class_uses(new foobarUser, false, 'baz'));
+var_dump(class_uses('foobarUser', false, 'foo'));
+var_dump(class_uses('foobarUser', false, 'bar'));
+var_dump(class_uses('foobarUser', false, 'baz'));
+
+var_dump(class_uses(new fooViaBarUser, false, 'foo'));
+var_dump(class_uses(new fooViaBarUser, false, 'bar'));
+var_dump(class_uses(new fooViaBarUser, false, 'baz'));
+var_dump(class_uses('fooViaBarUser', false, 'foo'));
+var_dump(class_uses('fooViaBarUser', false, 'bar'));
+var_dump(class_uses('fooViaBarUser', false, 'baz'));
+
+var_dump(class_uses(new fooExtended, false, 'foo'));
+var_dump(class_uses(new fooExtended, false, 'bar'));
+var_dump(class_uses(new fooExtended, false, 'baz'));
+var_dump(class_uses('fooExtended', false, 'foo'));
+var_dump(class_uses('fooExtended', false, 'bar'));
+var_dump(class_uses('fooExtended', false, 'baz'));
+
+?>
+===DONE===
+--EXPECT--
+*** Testing class_uses() : basic ***
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(true)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+bool(true)
+bool(false)
+bool(false)
+===DONE===