php_vm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/extconf.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "mkmf"
2
+
3
+ if !have_library('php5', 'php_embed_init')
4
+ exit(1)
5
+ end
6
+
7
+ $CFLAGS += " "+`php-config --includes`
8
+ $LOCAL_LIBS += ""
9
+ create_makefile("php_vm")
data/ext/php_vm.c ADDED
@@ -0,0 +1,567 @@
1
+ #include "php_vm.h"
2
+ #include "php_vm_z2v.h"
3
+ #include "php_vm_v2z.h"
4
+ #include <string.h>
5
+
6
+
7
+ // global
8
+
9
+ VALUE rb_mPHPVM;
10
+ VALUE rb_cPHPClass;
11
+ VALUE rb_cPHPObject;
12
+ VALUE rb_ePHPError;
13
+ VALUE rb_ePHPExceptionObject;
14
+ VALUE rb_ePHPSyntaxError;
15
+
16
+
17
+ // PHP
18
+
19
+ void php_eval_string(char *code, int code_len TSRMLS_DC)
20
+ {
21
+ int syntax_error = 0;
22
+
23
+ // eval
24
+ zend_try {
25
+ if (zend_eval_stringl(code, code_len, NULL, "php_vm" TSRMLS_CC)==FAILURE) {
26
+ syntax_error = 1;
27
+ }
28
+ } zend_end_try();
29
+
30
+ // syntax error
31
+ if (syntax_error) {
32
+ VALUE exception = rb_exc_new2(rb_ePHPSyntaxError, "Syntax error");
33
+ rb_exc_raise(exception);
34
+ }
35
+
36
+ // exception
37
+ if (EG(exception)) {
38
+ VALUE exception = zval_to_value(EG(exception));
39
+ EG(exception) = NULL;
40
+ rb_exc_raise(exception);
41
+ }
42
+
43
+ // exit
44
+ if (EG(exit_status)!=0) {
45
+ int exit_status = EG(exit_status);
46
+ EG(exit_status) = 0;
47
+
48
+ char message[32];
49
+ sprintf(message, "exit status error: %d", exit_status);
50
+
51
+ VALUE exception = rb_exc_new2(rb_ePHPError, message);
52
+ rb_exc_raise(exception);
53
+ }
54
+ }
55
+
56
+ void find_zend_class_entry(char *name, int name_len, zend_class_entry ***ce)
57
+ {
58
+ // lowercase
59
+ char *lcname = malloc(name_len+1);
60
+ memcpy(lcname, name, name_len);
61
+ zend_str_tolower(lcname, name_len);
62
+ lcname[name_len] = '\0';
63
+
64
+ // find zend class
65
+ *ce = NULL;
66
+ zend_hash_find(CG(class_table), lcname, name_len+1, (void **)ce);
67
+ if (*ce) {
68
+ // check string case
69
+ if (strcmp(name, (**ce)->name)!=0) {
70
+ *ce = NULL;
71
+ }
72
+ }
73
+
74
+ free(lcname);
75
+ }
76
+
77
+ int is_exception_zend_class_entry(zend_class_entry *ce TSRMLS_DC)
78
+ {
79
+ return instanceof_function(ce, zend_exception_get_default() TSRMLS_CC);
80
+ }
81
+
82
+ int is_exception_zval(zval *z TSRMLS_DC)
83
+ {
84
+ return is_exception_zend_class_entry(Z_OBJCE_P(z) TSRMLS_CC);
85
+ }
86
+
87
+ void find_zend_function(zend_class_entry *ce, char *name, int name_len, zend_function **mptr)
88
+ {
89
+ // function_table
90
+ HashTable *function_table = NULL;
91
+ if (ce && &ce->function_table) {
92
+ function_table = &ce->function_table;
93
+ } else {
94
+ function_table = EG(function_table);
95
+ }
96
+
97
+ // lowercase
98
+ char *lcname = malloc(name_len+1);
99
+ memcpy(lcname, name, name_len);
100
+ zend_str_tolower(lcname, name_len);
101
+ lcname[name_len] = '\0';
102
+
103
+ // find zend function
104
+ *mptr = NULL;
105
+ zend_hash_find(function_table, lcname, name_len+1, (void **)mptr);
106
+ if (*mptr) {
107
+ // check string case
108
+ if (strcmp(name, (*mptr)->common.function_name)!=0) {
109
+ *mptr = NULL;
110
+ }
111
+ }
112
+
113
+ free(lcname);
114
+ }
115
+
116
+ int new_php_object(zend_class_entry *ce, VALUE v_args, zval *retval)
117
+ {
118
+ int result = FAILURE;
119
+
120
+ if (ce->constructor) {
121
+ // defined constructor
122
+ /*
123
+ if (!(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) {
124
+ char *message = malloc(50+strlen(ce->name));
125
+ sprintf(message, "Access to non-public constructor of class %s", ce->name);
126
+ VALUE v_exception = rb_exc_new2(rb_ePHPError, message);
127
+ free(message);
128
+ rb_exc_raise(v_exception);
129
+ }
130
+ */
131
+
132
+ // alloc
133
+ object_init_ex(retval, ce);
134
+
135
+ // call
136
+ int result = call_php_method(ce, retval, ce->constructor, RARRAY_LEN(v_args), RARRAY_PTR(v_args), &retval TSRMLS_CC);
137
+
138
+ // error
139
+ if (result==FAILURE) {
140
+ char *message = malloc(40+strlen(ce->name));
141
+ sprintf(message, "Invocation of %s's constructor failed", ce->name);
142
+ VALUE v_exception = rb_exc_new2(rb_ePHPError, message);
143
+ free(message);
144
+ rb_exc_raise(v_exception);
145
+ }
146
+
147
+ } else if (!RARRAY_LEN(v_args)) {
148
+ // undefined constructor, hasnt args
149
+ object_init_ex(retval, ce);
150
+ result = SUCCESS;
151
+
152
+ } else {
153
+ // undefine constructor, has args
154
+ char *message = malloc(90+strlen(ce->name));
155
+ sprintf(message, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ce->name);
156
+ VALUE v_exception = rb_exc_new2(rb_ePHPError, message);
157
+ free(message);
158
+ rb_exc_raise(v_exception);
159
+ }
160
+ return result;
161
+ }
162
+
163
+ void define_php_methods(VALUE v_obj, zend_class_entry *ce, int is_static)
164
+ {
165
+ // TODO: access modifier
166
+ // TODO: __toString
167
+ // TODO: __clone
168
+ // TODO: __call
169
+ // TODO: __callStatic
170
+ // TODO: __get
171
+ // TODO: __set
172
+ // TODO: __isset
173
+
174
+ HashPosition pos;
175
+ zend_function *mptr;
176
+
177
+ zend_hash_internal_pointer_reset_ex(&ce->function_table, &pos);
178
+
179
+ while (zend_hash_get_current_data_ex(&ce->function_table, (void **)&mptr, &pos) == SUCCESS) {
180
+ int flag = mptr->common.fn_flags;
181
+ const char *fname = mptr->common.function_name;
182
+
183
+ if (is_static) {
184
+ // class method
185
+ if (strcmp("new", fname)==0) {
186
+ // new => no define
187
+ } else if (0<(flag & ZEND_ACC_STATIC)) {
188
+ // other method
189
+ rb_define_singleton_method(v_obj, fname, rb_php_class_call, -1);
190
+ }
191
+ } else {
192
+ // instance method
193
+ if (strcmp("__construct", fname)==0) {
194
+ // __construct => no define
195
+
196
+ } else if (0==(flag & ZEND_ACC_STATIC)) {
197
+ // other method
198
+ rb_define_singleton_method(v_obj, fname, rb_php_object_call, -1);
199
+ }
200
+ }
201
+
202
+ zend_hash_move_forward_ex(&ce->function_table, &pos);
203
+ }
204
+ }
205
+
206
+ int call_php_method(zend_class_entry *ce, zval *obj, zend_function *mptr, int argc, VALUE *v_argv, zval **retval_ptr TSRMLS_DC)
207
+ {
208
+ int result = FAILURE;
209
+
210
+ // argv
211
+ zval ***z_argv = malloc(sizeof(zval **) * argc);
212
+ long i;
213
+ for (i=0; i<argc; i++) {
214
+ zval *tmp;
215
+ MAKE_STD_ZVAL(tmp);
216
+ value_to_zval(v_argv[i], tmp);
217
+ z_argv[i] = &tmp;
218
+ }
219
+
220
+ // call info
221
+ zend_fcall_info fci;
222
+ zend_fcall_info_cache fcc;
223
+ zval *return_value;
224
+ ALLOC_INIT_ZVAL(return_value);
225
+
226
+ fci.size = sizeof(fci);
227
+ fci.function_table = NULL;
228
+ fci.function_name = NULL;
229
+ fci.symbol_table = NULL;
230
+ fci.object_ptr = obj;
231
+ fci.retval_ptr_ptr = retval_ptr;
232
+ fci.param_count = argc;
233
+ fci.params = z_argv;
234
+ fci.no_separation = 1;
235
+
236
+ fcc.initialized = 1;
237
+ fcc.function_handler = mptr;
238
+ fcc.calling_scope = ce ? ce : EG(scope);
239
+ fcc.called_scope = ce ? ce : NULL;
240
+ fcc.object_ptr = obj;
241
+
242
+ // call
243
+ zend_try {
244
+ result = zend_call_function(&fci, &fcc TSRMLS_CC);
245
+ } zend_catch {
246
+ //printf("call_php_method exception: %p\n", EG(exception));
247
+ } zend_end_try();
248
+
249
+ // release
250
+ for (i=0; i<argc; i++) {
251
+ zval_ptr_dtor(z_argv[i]);
252
+ }
253
+ free(z_argv);
254
+
255
+ return result;
256
+ }
257
+
258
+
259
+ // Ruby
260
+
261
+ VALUE get_callee_name()
262
+ {
263
+ VALUE backtrace_arr = rb_funcall(rb_mKernel, rb_intern("caller"), 1, INT2NUM(0));
264
+ if (backtrace_arr) {
265
+ VALUE backtrace = rb_funcall(backtrace_arr, rb_intern("first"), 0);
266
+ if (backtrace) {
267
+ VALUE re = rb_funcall(rb_cRegexp, rb_intern("new"), 1, rb_str_new2("^(.+?):(\\d+)(?::in `(.*)')?"));
268
+ VALUE m = rb_funcall(backtrace, rb_intern("match"), 1, re);
269
+ if (m) {
270
+ return rb_funcall(m, rb_intern("[]"), 1, INT2NUM(3));
271
+ }
272
+ }
273
+ }
274
+ return Qnil;
275
+ }
276
+
277
+ VALUE call_php_method_bridge(zend_class_entry *ce, zval *obj, VALUE callee, int argc, VALUE *argv)
278
+ {
279
+ // callee
280
+ if (callee==Qnil) {
281
+ VALUE exception = rb_exc_new2(rb_ePHPError, "callee is nil");
282
+ rb_exc_raise(exception);
283
+ }
284
+
285
+ // method
286
+ zend_function *mptr;
287
+ find_zend_function(ce, RSTRING_PTR(callee), RSTRING_LEN(callee), &mptr);
288
+
289
+ // call
290
+ zval *retval;
291
+ int result = call_php_method(ce, obj, mptr, argc, argv, &retval TSRMLS_CC);
292
+
293
+ // exception
294
+ if (result==FAILURE) {
295
+ // TODO: read var
296
+ // TODO: raise exception. method missing
297
+ }
298
+
299
+ return zval_to_value(retval);
300
+ }
301
+
302
+
303
+ // PHP Native resource
304
+
305
+ void php_native_resource_delete(PHPNativeResource *p)
306
+ {
307
+ free(p);
308
+ }
309
+
310
+ zend_class_entry* get_zend_class_entry(VALUE self)
311
+ {
312
+ VALUE resource = rb_iv_get(self, "php_native_resource");
313
+ if (resource==Qnil) {
314
+ return NULL;
315
+ }
316
+
317
+ PHPNativeResource *p;
318
+ Data_Get_Struct(rb_iv_get(self, "php_native_resource"), PHPNativeResource, p);
319
+ if (p) {
320
+ return p->ce;
321
+ }
322
+ return NULL;
323
+ }
324
+
325
+ zval* get_zval(VALUE self)
326
+ {
327
+ VALUE resource = rb_iv_get(self, "php_native_resource");
328
+ if (resource==Qnil) {
329
+ return NULL;
330
+ }
331
+
332
+ PHPNativeResource *p;
333
+ Data_Get_Struct(rb_iv_get(self, "php_native_resource"), PHPNativeResource, p);
334
+ if (p) {
335
+ return p->zobj;
336
+ }
337
+ return NULL;
338
+ }
339
+
340
+
341
+ // module PHPVM
342
+
343
+ VALUE rb_php_vm_require(VALUE cls, VALUE filepath)
344
+ {
345
+ StringValue(filepath);
346
+ filepath = rb_funcall(filepath, rb_intern("gsub"), 2, rb_str_new2("\\"), rb_str_new2("\\\\"));
347
+ filepath = rb_funcall(filepath, rb_intern("gsub"), 2, rb_str_new2("\""), rb_str_new2("\\\""));
348
+
349
+ VALUE code = rb_str_new2("require \"");
350
+ code = rb_str_plus(code, filepath);
351
+ code = rb_str_plus(code, rb_str_new2("\";"));
352
+
353
+ php_eval_string(RSTRING_PTR(code), RSTRING_LEN(code));
354
+
355
+ return Qnil;
356
+ }
357
+
358
+ VALUE rb_php_vm_require_once(VALUE cls, VALUE filepath)
359
+ {
360
+ StringValue(filepath);
361
+ filepath = rb_funcall(filepath, rb_intern("gsub"), 2, rb_str_new2("\\"), rb_str_new2("\\\\"));
362
+ filepath = rb_funcall(filepath, rb_intern("gsub"), 2, rb_str_new2("\""), rb_str_new2("\\\""));
363
+
364
+ VALUE code = rb_str_new2("require_once \"");
365
+ code = rb_str_plus(code, filepath);
366
+ code = rb_str_plus(code, rb_str_new2("\";"));
367
+
368
+ php_eval_string(RSTRING_PTR(code), RSTRING_LEN(code));
369
+
370
+ return Qnil;
371
+ }
372
+
373
+ VALUE rb_php_vm_exec(VALUE cls, VALUE code)
374
+ {
375
+ php_eval_string(RSTRING_PTR(code), RSTRING_LEN(code) TSRMLS_CC);
376
+ return Qnil;
377
+ }
378
+
379
+ VALUE rb_php_vm_getClass(VALUE cls, VALUE v_class_name)
380
+ {
381
+ return rb_php_class_get(rb_cPHPClass, v_class_name);
382
+ }
383
+
384
+
385
+ // class PHPVM::PHPClass
386
+
387
+ VALUE rb_php_class_get(VALUE cls, VALUE v_name)
388
+ {
389
+ // find
390
+ VALUE name_sym = rb_str_intern(v_name);
391
+ VALUE classes = rb_cv_get(cls, "@@classes");
392
+ VALUE class = rb_hash_lookup(classes, name_sym);
393
+
394
+ if (class==Qnil) {
395
+ // create
396
+ class = rb_obj_alloc(rb_cPHPClass);
397
+ rb_php_class_initialize(class, v_name);
398
+ rb_hash_aset(classes, name_sym, class);
399
+ }
400
+
401
+ return class;
402
+ }
403
+
404
+ VALUE rb_php_class_initialize(VALUE self, VALUE v_name)
405
+ {
406
+ rb_iv_set(self, "name", v_name);
407
+
408
+ // find zend class
409
+ zend_class_entry **ce = NULL;
410
+ find_zend_class_entry(RSTRING_PTR(v_name), RSTRING_LEN(v_name), &ce);
411
+
412
+ // class not found
413
+ if (!ce) {
414
+ char *message = malloc(32+RSTRING_LEN(v_name));
415
+ sprintf(message, "Class is not found: %s", RSTRING_PTR(v_name));
416
+ VALUE exception = rb_exc_new2(rb_ePHPError, message);
417
+ free(message);
418
+ rb_exc_raise(exception);
419
+ }
420
+
421
+ // set resource
422
+ PHPNativeResource *p = ALLOC(PHPNativeResource);
423
+ p->ce = *ce;
424
+ VALUE resource = Data_Wrap_Struct(CLASS_OF(self), 0, php_native_resource_delete, p);
425
+ rb_iv_set(self, "php_native_resource", resource);
426
+
427
+ // define php static methods
428
+ define_php_methods(self, *ce, 1);
429
+
430
+ return self;
431
+ }
432
+
433
+ VALUE rb_php_class_name(VALUE self)
434
+ {
435
+ return rb_iv_get(self, "name");
436
+ }
437
+
438
+ VALUE rb_php_class_new(int argc, VALUE *argv, VALUE self)
439
+ {
440
+ VALUE args;
441
+ rb_scan_args(argc, argv, "*", &args);
442
+
443
+ VALUE obj = Qnil;
444
+ zend_class_entry *ce = get_zend_class_entry(self);
445
+ if (is_exception_zend_class_entry(ce)) {
446
+ obj = rb_obj_alloc(rb_ePHPExceptionObject);
447
+ } else {
448
+ obj = rb_obj_alloc(rb_cPHPObject);
449
+ }
450
+ rb_php_object_initialize(obj, self, args);
451
+
452
+ // define php instance method
453
+ define_php_methods(obj, ce, 0);
454
+
455
+ return obj;
456
+ }
457
+
458
+ VALUE rb_php_class_call(int argc, VALUE *argv, VALUE self)
459
+ {
460
+ zend_class_entry *ce = get_zend_class_entry(self);
461
+ VALUE callee = get_callee_name();
462
+ return call_php_method_bridge(ce, NULL, callee, argc, argv);
463
+ }
464
+
465
+
466
+ // class PHPVM::PHPObject
467
+
468
+ VALUE rb_php_object_initialize(VALUE self, VALUE class, VALUE args)
469
+ {
470
+ // set class
471
+ rb_iv_set(self, "php_class", class);
472
+
473
+ // create php object
474
+ zend_class_entry *ce = get_zend_class_entry(class);
475
+ zval *z_obj;
476
+ ALLOC_INIT_ZVAL(z_obj);
477
+ new_php_object(ce, args, z_obj);
478
+
479
+ // set resource
480
+ PHPNativeResource *p = ALLOC(PHPNativeResource);
481
+ p->ce = ce;
482
+ p->zobj = z_obj;
483
+ VALUE resource = Data_Wrap_Struct(CLASS_OF(self), 0, php_native_resource_delete, p);
484
+ rb_iv_set(self, "php_native_resource", resource);
485
+
486
+ return self;
487
+ }
488
+
489
+ VALUE rb_php_object_php_class(VALUE self)
490
+ {
491
+ return rb_iv_get(self, "php_class");
492
+ }
493
+
494
+ VALUE rb_php_object_call(int argc, VALUE *argv, VALUE self)
495
+ {
496
+ zend_class_entry *ce = get_zend_class_entry(self);
497
+ zval *zobj = get_zval(self);
498
+ VALUE callee = get_callee_name();
499
+ return call_php_method_bridge(ce, zobj, callee, argc, argv);
500
+ }
501
+
502
+
503
+ // class PHPVM::PHPExceptionObject
504
+
505
+ VALUE rb_php_exception_object_initialize(int argc, VALUE *argv, VALUE self)
506
+ {
507
+ rb_call_super(argc, argv);
508
+ return self;
509
+ }
510
+
511
+
512
+ // module
513
+
514
+ void php_vm_module_init()
515
+ {
516
+ int argc = 1;
517
+ char *argv[2] = {"php_vm", NULL};
518
+ php_embed_init(argc, argv PTSRMLS_CC);
519
+ EG(bailout) = NULL;
520
+ }
521
+
522
+ void php_vm_module_exit()
523
+ {
524
+ php_embed_shutdown(TSRMLS_C);
525
+ }
526
+
527
+ void Init_php_vm()
528
+ {
529
+ // initialize php_vm
530
+ php_vm_module_init();
531
+ atexit(php_vm_module_exit);
532
+
533
+ // module PHPVM
534
+ rb_mPHPVM = rb_define_module("PHPVM");
535
+
536
+ rb_define_singleton_method(rb_mPHPVM, "require", rb_php_vm_require, 1);
537
+ rb_define_singleton_method(rb_mPHPVM, "require_once", rb_php_vm_require_once, 1);
538
+ rb_define_singleton_method(rb_mPHPVM, "exec", rb_php_vm_exec, 1);
539
+ rb_define_singleton_method(rb_mPHPVM, "getClass", rb_php_vm_getClass, 1);
540
+
541
+ // class PHPVM::PHPClass
542
+ rb_cPHPClass = rb_define_class_under(rb_mPHPVM, "PHPClass", rb_cObject);
543
+ rb_define_class_variable(rb_cPHPClass, "@@classes", rb_obj_alloc(rb_cHash));
544
+
545
+ rb_define_private_method(rb_cPHPClass, "initialize", rb_php_class_initialize, 1);
546
+ rb_define_method(rb_cPHPClass, "name", rb_php_class_name, 0);
547
+ rb_define_method(rb_cPHPClass, "new", rb_php_class_new, -1);
548
+ rb_define_singleton_method(rb_cPHPClass, "get", rb_php_class_get, 1);
549
+
550
+ // class PHPVM::PHPObject
551
+ rb_cPHPObject = rb_define_class_under(rb_mPHPVM, "PHPObject", rb_cObject);
552
+
553
+ rb_define_private_method(rb_cPHPObject, "initialize", rb_php_object_initialize, 1);
554
+ rb_define_method(rb_cPHPObject, "php_class", rb_php_object_php_class, 0);
555
+
556
+ // class PHPVM::PHPError < StandardError
557
+ rb_ePHPError = rb_define_class_under(rb_mPHPVM, "PHPError", rb_eStandardError);
558
+
559
+ // class PHPVM::PHPExceptionObject < PHPVM::PHPError
560
+ rb_ePHPExceptionObject = rb_define_class_under(rb_mPHPVM, "PHPExceptionObject", rb_ePHPError);
561
+
562
+ rb_define_method(rb_ePHPExceptionObject, "initialize", rb_php_exception_object_initialize, -1);
563
+ rb_define_method(rb_ePHPExceptionObject, "php_class", rb_php_object_php_class, 0);
564
+
565
+ // class PHPVM::PHPSyntaxError < PHPVM::PHPError
566
+ rb_ePHPSyntaxError = rb_define_class_under(rb_mPHPVM, "PHPSyntaxError", rb_ePHPError);
567
+ }
data/ext/php_vm.h ADDED
@@ -0,0 +1,64 @@
1
+ #ifndef PHP_VM_H
2
+ #define PHP_VM_H
3
+
4
+ #include <ruby.h>
5
+ #include <sapi/embed/php_embed.h>
6
+ #include <Zend/zend_execute.h>
7
+ #include <Zend/zend_exceptions.h>
8
+
9
+
10
+ extern VALUE rb_mPHPVM;
11
+ extern VALUE rb_cPHPClass;
12
+ extern VALUE rb_cPHPObject;
13
+ extern VALUE rb_ePHPExceptionObject;
14
+ extern VALUE rb_ePHPError;
15
+ extern VALUE rb_ePHPSyntaxError;
16
+
17
+ typedef struct {
18
+ zend_class_entry *ce;
19
+ zval *zobj;
20
+ } PHPNativeResource;
21
+
22
+
23
+ // PHP
24
+ extern void php_eval_string(char *code, int code_len TSRMLS_DC);
25
+ extern void find_zend_class_entry(char *name, int name_len, zend_class_entry ***ce);
26
+ extern void find_zend_class_entry2(char *name, zend_class_entry ***ce);
27
+ extern int is_exception_zend_class_entry(zend_class_entry *ce TSRMLS_DC);
28
+ extern int is_exception_zval(zval *z TSRMLS_DC);
29
+ extern int new_php_object(zend_class_entry *ce, VALUE v_args, zval *retval);
30
+ extern void define_php_methods(VALUE v_obj, zend_class_entry *ce, int is_static);
31
+ extern int call_php_method(zend_class_entry *ce, zval *obj, zend_function *mptr, int argc, VALUE *v_argv, zval **retval_ptr TSRMLS_DC);
32
+
33
+ // Ruby
34
+ extern VALUE get_callee_name();
35
+ extern VALUE call_php_method_bridge(zend_class_entry *ce, zval *obj, VALUE callee, int argc, VALUE *argv);
36
+
37
+ // PHP Native resource
38
+ extern void php_native_resource_delete(PHPNativeResource *p);
39
+ extern zend_class_entry* get_zend_class_entry(VALUE self);
40
+ extern zval* get_zval(VALUE self);
41
+
42
+ // module PHPVM
43
+ extern VALUE rb_php_vm_require(VALUE cls, VALUE rbv_filepath);
44
+ extern VALUE rb_php_vm_exec(VALUE cls, VALUE rbv_code);
45
+ extern VALUE rb_php_vm_getClass(VALUE cls, VALUE rbv_class_name);
46
+
47
+ // class PHPVM::PHPClass
48
+ extern VALUE rb_php_class_get(VALUE cls, VALUE rbv_name);
49
+ extern VALUE rb_php_class_initialize(VALUE self, VALUE rbv_name);
50
+ extern VALUE rb_php_class_name(VALUE self);
51
+ extern VALUE rb_php_class_new(int argc, VALUE *argv, VALUE self);
52
+ extern VALUE rb_php_class_call(int argc, VALUE *argv, VALUE self);
53
+
54
+ // class PHPVM::PHPObject
55
+ extern VALUE rb_php_object_initialize(VALUE self, VALUE class, VALUE arg_arr);
56
+ extern VALUE rb_php_object_php_class(VALUE self);
57
+ extern VALUE rb_php_object_call(int argc, VALUE *argv, VALUE self);
58
+
59
+ // module
60
+ extern void php_vm_module_init();
61
+ extern void php_vm_module_exit();
62
+ extern void Init_php_vm();
63
+
64
+ #endif
data/ext/php_vm_v2z.c ADDED
@@ -0,0 +1,81 @@
1
+ #include "php_vm.h"
2
+ #include "php_vm_v2z.h"
3
+ #include <string.h>
4
+
5
+
6
+ static void value_to_zval_array(VALUE v, zval *z)
7
+ {
8
+ array_init(z);
9
+
10
+ long i;
11
+ for (i=0; i<RARRAY_LEN(v); i++) {
12
+ zval *new_var;
13
+ MAKE_STD_ZVAL(z);
14
+ value_to_zval(RARRAY_PTR(v)[i], new_var);
15
+
16
+ zend_hash_next_index_insert(Z_ARRVAL_P(z), &new_var, sizeof(zval *), NULL);
17
+ }
18
+ }
19
+
20
+ static void value_to_zval_hash(VALUE v, zval *z)
21
+ {
22
+ array_init(z);
23
+
24
+ v = rb_funcall(v, rb_intern("flatten"), 0);
25
+
26
+ long i;
27
+ for (i=0; i<RARRAY_LEN(v); i+=2) {
28
+ VALUE v_key = RARRAY_PTR(v)[i];
29
+ StringValue(v_key);
30
+
31
+ zval *z_value;
32
+ MAKE_STD_ZVAL(z);
33
+ value_to_zval(RARRAY_PTR(v)[i+1], z_value);
34
+
35
+ add_assoc_zval_ex(z, RSTRING_PTR(v_key), RSTRING_LEN(v), z_value);
36
+ }
37
+ }
38
+
39
+
40
+ void value_to_zval(VALUE v, zval *z)
41
+ {
42
+ switch (TYPE(v)) {
43
+ // nil
44
+ case T_NIL:
45
+ ZVAL_NULL(z);
46
+ break;
47
+ // bool
48
+ case T_TRUE:
49
+ ZVAL_TRUE(z);
50
+ break;
51
+ case T_FALSE:
52
+ ZVAL_FALSE(z);
53
+ break;
54
+ // number
55
+ case T_FIXNUM:
56
+ ZVAL_LONG(z, NUM2LONG(v));
57
+ break;
58
+ case T_FLOAT:
59
+ ZVAL_DOUBLE(z, RFLOAT_VALUE(v));
60
+ break;
61
+ // array
62
+ case T_ARRAY:
63
+ value_to_zval_array(v, z);
64
+ break;
65
+ // hash
66
+ case T_HASH:
67
+ value_to_zval_hash(v, z);
68
+ break;
69
+ // object string
70
+ default:{
71
+ VALUE cls = CLASS_OF(v);
72
+ if (cls==rb_cPHPObject || cls==rb_ePHPExceptionObject) {
73
+ // wrap php object
74
+ } else {
75
+ // other to_s
76
+ StringValue(v);
77
+ ZVAL_STRING(z, RSTRING_PTR(v), 1);
78
+ }
79
+ }
80
+ }
81
+ }
data/ext/php_vm_v2z.h ADDED
@@ -0,0 +1,9 @@
1
+ #ifndef PHP_VM_V2Z
2
+ #define PHP_VM_V2Z
3
+
4
+ #include <ruby.h>
5
+ #include <sapi/embed/php_embed.h>
6
+
7
+ extern void value_to_zval(VALUE v, zval *z);
8
+
9
+ #endif
data/ext/php_vm_z2v.c ADDED
@@ -0,0 +1,155 @@
1
+ #include "php_vm.h"
2
+ #include "php_vm_z2v.h"
3
+ #include <string.h>
4
+
5
+
6
+ static int is_array_convertable(HashTable* ht)
7
+ {
8
+ HashPosition pos;
9
+ char *string_key;
10
+ ulong num_index;
11
+ ulong index = 0;
12
+
13
+ zend_hash_internal_pointer_reset_ex(ht, &pos);
14
+ do {
15
+ switch(zend_hash_get_current_key_ex(ht, &string_key, NULL, &num_index, 0, &pos)) {
16
+ case HASH_KEY_IS_STRING:
17
+ return 0;
18
+ case HASH_KEY_NON_EXISTANT:
19
+ return 1;
20
+ case HASH_KEY_IS_LONG:
21
+ if (num_index != index) {
22
+ return 0;
23
+ }
24
+ ++index;
25
+ }
26
+ } while(SUCCESS == zend_hash_move_forward_ex(ht, &pos));
27
+ return 1;
28
+ }
29
+
30
+ static VALUE zval_to_value_array(HashTable* ht)
31
+ {
32
+ HashPosition pos;
33
+ zval** data;
34
+ VALUE ret;
35
+
36
+ ret = rb_ary_new2(zend_hash_num_elements(ht));
37
+
38
+ zend_hash_internal_pointer_reset_ex(ht, &pos);
39
+ while (SUCCESS == zend_hash_get_current_data_ex(ht, (void **)&data, &pos)) {
40
+ VALUE t = zval_to_value(*data);
41
+ rb_ary_push(ret, t);
42
+ zend_hash_move_forward_ex(ht, &pos);
43
+ }
44
+ return ret;
45
+ }
46
+
47
+ static VALUE zval_to_value_hash(HashTable* ht)
48
+ {
49
+ HashPosition pos;
50
+ zval** data;
51
+ VALUE ret;
52
+
53
+ ret = rb_hash_new();
54
+
55
+ zend_hash_internal_pointer_reset_ex(ht, &pos);
56
+ while (SUCCESS == zend_hash_get_current_data_ex(ht, (void **)&data, &pos)) {
57
+ char* string_key;
58
+ ulong num_index;
59
+ VALUE key = Qnil;
60
+ VALUE val = zval_to_value(*data);
61
+
62
+ switch(zend_hash_get_current_key_ex(ht, &string_key, NULL, &num_index, 0, &pos)) {
63
+ case HASH_KEY_IS_STRING:
64
+ key = rb_str_new_cstr(string_key);
65
+ break;
66
+ case HASH_KEY_IS_LONG:
67
+ key = LONG2NUM(num_index);
68
+ break;
69
+ }
70
+
71
+ rb_hash_aset(ret, key, val);
72
+ zend_hash_move_forward_ex(ht, &pos);
73
+ }
74
+ return ret;
75
+ }
76
+
77
+ static VALUE zval_to_value_object(zval *z)
78
+ {
79
+ // class name
80
+ const char *name = "";
81
+ zend_uint name_len = 0;
82
+ int dup;
83
+ dup = zend_get_object_classname(z, &name, &name_len TSRMLS_CC);
84
+ VALUE v_name = rb_str_new(name, name_len);
85
+
86
+ // class
87
+ VALUE class = rb_php_class_get(rb_cPHPClass, v_name);
88
+
89
+ // object
90
+ VALUE obj = Qnil;
91
+ if (is_exception_zval(z)) {
92
+ // exception object
93
+ zval *z_message = zend_read_property(zend_exception_get_default(TSRMLS_C), z, "message", sizeof("message")-1, 0 TSRMLS_CC);
94
+
95
+ obj = rb_exc_new2(rb_ePHPExceptionObject, Z_STRVAL_P(z_message));
96
+
97
+ rb_iv_set(obj, "php_class", class);
98
+ } else {
99
+ // normal object
100
+ obj = rb_obj_alloc(rb_cPHPObject);
101
+
102
+ rb_iv_set(obj, "php_class", class);
103
+ }
104
+
105
+ // resource
106
+ PHPNativeResource *p = ALLOC(PHPNativeResource);
107
+ p->ce = get_zend_class_entry(class);
108
+ p->zobj = z;
109
+ VALUE resource = Data_Wrap_Struct(CLASS_OF(obj), 0, php_native_resource_delete, p);
110
+ rb_iv_set(obj, "php_native_resource", resource);
111
+
112
+ return obj;
113
+ }
114
+
115
+
116
+ VALUE zval_to_value(zval *z)
117
+ {
118
+ if (z) {
119
+ switch(Z_TYPE_P(z)) {
120
+ case IS_NULL:
121
+ return Qnil;
122
+ case IS_BOOL:
123
+ return (zval_is_true(z)) ? Qtrue : Qfalse;
124
+ case IS_LONG:
125
+ return INT2NUM(Z_LVAL_P(z));
126
+ case IS_DOUBLE:
127
+ return DBL2NUM(Z_DVAL_P(z));
128
+ case IS_ARRAY:
129
+ case IS_CONSTANT_ARRAY:{
130
+ HashTable* ht = Z_ARRVAL_P(z);
131
+
132
+ if (0 == zend_hash_num_elements(ht)) {
133
+ return rb_ary_new();
134
+ }
135
+
136
+ if (is_array_convertable(ht)) {
137
+ return zval_to_value_array(ht);
138
+ }
139
+
140
+ return zval_to_value_hash(ht);
141
+ }
142
+ case IS_OBJECT:
143
+ case IS_RESOURCE:
144
+ case IS_CONSTANT:
145
+ return zval_to_value_object(z);
146
+ case IS_STRING:
147
+ return rb_str_new(Z_STRVAL_P(z), Z_STRLEN_P(z));
148
+ default:
149
+ return Qnil;
150
+ }
151
+ }
152
+ return Qnil;
153
+ }
154
+
155
+
data/ext/php_vm_z2v.h ADDED
@@ -0,0 +1,9 @@
1
+ #ifndef PHP_VM_Z2V
2
+ #define PHP_VM_Z2V
3
+
4
+ #include <ruby.h>
5
+ #include <sapi/embed/php_embed.h>
6
+
7
+ extern VALUE zval_to_value(zval *z);
8
+
9
+ #endif
data/sample/class.rb ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH << File.expand_path(__FILE__+"/../../ext/")
4
+ require "php_vm"
5
+
6
+ PHPVM.exec <<EOS
7
+ class HelloClass
8
+ {
9
+ public function __construct($name)
10
+ {
11
+ $this->name = $name;
12
+ }
13
+
14
+
15
+ // instance
16
+
17
+ public function instanceGetHello()
18
+ {
19
+ return "Hello {$this->name}!!";
20
+ }
21
+
22
+ public function instanceSayHello()
23
+ {
24
+ var_dump($this->instanceGetHello());
25
+ }
26
+
27
+
28
+ // static
29
+
30
+ public static function classGetHello($name)
31
+ {
32
+ return "Hello {$name}!!";
33
+ }
34
+
35
+ public static function classSayHello($name)
36
+ {
37
+ var_dump(self::classGetHello($name));
38
+ }
39
+ }
40
+ EOS
41
+
42
+ HelloClass = PHPVM::getClass("HelloClass")
43
+
44
+ puts "[class]"
45
+ puts HelloClass
46
+ puts HelloClass.name
47
+ puts ""
48
+
49
+ puts "[instance method]"
50
+ h = HelloClass.new("instance world")
51
+ h.instanceSayHello
52
+ p h.instanceGetHello
53
+ puts ""
54
+
55
+ puts "[class method]"
56
+ HelloClass.classSayHello("class world")
57
+ p HelloClass.classGetHello("class world")
data/sample/exec.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH << File.expand_path(__FILE__+"/../../ext/")
4
+ require "php_vm"
5
+
6
+ PHPVM.exec('echo "Hello world!!\n";')
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH << File.expand_path(__FILE__+"/../../ext/")
4
+ require "php_vm"
5
+
6
+ begin
7
+ PHPVM.exec("throw new Exception('throw exception!!');")
8
+ rescue PHPVM::PHPError => e
9
+ puts "#{e.php_class.name}: #{e}"
10
+ end
@@ -0,0 +1,3 @@
1
+ <?php
2
+
3
+ echo "Hello world!!\n";
data/sample/require.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH << File.expand_path(__FILE__+"/../../ext/")
4
+ require "php_vm"
5
+
6
+ PHPVM.require(File.expand_path(__FILE__+"/../require.php"))
data/sample/version.rb ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH << File.expand_path(__FILE__+"/../../ext/")
4
+ require "php_vm"
5
+
6
+ puts PHPVM::VERSION
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: php_vm
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Yoshida Tetsuya
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-23 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: php_vm is a native bridge between Ruby and PHP.
15
+ email:
16
+ - yoshida.eth0@gmail.com
17
+ executables: []
18
+ extensions:
19
+ - ext/extconf.rb
20
+ extra_rdoc_files: []
21
+ files:
22
+ - ext/extconf.rb
23
+ - ext/php_vm.c
24
+ - ext/php_vm.h
25
+ - ext/php_vm_v2z.c
26
+ - ext/php_vm_v2z.h
27
+ - ext/php_vm_z2v.c
28
+ - ext/php_vm_z2v.h
29
+ - sample/class.rb
30
+ - sample/exec.rb
31
+ - sample/exec_raise_exception.rb
32
+ - sample/require.php
33
+ - sample/require.rb
34
+ - sample/version.rb
35
+ homepage: ''
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 1.8.23
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: php_vm is a native bridge between Ruby and PHP.
59
+ test_files: []