Commits

Anonymous committed 10f5697

When converting a PHP exception to an Obj-C exception, also proprogate
a useful reason string.

Cut over to Obj-C 2.0 APIs. This makes the code only compatible with OS X.5
and up, but more importantly, eliminates the boat-load of compiler warnings
that were making it impossible to spot real compilation issues.

If there is sufficient demand for supporting earlier versions of OS X, then
this can be implemented in the form of a set of functions that smell like
the 2.0 APIs.

Do a better job at inheriting the type encoding of methods in PHP classes that
extend Obj-C classes.

  • Participants
  • Parent commits a2b3b0f

Comments (0)

Files changed (3)

 /* raise a PHP level exception as an objective-C exception */
 void php_objc_raise(zval *excep TSRMLS_DC)
 {
+	zval msg;
+	NSString *reason = NULL;
+
 	exception_hack = excep;
 	EG(exception) = NULL;
-	[[NSException exceptionWithName:@"PHPException" reason:@"" userInfo:nil] raise];
+
+	/* get a string rendition of the exception */
+	if (Z_OBJ_HT_P(excep)->cast_object) {
+		memset(&msg, 0, sizeof(msg));
+		if (Z_OBJ_HT_P(excep)->cast_object(excep, &msg, IS_STRING TSRMLS_CC)
+				== SUCCESS) {
+			reason = [[NSString alloc] initWithUTF8String:Z_STRVAL(msg)];
+		}
+		zval_dtor(&msg);
+	}
+
+	if (reason == NULL) {
+		reason = @"";
+	}
+
+	[[NSException exceptionWithName:@"PHPException"
+		reason:reason userInfo:nil] raise];
 }
 
 void php_objc_throw_NS(NSException *nse TSRMLS_DC)
  * This source file is subject to version 3.01 of the PHP license.
  */
 #include "php_objc.h"
+		  
+typedef void (*my_ffi_func)(ffi_cif*,void*,void**,void*);
 
 static zend_object_handlers php_objc_handlers;
 static PHP_FUNCTION(objc_method_handler);
 static zend_object_value php_objc_object_new(zend_class_entry *ce TSRMLS_DC);
 static int php_objc_method_call(char *method, INTERNAL_FUNCTION_PARAMETERS);
 static int php_objc_method_call_inner(char *method, int skip_arg_static, INTERNAL_FUNCTION_PARAMETERS);
