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.

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