You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

596 lines
16 KiB

26 years ago
26 years ago
26 years ago
26 years ago
26 years ago
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP version 4.0 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 2.02 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available at through the world-wide-web at |
  10. | http://www.php.net/license.html. |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: Sam Ruby (rubys@us.ibm.com) |
  16. +----------------------------------------------------------------------+
  17. */
  18. /*
  19. * This module implements Zend OO syntax overloading support for Java
  20. * components using JNI and reflection.
  21. */
  22. #include "dl/phpdl.h"
  23. #include "php.h"
  24. #include "zend_compile.h"
  25. #include "php_ini.h"
  26. #include "php_globals.h"
  27. #include <jni.h>
  28. #include <stdio.h>
  29. #define IS_EXCEPTION 86
  30. /***************************************************************************/
  31. #ifndef KAFFE
  32. #ifndef JNI_11
  33. #ifndef JNI_12
  34. #ifdef JNI_VERSION_1_2
  35. #define JNI_12
  36. #else
  37. #define JNI_11
  38. #endif
  39. #endif
  40. #endif
  41. #endif
  42. #ifdef PHP_WIN32
  43. #ifdef JNI_12
  44. #define JAVALIB "jvm.dll"
  45. #else
  46. #define JAVALIB "javai.dll"
  47. #endif
  48. #else
  49. #endif
  50. /***************************************************************************/
  51. static int le_jobject = 0;
  52. static char *classpath = 0;
  53. static char *libpath = 0;
  54. static char *javahome = 0;
  55. static char *javalib = 0;
  56. static int iniUpdated = 0;
  57. static JavaVM *jvm = 0;
  58. static JNIEnv *jenv = 0;
  59. static jclass php_reflect;
  60. static void *dl_handle = 0;
  61. static zend_class_entry java_class_entry;
  62. static PHP_INI_MH(OnIniUpdate) {
  63. if (new_value) *(char**)mh_arg1 = new_value;
  64. iniUpdated=1;
  65. return SUCCESS;
  66. }
  67. PHP_INI_BEGIN()
  68. PHP_INI_ENTRY1("java.class.path",
  69. NULL, PHP_INI_ALL, OnIniUpdate, &classpath)
  70. #ifndef JNI_11
  71. PHP_INI_ENTRY1("java.home",
  72. NULL, PHP_INI_ALL, OnIniUpdate, &javahome)
  73. PHP_INI_ENTRY1("java.library.path",
  74. NULL, PHP_INI_ALL, OnIniUpdate, &libpath)
  75. #endif
  76. #ifdef JAVALIB
  77. PHP_INI_ENTRY1("java.library",
  78. JAVALIB, PHP_INI_ALL, OnIniUpdate, &javalib)
  79. #else
  80. PHP_INI_ENTRY1("java.library",
  81. NULL, PHP_INI_ALL, OnIniUpdate, &javalib)
  82. #endif
  83. PHP_INI_END()
  84. /***************************************************************************/
  85. /*
  86. * Destroy a Java Virtual Machine.
  87. */
  88. void jvm_destroy() {
  89. if (php_reflect) (*jenv)->DeleteGlobalRef(jenv, php_reflect);
  90. if (jvm) {
  91. (*jvm)->DetachCurrentThread(jvm);
  92. (*jvm)->DestroyJavaVM(jvm);
  93. jvm = 0;
  94. }
  95. if (dl_handle) DL_UNLOAD(dl_handle);
  96. php_reflect = 0;
  97. jenv = 0;
  98. }
  99. /*
  100. * Create a Java Virtual Machine.
  101. * - class.path, home, and library.path are read out of the INI file
  102. * - appropriate (pre 1.1, JDK 1.1, and JDK 1.2) initialization is performed
  103. * - net.php.reflect class file is located
  104. */
  105. #ifdef JNI_12
  106. static void addJVMOption(JavaVMInitArgs *vm_args, char *name, char *value) {
  107. char *option = (char*) malloc(strlen(name) + strlen(value) + 1);
  108. strcpy(option, name);
  109. strcat(option, value);
  110. vm_args->options[vm_args->nOptions++].optionString = option;
  111. }
  112. #endif
  113. static int jvm_create() {
  114. int rc;
  115. jclass local_php_reflect;
  116. jthrowable error;
  117. jint (JNICALL *JNI_CreateVM)(const void*,const void*,void*);
  118. #ifndef JNI_12
  119. jint (JNICALL *JNI_DefaultArgs)(void*);
  120. #endif
  121. #ifdef JNI_11
  122. JDK1_1InitArgs vm_args;
  123. #else
  124. JavaVMInitArgs vm_args;
  125. #ifdef JNI_12
  126. JavaVMOption options[3];
  127. #endif
  128. #endif
  129. iniUpdated=0;
  130. if (javalib) {
  131. dl_handle = DL_LOAD(javalib);
  132. if (!dl_handle) {
  133. php_error(E_ERROR, "Unable to load Java Library %s", javalib);
  134. return -1;
  135. }
  136. }
  137. #ifndef JAVALIB
  138. if (!dl_handle)
  139. JNI_CreateVM = &JNI_CreateJavaVM;
  140. else
  141. #endif
  142. JNI_CreateVM = (jint (JNICALL *)(const void*,const void*,void*))
  143. DL_FETCH_SYMBOL(dl_handle, "JNI_CreateJavaVM");
  144. if (!JNI_CreateVM) {
  145. php_error(E_ERROR, "Unable to locate CreateJavaVM function");
  146. return -1;
  147. }
  148. #ifdef JNI_12
  149. vm_args.version = JNI_VERSION_1_2;
  150. vm_args.ignoreUnrecognized = JNI_FALSE;
  151. vm_args.options = options;
  152. vm_args.nOptions = 0;
  153. if (classpath) addJVMOption(&vm_args, "-Djava.class.path=", classpath);
  154. if (javahome) addJVMOption(&vm_args, "-Djava.home=", javahome);
  155. if (libpath) addJVMOption(&vm_args, "-Djava.library.path=", libpath);
  156. #else
  157. #ifndef JAVALIB
  158. if (!dl_handle)
  159. JNI_DefaultArgs = &JNI_GetDefaultJavaVMInitArgs;
  160. else
  161. #endif
  162. JNI_DefaultArgs = (jint (JNICALL *)(void*))
  163. DL_FETCH_SYMBOL(dl_handle, "JNI_GetDefaultJavaVMInitArgs");
  164. if (!JNI_DefaultArgs) {
  165. php_error(E_ERROR, "Unable to locate GetDefaultJavaVMInitArgs function");
  166. return -1;
  167. }
  168. vm_args.version=0x00010001;
  169. (*JNI_DefaultArgs)(&vm_args);
  170. if (!classpath) classpath = "";
  171. vm_args.classpath = classpath;
  172. #ifdef KAFFE
  173. vm_args.classhome = javahome;
  174. vm_args.libraryhome = libpath;
  175. #endif
  176. #endif
  177. rc = (*JNI_CreateVM)(&jvm, &jenv, &vm_args);
  178. if (rc) {
  179. php_error(E_ERROR, "Unable to create Java Virtual Machine");
  180. return rc;
  181. }
  182. local_php_reflect = (*jenv)->FindClass(jenv, "net/php/reflect");
  183. error = (*jenv)->ExceptionOccurred(jenv);
  184. if (error) {
  185. jclass errClass;
  186. jmethodID toString;
  187. jobject errString;
  188. const char *errAsUTF;
  189. jboolean isCopy;
  190. (*jenv)->ExceptionClear(jenv);
  191. errClass = (*jenv)->GetObjectClass(jenv, error);
  192. toString = (*jenv)->GetMethodID(jenv, errClass, "toString",
  193. "()Ljava/lang/String;");
  194. errString = (*jenv)->CallObjectMethod(jenv, error, toString);
  195. errAsUTF = (*jenv)->GetStringUTFChars(jenv, errString, &isCopy);
  196. php_error(E_ERROR, "%s", errAsUTF);
  197. if (isCopy) (*jenv)->ReleaseStringUTFChars(jenv, error, errAsUTF);
  198. jvm_destroy();
  199. return -1;
  200. }
  201. php_reflect = (*jenv)->NewGlobalRef(jenv, local_php_reflect);
  202. return rc;
  203. }
  204. /***************************************************************************/
  205. static jobjectArray _java_makeArray(int argc, pval** argv) {
  206. jclass objectClass = (*jenv)->FindClass(jenv, "java/lang/Object");
  207. jobjectArray result = (*jenv)->NewObjectArray(jenv, argc, objectClass, 0);
  208. jobject arg;
  209. jmethodID makeArg;
  210. int i;
  211. pval **handle;
  212. int type;
  213. for (i=0; i<argc; i++) {
  214. switch (argv[i]->type) {
  215. case IS_STRING:
  216. arg=(*jenv)->NewByteArray(jenv,argv[i]->value.str.len);
  217. (*jenv)->SetByteArrayRegion(jenv,(jbyteArray)arg,0,
  218. argv[i]->value.str.len, argv[i]->value.str.val);
  219. break;
  220. case IS_OBJECT:
  221. zend_hash_index_find(argv[i]->value.obj.properties, 0, (void*)&handle);
  222. arg = zend_list_find((*handle)->value.lval, &type);
  223. break;
  224. case IS_BOOL:
  225. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  226. "(Z)Ljava/lang/Object;");
  227. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  228. (jboolean)(argv[i]->value.lval));
  229. break;
  230. case IS_LONG:
  231. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  232. "(J)Ljava/lang/Object;");
  233. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  234. (jlong)(argv[i]->value.lval));
  235. break;
  236. case IS_DOUBLE:
  237. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  238. "(D)Ljava/lang/Object;");
  239. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  240. (jdouble)(argv[i]->value.dval));
  241. break;
  242. default:
  243. arg=0;
  244. }
  245. (*jenv)->SetObjectArrayElement(jenv, result, i, arg);
  246. if (argv[i]->type != IS_OBJECT)
  247. (*jenv)->DeleteLocalRef(jenv, arg);
  248. }
  249. return result;
  250. }
  251. static int checkError(pval *value) {
  252. if (value->type == IS_EXCEPTION) {
  253. php_error(E_WARNING, "%s", value->value.str.val);
  254. efree(value->value.str.val);
  255. var_reset(value);
  256. return 1;
  257. };
  258. return 0;
  259. }
  260. /***************************************************************************/
  261. /*
  262. * Invoke a method on an object. If method name is "java", create a new
  263. * object instead.
  264. */
  265. void java_call_function_handler
  266. (INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
  267. {
  268. pval *object = property_reference->object;
  269. zend_overloaded_element *function_name = (zend_overloaded_element *)
  270. property_reference->elements_list->tail->data;
  271. int arg_count = ZEND_NUM_ARGS();
  272. jlong result = 0;
  273. pval **arguments = (pval **) emalloc(sizeof(pval *)*arg_count);
  274. getParametersArray(ht, arg_count, arguments);
  275. if (iniUpdated && jenv) jvm_destroy();
  276. if (!jenv) jvm_create();
  277. if (!jenv) return;
  278. if (!strcmp("java",function_name->element.value.str.val)) {
  279. /* construct a Java object:
  280. First argument is the class name. Any additional arguments will
  281. be treated as constructor parameters. */
  282. jmethodID co = (*jenv)->GetStaticMethodID(jenv, php_reflect, "CreateObject",
  283. "(Ljava/lang/String;[Ljava/lang/Object;J)V");
  284. jstring className=(*jenv)->NewStringUTF(jenv, arguments[0]->value.str.val);
  285. (pval*)(long)result = object;
  286. (*jenv)->CallStaticVoidMethod(jenv, php_reflect, co,
  287. className, _java_makeArray(arg_count-1, arguments+1), result);
  288. (*jenv)->DeleteLocalRef(jenv, className);
  289. } else {
  290. pval **handle;
  291. int type;
  292. jobject obj;
  293. jstring method;
  294. /* invoke a method on the given object */
  295. jmethodID invoke = (*jenv)->GetStaticMethodID(jenv, php_reflect, "Invoke",
  296. "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;J)V");
  297. zend_hash_index_find(object->value.obj.properties, 0, (void**) &handle);
  298. obj = zend_list_find((*handle)->value.lval, &type);
  299. method = (*jenv)->NewStringUTF(jenv, function_name->element.value.str.val);
  300. (pval*)(long)result = return_value;
  301. (*jenv)->CallStaticVoidMethod(jenv, php_reflect, invoke,
  302. obj, method, _java_makeArray(arg_count, arguments), result);
  303. (*jenv)->DeleteLocalRef(jenv, method);
  304. }
  305. efree(arguments);
  306. pval_destructor(&function_name->element);
  307. checkError((pval*)(long)result);
  308. }
  309. /***************************************************************************/
  310. static pval _java_getset_property
  311. (zend_property_reference *property_reference, jobjectArray value)
  312. {
  313. pval presult;
  314. jlong result = 0;
  315. pval **pobject;
  316. jobject obj;
  317. int type;
  318. /* get the property name */
  319. zend_llist_element *element = property_reference->elements_list->head;
  320. zend_overloaded_element *property=(zend_overloaded_element *)element->data;
  321. jstring propName =
  322. (*jenv)->NewStringUTF(jenv, property->element.value.str.val);
  323. /* get the object */
  324. zend_hash_index_find(property_reference->object->value.obj.properties,
  325. 0, (void **) &pobject);
  326. obj = zend_list_find((*pobject)->value.lval,&type);
  327. (pval*)(long)result = &presult;
  328. var_uninit(&presult);
  329. if (!obj || (type!=le_jobject)) {
  330. php_error(E_ERROR,
  331. "Attempt to access a Java property on a non-Java object");
  332. } else {
  333. /* invoke the method */
  334. jmethodID gsp = (*jenv)->GetStaticMethodID(jenv, php_reflect, "GetSetProp",
  335. "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;J)V");
  336. (*jenv)->CallStaticVoidMethod
  337. (jenv, php_reflect, gsp, obj, propName, value, result);
  338. }
  339. (*jenv)->DeleteLocalRef(jenv, propName);
  340. pval_destructor(&property->element);
  341. return presult;
  342. }
  343. pval java_get_property_handler
  344. (zend_property_reference *property_reference)
  345. {
  346. pval presult = _java_getset_property(property_reference, 0);
  347. checkError(&presult);
  348. return presult;
  349. }
  350. int java_set_property_handler
  351. (zend_property_reference *property_reference, pval *value)
  352. {
  353. pval presult = _java_getset_property
  354. (property_reference, _java_makeArray(1, &value));
  355. return checkError(&presult) ? FAILURE : SUCCESS;
  356. }
  357. /***************************************************************************/
  358. static void _php_java_destructor(void *jobject) {
  359. if (jenv) (*jenv)->DeleteGlobalRef(jenv, jobject);
  360. }
  361. PHP_MINIT_FUNCTION(java) {
  362. INIT_OVERLOADED_CLASS_ENTRY(java_class_entry, "java", NULL,
  363. java_call_function_handler,
  364. java_get_property_handler,
  365. java_set_property_handler);
  366. zend_register_internal_class(&java_class_entry);
  367. le_jobject = register_list_destructors(_php_java_destructor,NULL);
  368. REGISTER_INI_ENTRIES();
  369. if (!classpath) classpath = getenv("CLASSPATH");
  370. if (!libpath) {
  371. PLS_FETCH();
  372. libpath=PG(extension_dir);
  373. }
  374. return SUCCESS;
  375. }
  376. PHP_MSHUTDOWN_FUNCTION(java) {
  377. UNREGISTER_INI_ENTRIES();
  378. if (jvm) jvm_destroy();
  379. return SUCCESS;
  380. }
  381. function_entry java_functions[] = {
  382. {NULL, NULL, NULL}
  383. };
  384. static PHP_MINFO_FUNCTION(java) {
  385. DISPLAY_INI_ENTRIES();
  386. }
  387. zend_module_entry java_module_entry = {
  388. "java",
  389. java_functions,
  390. PHP_MINIT(java),
  391. PHP_MSHUTDOWN(java),
  392. NULL,
  393. NULL,
  394. PHP_MINFO(java),
  395. STANDARD_MODULE_PROPERTIES
  396. };
  397. ZEND_GET_MODULE(java)
  398. /***************************************************************************/
  399. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromString
  400. (JNIEnv *jenv, jclass self, jlong result, jbyteArray jvalue)
  401. {
  402. jboolean isCopy;
  403. jbyte *value = (*jenv)->GetByteArrayElements(jenv, jvalue, &isCopy);
  404. pval *presult = (pval*)(long)result;
  405. presult->type=IS_STRING;
  406. presult->value.str.len=(*jenv)->GetArrayLength(jenv, jvalue);
  407. presult->value.str.val=emalloc(presult->value.str.len+1);
  408. strcpy(presult->value.str.val, value);
  409. if (isCopy) (*jenv)->ReleaseByteArrayElements(jenv, jvalue, value, 0);
  410. }
  411. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromLong
  412. (JNIEnv *jenv, jclass self, jlong result, jlong value)
  413. {
  414. pval *presult = (pval*)(long)result;
  415. presult->type=IS_LONG;
  416. presult->value.lval=(long)value;
  417. }
  418. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromDouble
  419. (JNIEnv *jenv, jclass self, jlong result, jdouble value)
  420. {
  421. pval *presult = (pval*)(long)result;
  422. presult->type=IS_DOUBLE;
  423. presult->value.dval=value;
  424. }
  425. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromBoolean
  426. (JNIEnv *jenv, jclass self, jlong result, jboolean value)
  427. {
  428. pval *presult = (pval*)(long)result;
  429. presult->type=IS_BOOL;
  430. presult->value.lval=value;
  431. }
  432. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromObject
  433. (JNIEnv *jenv, jclass self, jlong result, jobject value)
  434. {
  435. /* wrapper the java object in a pval object */
  436. pval *presult = (pval*)(long)result;
  437. pval *handle;
  438. if (presult->type != IS_OBJECT) {
  439. presult->type=IS_OBJECT;
  440. presult->value.obj.ce=&java_class_entry;
  441. presult->value.obj.properties = (HashTable *) emalloc(sizeof(HashTable));
  442. presult->is_ref=1;
  443. presult->refcount=1;
  444. zend_hash_init(presult->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
  445. };
  446. ALLOC_ZVAL(handle);
  447. handle->type = IS_LONG;
  448. handle->value.lval =
  449. zend_list_insert((*jenv)->NewGlobalRef(jenv,value), le_jobject);
  450. pval_copy_constructor(handle);
  451. INIT_PZVAL(handle);
  452. zend_hash_index_update(presult->value.obj.properties, 0,
  453. &handle, sizeof(pval *), NULL);
  454. }
  455. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromArray
  456. (JNIEnv *jenv, jclass self, jlong result)
  457. {
  458. array_init( (pval*)(long)result );
  459. }
  460. JNIEXPORT jlong JNICALL Java_net_php_reflect_nextElement
  461. (JNIEnv *jenv, jclass self, jlong array)
  462. {
  463. pval *result;
  464. pval *handle = (pval*)(long)array;
  465. ALLOC_ZVAL(result);
  466. zend_hash_next_index_insert(handle->value.ht, &result, sizeof(zval *), NULL);
  467. return (jlong)(long)result;
  468. }
  469. JNIEXPORT void JNICALL Java_net_php_reflect_setException
  470. (JNIEnv *jenv, jclass self, jlong result, jstring value)
  471. {
  472. pval *presult = (pval*)(long)result;
  473. Java_net_php_reflect_setResultFromString(jenv, self, result, value);
  474. presult->type=IS_EXCEPTION;
  475. }
  476. JNIEXPORT void JNICALL Java_net_php_reflect_setEnv
  477. (JNIEnv *newJenv, jclass self)
  478. {
  479. iniUpdated=0;
  480. jenv=newJenv;
  481. if (!self) self = (*jenv)->FindClass(jenv, "net/php/reflect");
  482. php_reflect = (*jenv)->NewGlobalRef(jenv, self);
  483. }