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.

594 lines
16 KiB

  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.01 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)->NewStringUTF(jenv,argv[i]->value.str.val);
  217. break;
  218. case IS_OBJECT:
  219. zend_hash_index_find(argv[i]->value.obj.properties, 0, (void*)&handle);
  220. arg = zend_list_find((*handle)->value.lval, &type);
  221. break;
  222. case IS_BOOL:
  223. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  224. "(Z)Ljava/lang/Object;");
  225. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  226. (jboolean)(argv[i]->value.lval));
  227. break;
  228. case IS_LONG:
  229. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  230. "(J)Ljava/lang/Object;");
  231. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  232. (jlong)(argv[i]->value.lval));
  233. break;
  234. case IS_DOUBLE:
  235. makeArg = (*jenv)->GetStaticMethodID(jenv, php_reflect, "MakeArg",
  236. "(D)Ljava/lang/Object;");
  237. arg = (*jenv)->CallStaticObjectMethod(jenv, php_reflect, makeArg,
  238. (jdouble)(argv[i]->value.dval));
  239. break;
  240. default:
  241. arg=0;
  242. }
  243. (*jenv)->SetObjectArrayElement(jenv, result, i, arg);
  244. if (argv[i]->type != IS_OBJECT)
  245. (*jenv)->DeleteLocalRef(jenv, arg);
  246. }
  247. return result;
  248. }
  249. static int checkError(pval *value) {
  250. if (value->type == IS_EXCEPTION) {
  251. php_error(E_WARNING, "%s", value->value.str.val);
  252. efree(value->value.str.val);
  253. var_reset(value);
  254. return 1;
  255. };
  256. return 0;
  257. }
  258. /***************************************************************************/
  259. /*
  260. * Invoke a method on an object. If method name is "java", create a new
  261. * object instead.
  262. */
  263. void java_call_function_handler
  264. (INTERNAL_FUNCTION_PARAMETERS, zend_property_reference *property_reference)
  265. {
  266. pval *object = property_reference->object;
  267. zend_overloaded_element *function_name = (zend_overloaded_element *)
  268. property_reference->elements_list->tail->data;
  269. int arg_count = ARG_COUNT(ht);
  270. jlong result = 0;
  271. pval **arguments = (pval **) emalloc(sizeof(pval *)*arg_count);
  272. getParametersArray(ht, arg_count, arguments);
  273. if (iniUpdated && jenv) jvm_destroy();
  274. if (!jenv) jvm_create();
  275. if (!jenv) return;
  276. if (!strcmp("java",function_name->element.value.str.val)) {
  277. /* construct a Java object:
  278. First argument is the class name. Any additional arguments will
  279. be treated as constructor parameters. */
  280. jmethodID co = (*jenv)->GetStaticMethodID(jenv, php_reflect, "CreateObject",
  281. "(Ljava/lang/String;[Ljava/lang/Object;J)V");
  282. jstring className=(*jenv)->NewStringUTF(jenv, arguments[0]->value.str.val);
  283. (pval*)(long)result = object;
  284. (*jenv)->CallStaticVoidMethod(jenv, php_reflect, co,
  285. className, _java_makeArray(arg_count-1, arguments+1), result);
  286. (*jenv)->DeleteLocalRef(jenv, className);
  287. } else {
  288. pval **handle;
  289. int type;
  290. jobject obj;
  291. jstring method;
  292. /* invoke a method on the given object */
  293. jmethodID invoke = (*jenv)->GetStaticMethodID(jenv, php_reflect, "Invoke",
  294. "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;J)V");
  295. zend_hash_index_find(object->value.obj.properties, 0, (void**) &handle);
  296. obj = zend_list_find((*handle)->value.lval, &type);
  297. method = (*jenv)->NewStringUTF(jenv, function_name->element.value.str.val);
  298. (pval*)(long)result = return_value;
  299. (*jenv)->CallStaticVoidMethod(jenv, php_reflect, invoke,
  300. obj, method, _java_makeArray(arg_count, arguments), result);
  301. (*jenv)->DeleteLocalRef(jenv, method);
  302. }
  303. efree(arguments);
  304. pval_destructor(&function_name->element);
  305. checkError((pval*)(long)result);
  306. }
  307. /***************************************************************************/
  308. static pval _java_getset_property
  309. (zend_property_reference *property_reference, jobjectArray value)
  310. {
  311. pval presult;
  312. jlong result = 0;
  313. pval **pobject;
  314. jobject obj;
  315. int type;
  316. /* get the property name */
  317. zend_llist_element *element = property_reference->elements_list->head;
  318. zend_overloaded_element *property=(zend_overloaded_element *)element->data;
  319. jstring propName =
  320. (*jenv)->NewStringUTF(jenv, property->element.value.str.val);
  321. /* get the object */
  322. zend_hash_index_find(property_reference->object->value.obj.properties,
  323. 0, (void **) &pobject);
  324. obj = zend_list_find((*pobject)->value.lval,&type);
  325. (pval*)(long)result = &presult;
  326. var_uninit(&presult);
  327. if (!obj || (type!=le_jobject)) {
  328. php_error(E_ERROR,
  329. "Attempt to access a Java property on a non-Java object");
  330. } else {
  331. /* invoke the method */
  332. jmethodID gsp = (*jenv)->GetStaticMethodID(jenv, php_reflect, "GetSetProp",
  333. "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;J)V");
  334. (*jenv)->CallStaticVoidMethod
  335. (jenv, php_reflect, gsp, obj, propName, value, result);
  336. }
  337. (*jenv)->DeleteLocalRef(jenv, propName);
  338. pval_destructor(&property->element);
  339. return presult;
  340. }
  341. pval java_get_property_handler
  342. (zend_property_reference *property_reference)
  343. {
  344. pval presult = _java_getset_property(property_reference, 0);
  345. checkError(&presult);
  346. return presult;
  347. }
  348. int java_set_property_handler
  349. (zend_property_reference *property_reference, pval *value)
  350. {
  351. pval presult = _java_getset_property
  352. (property_reference, _java_makeArray(1, &value));
  353. return checkError(&presult) ? FAILURE : SUCCESS;
  354. }
  355. /***************************************************************************/
  356. static void _php_java_destructor(void *jobject) {
  357. if (jenv) (*jenv)->DeleteGlobalRef(jenv, jobject);
  358. }
  359. PHP_MINIT_FUNCTION(java) {
  360. INIT_OVERLOADED_CLASS_ENTRY(java_class_entry, "java", NULL,
  361. java_call_function_handler,
  362. java_get_property_handler,
  363. java_set_property_handler);
  364. register_internal_class(&java_class_entry);
  365. le_jobject = register_list_destructors(_php_java_destructor,NULL);
  366. REGISTER_INI_ENTRIES();
  367. if (!classpath) classpath = getenv("CLASSPATH");
  368. if (!libpath) {
  369. PLS_FETCH();
  370. libpath=PG(extension_dir);
  371. }
  372. return SUCCESS;
  373. }
  374. PHP_MSHUTDOWN_FUNCTION(java) {
  375. UNREGISTER_INI_ENTRIES();
  376. if (jvm) jvm_destroy();
  377. return SUCCESS;
  378. }
  379. function_entry java_functions[] = {
  380. {NULL, NULL, NULL}
  381. };
  382. static PHP_MINFO_FUNCTION(java) {
  383. DISPLAY_INI_ENTRIES();
  384. }
  385. zend_module_entry java_module_entry = {
  386. "java",
  387. java_functions,
  388. PHP_MINIT(java),
  389. PHP_MSHUTDOWN(java),
  390. NULL,
  391. NULL,
  392. PHP_MINFO(java),
  393. STANDARD_MODULE_PROPERTIES
  394. };
  395. DLEXPORT zend_module_entry *get_module(void) { return &java_module_entry; }
  396. /***************************************************************************/
  397. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromString
  398. (JNIEnv *jenv, jclass self, jlong result, jstring value)
  399. {
  400. jboolean isCopy;
  401. const char *valueAsUTF = (*jenv)->GetStringUTFChars(jenv, value, &isCopy);
  402. pval *presult = (pval*)(long)result;
  403. presult->type=IS_STRING;
  404. presult->value.str.len=strlen(valueAsUTF);
  405. presult->value.str.val=emalloc(presult->value.str.len+1);
  406. strcpy(presult->value.str.val, valueAsUTF);
  407. if (isCopy) (*jenv)->ReleaseStringUTFChars(jenv, value, valueAsUTF);
  408. }
  409. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromLong
  410. (JNIEnv *jenv, jclass self, jlong result, jlong value)
  411. {
  412. pval *presult = (pval*)(long)result;
  413. presult->type=IS_LONG;
  414. presult->value.lval=(long)value;
  415. }
  416. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromDouble
  417. (JNIEnv *jenv, jclass self, jlong result, jdouble value)
  418. {
  419. pval *presult = (pval*)(long)result;
  420. presult->type=IS_DOUBLE;
  421. presult->value.dval=value;
  422. }
  423. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromBoolean
  424. (JNIEnv *jenv, jclass self, jlong result, jboolean value)
  425. {
  426. pval *presult = (pval*)(long)result;
  427. presult->type=IS_BOOL;
  428. presult->value.lval=value;
  429. }
  430. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromObject
  431. (JNIEnv *jenv, jclass self, jlong result, jobject value)
  432. {
  433. /* wrapper the java object in a pval object */
  434. pval *presult = (pval*)(long)result;
  435. pval *handle;
  436. if (presult->type != IS_OBJECT) {
  437. presult->type=IS_OBJECT;
  438. presult->value.obj.ce=&java_class_entry;
  439. presult->value.obj.properties = (HashTable *) emalloc(sizeof(HashTable));
  440. presult->is_ref=1;
  441. presult->refcount=1;
  442. zend_hash_init(presult->value.obj.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
  443. };
  444. ALLOC_ZVAL(handle);
  445. handle->type = IS_LONG;
  446. handle->value.lval =
  447. zend_list_insert((*jenv)->NewGlobalRef(jenv,value), le_jobject);
  448. pval_copy_constructor(handle);
  449. INIT_PZVAL(handle);
  450. zend_hash_index_update(presult->value.obj.properties, 0,
  451. &handle, sizeof(pval *), NULL);
  452. }
  453. JNIEXPORT void JNICALL Java_net_php_reflect_setResultFromArray
  454. (JNIEnv *jenv, jclass self, jlong result)
  455. {
  456. array_init( (pval*)(long)result );
  457. }
  458. JNIEXPORT jlong JNICALL Java_net_php_reflect_nextElement
  459. (JNIEnv *jenv, jclass self, jlong array)
  460. {
  461. pval *result;
  462. pval *handle = (pval*)(long)array;
  463. ALLOC_ZVAL(result);
  464. zend_hash_next_index_insert(handle->value.ht, &result, sizeof(zval *), NULL);
  465. return (jlong)(long)result;
  466. }
  467. JNIEXPORT void JNICALL Java_net_php_reflect_setException
  468. (JNIEnv *jenv, jclass self, jlong result, jstring value)
  469. {
  470. pval *presult = (pval*)(long)result;
  471. Java_net_php_reflect_setResultFromString(jenv, self, result, value);
  472. presult->type=IS_EXCEPTION;
  473. }
  474. JNIEXPORT void JNICALL Java_net_php_reflect_setEnv
  475. (JNIEnv *newJenv, jclass self)
  476. {
  477. iniUpdated=0;
  478. jenv=newJenv;
  479. if (!self) self = (*jenv)->FindClass(jenv, "net/php/reflect");
  480. php_reflect = (*jenv)->NewGlobalRef(jenv, self);
  481. }