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 +9 -0
- data/ext/php_vm.c +567 -0
- data/ext/php_vm.h +64 -0
- data/ext/php_vm_v2z.c +81 -0
- data/ext/php_vm_v2z.h +9 -0
- data/ext/php_vm_z2v.c +155 -0
- data/ext/php_vm_z2v.h +9 -0
- data/sample/class.rb +57 -0
- data/sample/exec.rb +6 -0
- data/sample/exec_raise_exception.rb +10 -0
- data/sample/require.php +3 -0
- data/sample/require.rb +6 -0
- data/sample/version.rb +6 -0
- metadata +59 -0
data/ext/extconf.rb
ADDED
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
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
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
data/sample/require.php
ADDED
data/sample/require.rb
ADDED
data/sample/version.rb
ADDED
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: []
|