-static struct objc_class *php_objc_export_class(zend_class_entry *ce TSRMLS_DC);
+static Class php_objc_export_class(zend_class_entry *ce TSRMLS_DC);
 static int php_objc_classHandler(const char *classname);
 static void php_objc_import_classes(TSRMLS_D);
 static char *php_objc_SEL_to_string(SEL s TSRMLS_DC);
 		php_objc_raise(EG(exception) TSRMLS_CC);
 	}
 	if (retval) {
-		char *rettype = [[inv methodSignature] methodReturnType];
+		const char *rettype = [[inv methodSignature] methodReturnType];
 					
 		if (*rettype != _C_VOID) {
 			switch (Z_TYPE_P(retval)) {
 
 - (BOOL)respondsToSelector:(SEL)aSelector
 {
-	struct objc_method_list *mlist;
-	void *iter = 0;
-
-	while ((mlist = class_nextMethodList(self->isa, &iter))) {
-		int i;
-		for (i = 0; i < mlist->method_count; i++) {
-			if (mlist->method_list[i].method_name == aSelector) {
-				return YES;
-			}
-		}
+	if (class_respondsToSelector(self->isa, aSelector) == YES) {
+		return YES;
 	}
 
 	{
 - (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector
 {
 	char *sig;
-	struct objc_method *m;
+	Method m;
+	const char *buf;
+	const char *cur;
+	int nargs = 0;
 
 	m = class_getInstanceMethod(self->isa, aSelector);
 	if (m) {
-		sig = m->method_types;
-	} else {
-		const char *buf;
-		const char *cur;
-		int nargs = 0;
-
-		buf = sel_getName(aSelector);
-		for (cur = buf; *cur; cur++) {
-			if (*cur == ':') {
-				nargs++;
-			}
+		return [NSMethodSignature 
+			signatureWithObjCTypes:method_getTypeEncoding(m)];
+	}
+
+	buf = sel_getName(aSelector);
+	for (cur = buf; *cur; cur++) {
+		if (*cur == ':') {
+			nargs++;
 		}
-
-		sig = alloca(nargs + 4);
-		memset(sig, '@', nargs + 3);
-		sig[nargs+3] = '\0';
-		sig[2] = ':';
-		sig[0] = 'c'; /* FIXME: need to set this correctly */
 	}
 
+	sig = alloca(nargs + 4);
+	memset(sig, '@', nargs + 3);
+	sig[nargs+3] = '\0';
+	sig[2] = ':';
+	sig[0] = 'c'; /* FIXME: need to set this correctly */
+
 	return [NSMethodSignature signatureWithObjCTypes:sig];
 }
 
 
 static int populate_func_entry(zend_function_entry *fe,
 	SEL sel, const char *functype, int modifiers,
-	HashTable *dup_elim TSRMLS_DC)
+	HashTable *dup_elim, Method meth TSRMLS_DC)
 {
 	int narg;
 	char methname[1024];
 	char *cur;
-	struct objc_method fake_meth;
-
-	/* we want to determine the names of the args and their types.
-	 * We do the heavy lifting by faking a regular method entry
-	 * from the method description and using objc runtime provided
-	 * APIs. */
-	memset(&fake_meth, 0, sizeof(fake_meth));
-	fake_meth.method_name = sel;
-	fake_meth.method_types = (char*)functype;
-
-	/* TODO: skip over dangerous methods like release, retain */
+	Class fake = NULL;
+	
+	/* skip over dangerous methods like release, retain */
+	if (sel == @selector(release) || sel == @selector(retain) ||
+			sel == @selector(destroy)) {
+		return 0;
+	}
+
+	if (!meth) {
+		fake = objc_allocateClassPair(NULL, "fake", 0);
+		if (class_addMethod(fake, sel, 0, functype) == NO) {
+			abort();
+		}
+		meth = class_getInstanceMethod(fake, sel);
+	}
+
 
 	memset(fe, 0, sizeof(*fe));
-	fe->num_args = method_getNumberOfArguments(&fake_meth) - 2;
+	fe->num_args = method_getNumberOfArguments(meth) - 2;
 	fe->flags = modifiers;
 
 	if (fe->num_args) {
 
 		for (narg = 0; narg < fe->num_args; narg++) {
 			char *next = NULL;
-			int off;
 			const char *argtype;
+			char argbuf[128];
 
 			/* we name the arguments based on the selector
 			 * name, with the ordinal position appended.
 			cur = next;
 
 			/* what about the type? */
-			method_getArgumentInfo(&fake_meth, narg+2, &argtype, &off);
+			method_getArgumentType(meth, narg+2, argbuf, sizeof(argbuf));
+			argtype = argbuf;
 
 			while (*argtype) {
 				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
 					/* unknown type, perhaps a function pointer */
 					goto cant_map;
 
+				case _C_ARY_B:
+					/* we don't support passing arrays yet */
+					goto cant_map;
+
 				default:
 					printf("unhandled arg type %s for method %s\n",
 							argtype, sel_getName(sel));
 	fe->fname = strdup(methname);
 	fe->handler = PHP_FN(objc_method_handler);
 
+	if (fake) {
+		objc_disposeClassPair(fake);
+	}
+
 	return 1;
 cant_map:
 	/* FIXME: free up things here */
+	if (fake) {
+		objc_disposeClassPair(fake);
+	}
+
+
 	return 0;
 }
 
 		fe = *feptr;
 
 		if (populate_func_entry(fe, list->list[m].name, list->list[m].types,
-				modifiers, dup_elim TSRMLS_CC)) {
+				modifiers, dup_elim, NULL TSRMLS_CC)) {
 			*feptr = fe + 1;
 		}
 	}
 	HashTable dup_elim;
 	char *lname;
 
-	snprintf(namebuf, sizeof(namebuf)-1, "I%s", [p name]);
+	snprintf(namebuf, sizeof(namebuf)-1, "I%s", protocol_getName(p));
 	lname = zend_str_tolower_dup(namebuf, strlen(namebuf));
 
 	/* already registered? */
 {
 	php_objc_class c, *cls, *parent_cls = NULL;
 	zend_class_entry ce;
-	struct objc_protocol_list *plist;
-	void *iter = 0;
+	Protocol **plist;
+	unsigned int pcount;
 	zend_class_entry **ifaces = NULL;
 	int nifaces = 0;
 	int nstatic = 0;
 	int i;
-	struct objc_method_list *mlist;
+	Method *mlist;
+	unsigned int mcount;
 	HashTable dup_elim;
 	char *lname;
 	zend_class_entry **pce;
+	Class metac;
 	
 	if (zend_ts_hash_find(&php_objc_imported_classes, (char*)name,
 			strlen(name), (void**)&cls) == SUCCESS) {
 		return NULL;
 	}
 
-	if (c.class_id->super_class) {
-		parent_cls = php_objc_import_class(c.class_id->super_class->name TSRMLS_CC);
+	if (class_getSuperclass(c.class_id)) {
+		parent_cls = php_objc_import_class(class_getName(
+				class_getSuperclass(c.class_id)) TSRMLS_CC);
 		if (parent_cls == NULL) {
 			/* error */
-			printf("failed to import super %s\n", c.class_id->super_class->name);
+			printf("failed to import super %s\n",
+				class_getName(class_getSuperclass(c.class_id)));
 			return NULL;
 		}
 	} else {
 	zend_hash_init(&dup_elim, 2, NULL, NULL, 0);
 
 	/* for each protocol, register it as an interface */
-	for (plist = c.class_id->protocols; plist; plist = plist->next) {
-		int np;
-		for (np = 0; np < plist->count; np++) {
-			zend_class_entry *ice;
-
-			ice = php_objc_import_interface(plist->list[np] TSRMLS_CC);
-
-			ifaces = realloc(ifaces, (nifaces + 1) * sizeof(zend_class_entry*));
-			ifaces[nifaces++] = ice;
-
-			/* now, manually import the function defs from the interface
-			 * and remove their abstractness.
-			 * Its a shame that we need to do this, as we are able to
-			 * do method resolution on demand.
-			 */
-			c.ftable = realloc(c.ftable,
+	plist = class_copyProtocolList(c.class_id, &pcount);
+	for (i = 0; i < pcount; i++) {
+		zend_class_entry *ice;
+
+		ice = php_objc_import_interface(plist[i] TSRMLS_CC);
+
+		ifaces = realloc(ifaces, (nifaces + 1) * sizeof(zend_class_entry*));
+		ifaces[nifaces++] = ice;
+
+		/* now, manually import the function defs from the interface
+		 * and remove their abstractness.
+		 * Its a shame that we need to do this, as we are able to
+		 * do method resolution on demand.
+		 */
+		c.ftable = realloc(c.ftable,
 				(nstatic + count_interface_methods(ice) + 1) *
 				sizeof(zend_function_entry));
-			nstatic += import_interface_methods(ice, c.ftable + nstatic,
+		nstatic += import_interface_methods(ice, c.ftable + nstatic,
 				&dup_elim TSRMLS_CC);
-		}
 	}
+	free(plist);
+
 	/* due to the way that the zend engine works, we can't dynamically
 	 * resolve the static methods on the class, so we need to do this
 	 * relatively expensive bit of walking here to register our static
 	 * methods. */
-	while ((mlist = class_nextMethodList(c.class_id->isa, &iter))) {
-		int i;
-		for (i = 0; i < mlist->method_count; i++) {
-			if (mlist->method_list[i].method_name == @selector(alloc)) {
-				continue;
-			}
-			c.ftable = realloc(c.ftable,
-					(nstatic+1) * sizeof(zend_function_entry));
-			if (populate_func_entry(&c.ftable[nstatic],
-						mlist->method_list[i].method_name,
-						mlist->method_list[i].method_types,
-						ZEND_ACC_PUBLIC|ZEND_ACC_STATIC,
-						&dup_elim
-						TSRMLS_CC)) {
-				nstatic++;
-			}
+	metac = (Class)objc_getMetaClass(class_getName(c.class_id));
+	mlist = class_copyMethodList(metac, &mcount);
+	for (i = 0; i < mcount; i++) {
+		if (method_getName(mlist[i]) == @selector(alloc)) {
+			continue;
+		}
+		c.ftable = realloc(c.ftable,
+				(nstatic+1) * sizeof(zend_function_entry));
+		if (populate_func_entry(&c.ftable[nstatic],
+					method_getName(mlist[i]),
+					method_getTypeEncoding(mlist[i]),
+					ZEND_ACC_PUBLIC|ZEND_ACC_STATIC,
+					&dup_elim,
+					mlist[i]
+					TSRMLS_CC)) {
+			nstatic++;
 		}
 	}
+	free(mlist);
 	zend_hash_destroy(&dup_elim);
 
 	/* now create a fake alloc entry, to guarantee that the scope
 static void php_objc_import_classes(TSRMLS_D)
 {
 	char *name = NULL;
-	int namelen;
 	int size = 2048;
-	struct objc_class **classes = emalloc(size * sizeof(Class*));
+	Class *classes = emalloc(size * sizeof(Class*));
 	int n, i;
 
 	n = objc_getClassList(classes, size);
 	}
 
 	for (i = 0; i < n; i++) {
-		name = (char*)classes[i]->name;
+		name = (char*)class_getName(classes[i]);
 		if (name[0] == '_' || name[0] == '%') {
 			continue;
 		}
 		RETURN_FALSE;
 	}
 
-	bundle = [[NSBundle alloc] initWithPath:[[NSString alloc] initWithUTF8String:name]];
+	bundle = [[NSBundle alloc] initWithPath:[[NSString alloc]
+					initWithUTF8String:name]];
 	[bundle load];
 	php_objc_import_classes(TSRMLS_C);
 }
     }
 
 	
-	NSApplicationMain(argc, argv);
+	NSApplicationMain(argc, (const char**)argv);
 	efree(argv);
 }
 
 
 		[str dealloc];
 	} else {
-		val = std_object_handlers.has_dimension(object, member,
+		retval = std_object_handlers.has_dimension(object, member,
 				chk_empty TSRMLS_CC);
 	}
 
 static PHP_FUNCTION(objc_method_handler)
 {
 	if (!getThis()) {
+		printf("no this in objc_method_handler\n");
 		php_objc_method_call_inner(
 			((zend_internal_function*)EG(function_state_ptr)->function)
 			->function_name, 0,
 			php_objc_throw("Couldn't make a selector for %s\n" TSRMLS_CC, name);
 			return NULL;
 		}
+	printf("getmethod for %s::%s\n", class_getName(obj->cls->class_id), name);
 
 		meth = class_getInstanceMethod(obj->cls->class_id, sel);
 		if (!meth) {
 			
 			{
 				const char *argtype;
-				int off;
-				method_getArgumentInfo(meth, i+2, &argtype, &off);
+				char argbuf[128];
+				method_getArgumentType(meth, i+2, argbuf, sizeof(argbuf));
+				argtype = argbuf;
 
 				while (*argtype) {
 					if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
 		return;
 	}
 
-	if ([[idval class] conformsToProtocol:@protocol(PHPHasZval)]) {
+	if ([idval conformsToProtocol:@protocol(PHPHasZval)]) {
 		zval *zv = [idval _phpGetZval];
 
 		*return_value = *zv;
 	}
 
 	nobj = ecalloc(1, sizeof(*nobj));
-	cls = php_objc_import_class(idval->isa->name TSRMLS_CC);
+	cls = php_objc_import_class(class_getName(
+			object_getClass(idval)) TSRMLS_CC);
 	
 	nobj->cls = cls;
 	nobj->zo.ce = cls->ce;
 		obj->zo.ce = cls->ce;
 		obj->instance = inst;
 		[obj->instance retain];
-		obj->cls = php_objc_import_class(inst->isa->name TSRMLS_CC);
+		obj->cls = php_objc_import_class(
+				class_getName(object_getClass(inst)) TSRMLS_CC);
 
 		Z_TYPE_P(return_value) = IS_OBJECT;
 		return_value->value.obj.handle = zend_objects_store_put(obj,
 	const char *argtype;
 	int nargs_passed;
 	int first_passed;
-	int offset, is_ref;
+	int is_ref;
 	HashTable *byref_hash = NULL;
 	ffi_cif cif;
 	ffi_type *rettype, **ffi_arg_types = NULL;
 		cls = obj->cls;
 	}
 
+	printf("Looking for %s::%s\n", class_getName(cls->class_id), method);
 	if (obj) {
-		meth = class_getInstanceMethod(obj->cls->class_id, sel);
+		meth = class_getInstanceMethod(cls->class_id, sel);
 	}
 	if (!meth) {
 		meth = class_getClassMethod(cls->class_id, sel);
 		meth = class_getClassMethod(cls->class_id->isa, sel);
 	}
 	if (!meth) {
-		php_objc_throw("couldn't resolve method %s" TSRMLS_CC, method);
+		php_objc_throw("couldn't resolve method %s::%s" TSRMLS_CC,
+			EG(scope)->name, method);
 		return FAILURE;
 	}
 	nargs = method_getNumberOfArguments(meth) - 2;
 	if (obj && obj->from_objc && EG(active_op_array) &&
 			EG(active_op_array)->scope == cls->ce) {
 		sup.receiver = obj->instance;
-		sup.class = cls->class_id->super_class;
+		sup.class = class_getSuperclass(cls->class_id);
 		supptr = &sup;
 		ffi_args[0] = &supptr;
 	} else if (obj) {
 	ffi_args[1] = &sel;
 
 NS_DURING
-	printf("method type: %s(%s)\n", method, meth->method_types);
+	printf("method type: %s(%s)\n", method, method_getTypeEncoding(meth));
 
 	ALLOC_HASHTABLE(byref_hash);
 	zend_hash_init(byref_hash, 2, NULL, NULL, 0);
 	
 	for (i = 0; i < nargs; i++) {
+		char argbuf[128];
 		is_ref = 0;
 
-		method_getArgumentInfo(meth, i+2, &argtype, &offset);
+		method_getArgumentType(meth, i+2, argbuf, sizeof(argbuf));
+		argtype = argbuf;
 		php_objc_type_to_ffi(argtype, &ffi_arg_types[i+2], 0);
 		
 		while (*argtype) {
 		}
 	}
 
-	php_objc_type_to_ffi(meth->method_types, &rettype, 1);
+	php_objc_type_to_ffi(method_getTypeEncoding(meth), &rettype, 1);
 	ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2 + nargs_passed,
 		rettype, ffi_arg_types);
 
 				Class classval;
 			} u;
 
-			argtype = meth->method_types;
+			argtype = method_getTypeEncoding(meth);
 			while (*argtype) {
 				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R' ||
 						*argtype == 'r' || *argtype == 'n' || *argtype == 'O'
 	if (byref_hash) {
 		/* now fixup by-ref parameters */
 		for (i = 0; i < nargs; i++) {
+			char argbuf[128];
 			is_ref = 0;
 
-			method_getArgumentInfo(meth, i+2, &argtype, &offset);
+			method_getArgumentType(meth, i+2, argbuf, sizeof(argbuf));
+			argtype = argbuf;
 
 			while (*argtype) {
 				if (*argtype == 'N' || *argtype == 'o' || *argtype == 'R') {
 	php_objc_obj *obj;
 	zend_object_value retval;
 	zval *tmp;
-	struct objc_class *ocls = php_objc_export_class(ce TSRMLS_CC);
+	Class ocls = php_objc_export_class(ce TSRMLS_CC);
 
 	obj = ecalloc(1, sizeof(*obj));
 	obj->zo.ce = ce;
 	obj->instance = objc_msgSend(ocls, @selector(alloc));
-	obj->cls = php_objc_import_class(obj->instance->isa->name TSRMLS_CC);
+	obj->cls = php_objc_import_class(
+		class_getName(object_getClass(obj->instance)) TSRMLS_CC);
 	
 	retval.handle = zend_objects_store_put(obj, NULL,
 		php_objc_free_stg, php_objc_clone TSRMLS_CC);
 	zval *zme;
 	TSRMLS_FETCH();
 
-	if ([meth->cls->objc_class->isa->super_class
+	if ([class_getSuperclass(meth->cls->objc_class)
 			instancesRespondToSelector:aSelector]) {
 		*(BOOL*)retval = YES;
 		return;
 	TSRMLS_FETCH();
 
 	super.receiver = self;
-	super.class = meth->cls->objc_class->isa->super_class;
+	super.class = class_getSuperclass(meth->cls->objc_class);
 	self = objc_msgSendSuper(&super, sel);
 
 	object_getInstanceVariable(self, "__php_self", (void**)&me);
 	if (!me) {
+		const char *class_name;
 		zval *myzval = NULL;
 
 		zend_try {
 		me->cls = ecalloc(1, sizeof(*me->cls));
 		me->cls->class_id = [self class];
 
-		if (FAILURE == zend_lookup_class((char*)me->cls->class_id->name,
-				strlen(me->cls->class_id->name), &pce TSRMLS_CC)) {
+		class_name = class_getName(me->cls->class_id);
+
+		if (FAILURE == zend_lookup_class((char*)class_name,
+				strlen(class_name), &pce TSRMLS_CC)) {
 			abort();
 		}
 		me->cls->ce = *pce;
 
 printf("methodForSelector %s\n", sel_getName(aSelector));
 	super.receiver = self;
-	super.class = meth->cls->objc_class->isa->super_class;
+	super.class = class_getSuperclass(meth->cls->objc_class);
 	ret = (IMP)objc_msgSendSuper(&super, aSelector);
 
 	if (ret) {
 	zend_function *f, *fptr;
 	zend_class_entry *ce;
 	zval *zme;
+	Method m;
 	TSRMLS_FETCH();
 
 	zme = [self _phpGetZval];
 	ce = Z_OBJCE_P(zme);
 	if (zend_hash_find(&ce->function_table,
 				buf, strlen(buf)+1, (void**)&f) == SUCCESS) {
-		sig = "c@:"; /* reasonably safe default */
+		sig = NULL;
 
 		/* need to mine information from the doc comment */
 		for (fptr = f; fptr; fptr = fptr->common.prototype) {
 			if (fptr->type != ZEND_USER_FUNCTION) {
 				continue;
 			}
-			/* walk up the chain to see if an ancestor has something we can use */
-			if (fptr->op_array.doc_comment && fptr->op_array.doc_comment_len) {
+			/* walk up the chain to see if an ancestor has
+				* something we can use */
+			if (fptr->op_array.doc_comment &&
+					fptr->op_array.doc_comment_len) {
 				const char *s = strstr(fptr->op_array.doc_comment,
-									"@objc:signature");
+						"@objc:signature");
 				const char *e;
 
 				if (s) {
 					break;
 				}
 			}
-			/* TODO: if our ancestor comes from objective-c, we can grab
-			 * the info from there */
 		}
 	}
+	if (sig == NULL) {
+		m = class_getInstanceMethod(meth->cls->objc_class, aSelector);
+		if (m) {
+			sig = (char*)method_getTypeEncoding(m);
+		}
+	}
+
+	if (sig == NULL) {
+		sig = "c@:"; /* reasonably safe default */
+	}
 
 	if (sig) {
 		*(id*)retval = [NSMethodSignature signatureWithObjCTypes:sig];
 	}
 
 	super.receiver = self;
-	super.class = meth->cls->objc_class->isa->super_class;
+	super.class = class_getSuperclass(meth->cls->objc_class);
 	printf("super::methodSignatureForSelector %s\n", sel_getName(aSelector));
 	*(id*)retval = objc_msgSendSuper(&super, aSelector);
 }
 	NSInvocation *inv = *(NSInvocation**)args[2];
 /*	php_objc_method_ctx *meth = (php_objc_method_ctx*)ctx; */
 	php_objc_obj *me;
-	NSMethodSignature *sig = [inv methodSignature];
+//	NSMethodSignature *sig = [inv methodSignature];
 	zval *zme;
 	TSRMLS_FETCH();
 
 {
 	id self = *(id*)args[0];
 	SEL sel = *(SEL*)args[1];
-	php_objc_method_ctx *meth = (php_objc_method_ctx*)ctx;
+//	php_objc_method_ctx *meth = (php_objc_method_ctx*)ctx;
 	php_objc_obj *me;
 	zval *zme;
 	NSInvocation *inv;
 			{
 				php_objc_struct_type *s;
 				if (FAILURE == zend_ts_hash_find(&php_objc_structs,
-						orig, strlen(orig), &s)) {
+						(char*)orig, strlen(orig), (void**)&s)) {
 					s = php_objc_compile_struct(NULL, orig, NULL);
 //					printf("No defined structure to handle %s\n", orig);
 				}
 	return t;
 }
 
-static struct objc_class *php_objc_export_class(zend_class_entry *ce TSRMLS_DC)
+static BOOL do_add_method(php_objc_class_ctx *ctx, SEL name,
+	my_ffi_func closure_func, IMP imp,
+	const char *types, zend_function *f)
 {
-	struct objc_class *metac = NULL, *superc = NULL, *newc = NULL, *root = NULL;
 	ffi_cif *cif;
 	ffi_closure *closure;
 	void *codeloc;
-	static ffi_type *argtypes[3] = {
-			&ffi_type_pointer, &ffi_type_pointer, &ffi_type_pointer
-	};
-	struct objc_method_list *methods, *base_list;
-	struct objc_protocol_list *plist;
+	ffi_type **argtypes = NULL;
+	ffi_type *rettype = NULL;
+	php_objc_method_ctx *meth_ctx;
+	BOOL res;
+	int nargs;
+	char *argstr;
+	int i;
+
+	/* allocate the method before completing the cif, so that we can
+	 * leverage the objective C apis for introspecting the method */
+	if (closure_func) {
+		closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
+		res = class_addMethod(ctx->objc_class, name, (IMP)codeloc, types);
+	} else {
+		res = class_addMethod(ctx->objc_class, name, imp, types);
+	}
+	if (res == NO) {
+		if (closure) {
+			ffi_closure_free(closure);
+		}
+		return NO;
+	}
+
+	meth_ctx = calloc(1, sizeof(*meth_ctx));
+	meth_ctx->cls = ctx;
+	meth_ctx->f = f;
+	meth_ctx->objc_method = class_getInstanceMethod(ctx->objc_class, name);
+
+	argstr = method_copyReturnType(meth_ctx->objc_method);
+	php_objc_type_to_ffi(argstr, &rettype, 1);
+	free(argstr);
+
+	nargs = method_getNumberOfArguments(meth_ctx->objc_method);
+	argtypes = calloc(nargs, sizeof(ffi_type*));
+	for (i = 0; i < nargs; i++) {
+		argstr = method_copyArgumentType(meth_ctx->objc_method, i);
+		php_objc_type_to_ffi(argstr, &argtypes[i], 0);
+		free(argstr);
+	}
+
+	cif = calloc(1, sizeof(*cif));
+	ffi_prep_cif(cif, FFI_DEFAULT_ABI, nargs, rettype, argtypes);
+	if (closure) {
+		ffi_prep_closure_loc(closure, cif, closure_func, meth_ctx, codeloc);
+	}
+
+	return YES;
+}
+
+static Class php_objc_export_class(zend_class_entry *ce TSRMLS_DC)
+{
+	Class newc = NULL;
+	Class superc = NULL;
+	Class root = NULL;
 	php_objc_class_ctx *my_class;
-	php_objc_method_ctx *meth_ctx;
-	void *iter;
 	int i;
-	HashTable meth_hash, stat_hash;
-	struct method_record {
-		zend_function *f;
-		struct objc_method *meth;
-	} mr;
-	HashPosition pos;
-	static char get_zval_sig[128] = "";
-	int ivar_off = 0;
-	
+	HashTable meth_hash;
+	char get_zval_sig[128];
+
 	newc = objc_lookUpClass(ce->name);
 	if (newc) return newc;
 	if (!ce->parent) {
-//		printf("Class %s must inherit from an Objective-C class\n", ce->name);
 		return NULL;
 	}
-
 	superc = objc_lookUpClass(ce->parent->name);
 	if (!superc) {
-//		printf("Class %s must inherit from an Objective-C class\n", ce->name);
 		return NULL;
 	}
-
-	for (root = superc; root->super_class; root = root->super_class)
-		;
-
-	/* allocate class and meta class */
-	newc = calloc(2, sizeof(*newc));
-	metac = newc + 1;
-	newc->isa = metac;
-	
-	newc->info = CLS_CLASS;
-	metac->info = CLS_META;
-	
-	newc->name = strdup(ce->name);
-	metac->name = newc->name;
-
-	metac->methodLists = calloc(1, sizeof(struct objc_method_list*));
-	/* terminate the method list */
-	metac->methodLists[0] = (struct objc_method_list*)-1;
-
-	newc->methodLists = calloc(1, sizeof(struct objc_method_list*));
-	/* terminate the method list */
-	newc->methodLists[0] = (struct objc_method_list*)-1;
+	root = superc;
+	while (class_getSuperclass(root)) {
+		root = class_getSuperclass(root);
+	}
+	newc = objc_allocateClassPair(superc, ce->name, 0);
+	if (!newc) {
+		return NULL;
+	}
+
+	class_addProtocol(newc, @protocol(PHPHasZval));
+	class_addIvar(newc, "__php_self", sizeof(php_objc_obj*),
+		log2(sizeof(php_objc_obj*)), @encode(php_objc_obj*));
 
 	my_class = calloc(1, sizeof(*my_class));
 	my_class->objc_class = newc;
 	my_class->ce = ce;
 
+	/* "init" */
+	do_add_method(my_class, @selector(init),
+		php_objc_exported_init, NULL, "@@:", NULL);
+
+	/* "respondsToSelector" */
+	do_add_method(my_class, @selector(respondsToSelector:),
+		php_objc_exported_respondsToSelector, NULL, "c@::", NULL);
+	
+	/* "_phpGetZval" */
+	snprintf(get_zval_sig, sizeof(get_zval_sig)-1, "%s@:", @encode(zval*));
+	do_add_method(my_class, @selector(_phpGetZval), NULL,
+		php_objc_exported_get_zval, get_zval_sig, NULL);
+
+	/* "methodSignatureForSelector" */
+	do_add_method(my_class, @selector(methodSignatureForSelector:),
+		php_objc_exported_methodSignatureForSelector, NULL,
+		"@@::", NULL);
+
+	/* "forwardInvocation" */
+	do_add_method(my_class, @selector(forwardInvocation:),
+		php_objc_exported_forwardInvocation, NULL,
+		"v@:@", NULL);
+
+	/* "methodForSelector" */
+	do_add_method(my_class, @selector(methodForSelector:),
+		php_objc_exported_methodForSelector, NULL,
+		"@@:@", NULL);
 
 	/* for each method in the objective class method list that is present
 	 * in any of the base classes of the PHP class, up-to and excluding
 	 * We save ourselves from walking our class heirarchy by ignoring
 	 * PHP methods that have internal implementations.
 	 */
-	iter = 0;
 	zend_hash_init(&meth_hash, 2, NULL, NULL, 0);
-	zend_hash_init(&stat_hash, 2, NULL, NULL, 0);
-	while ((base_list = class_nextMethodList(superc->isa, &iter))) {
-		for (i = 0; i < base_list->method_count; i++) {
+
+	while (superc) {
+		Method *meth_list;
+		unsigned int nmethod = 0;
+
+		meth_list = class_copyMethodList(superc, &nmethod);
+		for (i = 0; i < nmethod; i++) {
 			zend_function *func;
-			const char *name = sel_getName(base_list->method_list[i].method_name);
+			const char *name = sel_getName(method_getName(meth_list[i]));
 			char *lname = zend_str_tolower_dup(name, strlen(name));
 			char *cur;
+			int yes;
 
 			cur = strchr(lname, ':');
 			while (cur) {
 				cur = strchr(cur, ':');
 			}
 
-			if (zend_hash_find(&ce->function_table, lname, strlen(name)+1,
-					(void**)&func) == SUCCESS) {
-				if (func->type == ZEND_USER_FUNCTION) {
-					/* let's wrap it up */
-					printf("Should wrap %s\n", func->common.function_name);
-
-					memset(&mr, 0, sizeof(mr));
-					mr.f = func;
-					mr.meth = &base_list->method_list[i];
-
-					zend_hash_update(&meth_hash,
-						lname, strlen(lname)+1,
-						&mr, sizeof(mr), NULL);
+			if (zend_hash_find(&meth_hash, lname, strlen(lname)+1,
+						NULL) == FAILURE) {
+				if (zend_hash_find(&ce->function_table, lname, strlen(name)+1,
+							(void**)&func) == SUCCESS) {
+					if (func->type == ZEND_USER_FUNCTION) {
+						/* let's wrap it up */
+						printf("Wrapping %s\n", func->common.function_name);
+
+						zend_hash_update(&meth_hash,
+								lname, strlen(lname)+1,
+								&yes, sizeof(yes), NULL);
+
+						do_add_method(my_class, method_getName(meth_list[i]),
+							php_objc_exported_func, NULL,
+							method_getTypeEncoding(meth_list[i]),
+							func);
+					}
 				}
 			}
 			efree(lname);
 		}
+		free(meth_list);
+		superc = class_getSuperclass(superc);
 	}
-
-	/* we always need our own "init" method */
-	zend_hash_del(&meth_hash, "init", sizeof("init"));
-
-	/* now we can allocate storage */
-#define N_OVERRIDES 6
-	methods = calloc(1, sizeof(*methods) + 
-		((N_OVERRIDES + meth_hash.nNumOfElements) * sizeof(struct objc_method)));
-	methods->method_count = N_OVERRIDES + meth_hash.nNumOfElements;
-//	printf("%s::Going to add %d methods\n", ce->name, methods->method_count);
-
-	/* the init method */
-	cif = calloc(1, sizeof(*cif));
-	ffi_prep_cif(cif, FFI_DEFAULT_ABI, 2, &ffi_type_pointer, argtypes);
-	closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-	meth_ctx = calloc(1, sizeof(*meth_ctx));
-	meth_ctx->cls = my_class;
-	ffi_prep_closure_loc(closure, cif, php_objc_exported_init, meth_ctx, codeloc);
-	meth_ctx->objc_method = &methods->method_list[0];
-	methods->method_list[0].method_imp = codeloc;
-	methods->method_list[0].method_name = @selector(init);
-	methods->method_list[0].method_types = "@@:";
-
-	/* the respondsToSelector method */
-	cif = calloc(1, sizeof(*cif));
-	ffi_prep_cif(cif, FFI_DEFAULT_ABI, 3, &ffi_type_pointer, argtypes);
-	closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-	meth_ctx = calloc(1, sizeof(*meth_ctx));
-	meth_ctx->cls = my_class;
-	ffi_prep_closure_loc(closure, cif, php_objc_exported_respondsToSelector, meth_ctx, codeloc);
-	meth_ctx->objc_method = &methods->method_list[1];
-	methods->method_list[1].method_imp = codeloc;
-	methods->method_list[1].method_name = @selector(respondsToSelector:);
-	methods->method_list[1].method_types = "c@::";
-
-	/* the _phpGetZval method */
-	if (!get_zval_sig[0]) {
-		snprintf(get_zval_sig, sizeof(get_zval_sig)-1, "%s@:", @encode(zval*));
-	}
-	methods->method_list[2].method_name = @selector(_phpGetZval);
-	methods->method_list[2].method_types = get_zval_sig;
-	methods->method_list[2].method_imp = php_objc_exported_get_zval;
-
-	/* the methodSignatureForSelector method */
-	cif = calloc(1, sizeof(*cif));
-	ffi_prep_cif(cif, FFI_DEFAULT_ABI, 3, &ffi_type_pointer, argtypes);
-	closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-	meth_ctx = calloc(1, sizeof(*meth_ctx));
-	meth_ctx->cls = my_class;
-	ffi_prep_closure_loc(closure, cif, php_objc_exported_methodSignatureForSelector, meth_ctx, codeloc);
-	meth_ctx->objc_method = &methods->method_list[3];
-	methods->method_list[3].method_imp = codeloc;
-	methods->method_list[3].method_name = @selector(methodSignatureForSelector:);
-	methods->method_list[3].method_types = "@@::";
-
-	/* the forwardInvocation method */
-	cif = calloc(1, sizeof(*cif));
-	ffi_prep_cif(cif, FFI_DEFAULT_ABI, 3, &ffi_type_pointer, argtypes);
-	closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-	meth_ctx = calloc(1, sizeof(*meth_ctx));
-	meth_ctx->cls = my_class;
-	ffi_prep_closure_loc(closure, cif, php_objc_exported_forwardInvocation, meth_ctx, codeloc);
-	meth_ctx->objc_method = &methods->method_list[4];
-	methods->method_list[4].method_imp = codeloc;
-	methods->method_list[4].method_name = @selector(forwardInvocation:);
-	methods->method_list[4].method_types = "v@:@";
-
-	/* the methodForSelector method */
-	cif = calloc(1, sizeof(*cif));
-	ffi_prep_cif(cif, FFI_DEFAULT_ABI, 3, &ffi_type_pointer, argtypes);
-	closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-	meth_ctx = calloc(1, sizeof(*meth_ctx));
-	meth_ctx->cls = my_class;
-	ffi_prep_closure_loc(closure, cif, php_objc_exported_methodForSelector, meth_ctx, codeloc);
-	meth_ctx->objc_method = &methods->method_list[5];
-	methods->method_list[5].method_imp = codeloc;
-	methods->method_list[5].method_name = @selector(methodForSelector:);
-	methods->method_list[5].method_types = "@@:@";
-
-	/* FIXME: dealloc */
-	
-	for (i = N_OVERRIDES, pos = meth_hash.pListHead;
-			pos; pos = pos->pListNext) {
-		const char *t;
-		ffi_type *func_args[24];
-		ffi_type *rettype = NULL;
-		ffi_type **heap_args;
-		int j;
-
-		mr = *(struct method_record*)pos->pData;
-		methods->method_list[i] = *mr.meth;
-
-		meth_ctx = calloc(1, sizeof(*meth_ctx));
-		meth_ctx->cls = my_class;
-		meth_ctx->objc_method = &methods->method_list[i];
-		meth_ctx->f = mr.f;
-
-		cif = calloc(1, sizeof(*cif));
-
-		/* parse methods->method_list[i].method_types into ffi arg types */
-		memset(func_args, 0, sizeof(func_args));
-		t = php_objc_type_to_ffi(methods->method_list[i].method_types,
-				&rettype, 1);
-		for (j = 0; j < 24 && t && *t; j++) {
-			t = php_objc_type_to_ffi(t, &func_args[j], 0);
-//			printf("  arg[%d] = %p\n", j, func_args[j]);
-		}
-
-		heap_args = malloc(j * sizeof(ffi_type*));
-		memcpy(heap_args, func_args, j * sizeof(ffi_type*));
-
-		printf("prepping closure for %s, %d args, type: %s\n",
-			sel_getName(mr.meth->method_name),
-			j, methods->method_list[i].method_types);
-		ffi_prep_cif(cif, FFI_DEFAULT_ABI, j, rettype, heap_args);
-//		printf("ret size is %d\n", rettype->size);
-		closure = ffi_closure_alloc(sizeof(*closure), &codeloc);
-		ffi_prep_closure_loc(closure, cif, php_objc_exported_func,
-			meth_ctx, codeloc);
-
-		methods->method_list[i].method_imp = codeloc;
-	} 
-
-	class_addMethods(newc, methods);
-
-	plist = calloc(1, sizeof(*plist));
-	plist->count = 1;
-	plist->list[0] = @protocol(PHPHasZval);
-	newc->protocols = plist;
-	metac->protocols = plist;
-
-	newc->super_class = superc;
-	metac->super_class = superc->isa;
-	metac->instance_size = metac->super_class->instance_size;
-	newc->instance_size = superc->instance_size;
-	metac->isa = root->isa;
-
-	newc->instance_size += sizeof(zval*);
-	metac->instance_size += sizeof(zval*);
-
-	newc->ivars = calloc(1, sizeof(struct objc_ivar_list) + (2 * sizeof(struct objc_ivar)));
-	newc->ivars[0].ivar_count = 0;
-	ivar_off = superc->instance_size;
-	
-	newc->ivars[0].ivar_list[0].ivar_name = "__php_self";
-	newc->ivars[0].ivar_list[0].ivar_type = @encode(php_objc_obj*);
-	newc->ivars[0].ivar_list[0].ivar_offset = ivar_off;
-	newc->ivars[0].ivar_count++;
-	ivar_off += sizeof(void*);
-
-	objc_addClass(newc);
+	objc_registerClassPair(newc);
+
 	return newc;
 }
 
 static int php_objc_classHandler(const char *classname)
 {
 	zend_class_entry **pce;
-	struct objc_class *cls;
+	Class cls;
 	TSRMLS_FETCH();
 
-//	printf("I'm trying to find class %s\n", classname);
+	printf("I'm trying to find class %s\n", classname);
 
 	if (FAILURE == zend_lookup_class((char*)classname,
 			strlen(classname), &pce TSRMLS_CC)) {
 		return 0;
 	}
 
-//	printf("I have one of those %s\n", (*pce)->name);
+	printf("I have one of those %s\n", (*pce)->name);
 	cls = php_objc_export_class(*pce);
 	if (cls) return 1;
 	
 
 	my_pool = [[NSAutoreleasePool alloc] init];
 //  Deprecated in Leopard
-	objc_setClassHandler(php_objc_classHandler);
+//	objc_setClassHandler(php_objc_classHandler);
 
 	zend_ts_hash_init(&php_objc_imported_classes, 0, NULL, NULL, 1);
 	zend_ts_hash_init(&php_objc_imported_frameworks, 0, NULL, NULL, 1);
 		NULL TSRMLS_CC);
 	php_objc_excep_ce->ce_flags |= ZEND_ACC_FINAL;
 
-#if 0 /* handled via BridgeSupport */
-	REGISTER_STRING_CONSTANT("NSNibTopLevelObjects", 
-		(char*)[NSNibTopLevelObjects UTF8String], CONST_CS|CONST_PERSISTENT);
-	REGISTER_STRING_CONSTANT("NSNibOwner",
-		(char*)[NSNibOwner UTF8String], CONST_CS|CONST_PERSISTENT);
-#endif
 	return SUCCESS;
 }
 
 static PHP_MSHUTDOWN_FUNCTION(php_objc)
 {
-//	[my_pool dealloc];
 	return SUCCESS;
 }
 
 #include <Foundation/NSInvocation.h>
 #include <Foundation/NSMethodSignature.h>
 #include <AppKit/NSNib.h>
+#include <AppKit/NSApplication.h>
 
 typedef struct {
 	zend_class_entry *ce;
 
 typedef struct {
 	php_objc_class_ctx *cls;
-	struct objc_method *objc_method;
+	Method objc_method;
 	zend_function *f;
 } php_objc_method_ctx;