php_vm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []