phuby 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/.autotest +23 -0
- data/CHANGELOG.rdoc +6 -0
- data/Manifest.txt +35 -0
- data/README.rdoc +96 -0
- data/Rakefile +23 -0
- data/bin/phrack +50 -0
- data/bin/phuby +3 -0
- data/bin/phuby_server +12 -0
- data/ext/phuby/extconf.rb +24 -0
- data/ext/phuby/phuby.c +11 -0
- data/ext/phuby/phuby.h +14 -0
- data/ext/phuby/phuby_array.c +92 -0
- data/ext/phuby/phuby_array.h +9 -0
- data/ext/phuby/phuby_conversions.c +92 -0
- data/ext/phuby/phuby_conversions.h +12 -0
- data/ext/phuby/phuby_runtime.c +243 -0
- data/ext/phuby/phuby_runtime.h +11 -0
- data/lib/phuby.rb +9 -0
- data/lib/phuby/array.rb +24 -0
- data/lib/phuby/events.rb +19 -0
- data/lib/phuby/php_handler.rb +96 -0
- data/lib/phuby/rails.rb +47 -0
- data/lib/phuby/runtime.rb +69 -0
- data/php.patch +145 -0
- data/test/assets/hello_world.php +6 -0
- data/test/assets/htdocs/index.php +22 -0
- data/test/helper.rb +19 -0
- data/test/test_array.rb +101 -0
- data/test/test_handlers.rb +49 -0
- data/test/test_header_sent.rb +25 -0
- data/test/test_nil.rb +10 -0
- data/test/test_object.rb +57 -0
- data/test/test_php_handler.rb +56 -0
- data/test/test_phuby.rb +90 -0
- data/test/test_runtime.rb +77 -0
- metadata +151 -0
@@ -0,0 +1,12 @@
|
|
1
|
+
#ifndef PHUBY_CONVERSIONS
|
2
|
+
#define PHUBY_CONVERSIONS
|
3
|
+
|
4
|
+
#include <phuby.h>
|
5
|
+
|
6
|
+
VALUE Phuby_zval_to_value(VALUE rt, zval * value);
|
7
|
+
zval * Phuby_value_to_zval(VALUE rt, VALUE thing);
|
8
|
+
|
9
|
+
#define ZVAL2VALUE(rt, a) Phuby_zval_to_value(rt, a)
|
10
|
+
#define VALUE2ZVAL(rt, a) Phuby_value_to_zval(rt, a)
|
11
|
+
|
12
|
+
#endif
|
@@ -0,0 +1,243 @@
|
|
1
|
+
#include <phuby_runtime.h>
|
2
|
+
|
3
|
+
VALUE cPhubyRuntime;
|
4
|
+
|
5
|
+
PHP_METHOD(RubyProxy, __call)
|
6
|
+
{
|
7
|
+
char *function;
|
8
|
+
int function_len;
|
9
|
+
zval *args = NULL;
|
10
|
+
|
11
|
+
if(zend_parse_parameters(ZEND_NUM_ARGS(), "sa|a", &function, &function_len,
|
12
|
+
&args) == FAILURE) {
|
13
|
+
printf("arg################:\n");
|
14
|
+
}
|
15
|
+
|
16
|
+
VALUE rt = rb_funcall(cPhubyRuntime, rb_intern("instance"), 0);
|
17
|
+
|
18
|
+
VALUE ret = rb_funcall(rt, rb_intern("call"), 3,
|
19
|
+
INT2NUM((int)this_ptr),
|
20
|
+
rb_str_new(function, function_len),
|
21
|
+
ZVAL2VALUE(rt, args)
|
22
|
+
);
|
23
|
+
|
24
|
+
if(ret != Qnil) {
|
25
|
+
zval * rv = Phuby_value_to_zval(rt, ret);
|
26
|
+
RETURN_ZVAL(rv, 1, 1);
|
27
|
+
}
|
28
|
+
RETURN_NULL();
|
29
|
+
}
|
30
|
+
|
31
|
+
zend_class_entry *php_ruby_proxy;
|
32
|
+
|
33
|
+
ZEND_BEGIN_ARG_INFO_EX(arginfo_foo___call, 0, 0, 2)
|
34
|
+
ZEND_ARG_INFO(0, function_name)
|
35
|
+
ZEND_ARG_INFO(0, arguments)
|
36
|
+
ZEND_END_ARG_INFO()
|
37
|
+
|
38
|
+
function_entry php_ruby_functions[] = {
|
39
|
+
PHP_ME(RubyProxy, __call, arginfo_foo___call, 0)
|
40
|
+
{ NULL, NULL, NULL }
|
41
|
+
};
|
42
|
+
|
43
|
+
static int phuby_ub_write(const char *str, unsigned int strlen)
|
44
|
+
{
|
45
|
+
VALUE self = rb_funcall(cPhubyRuntime, rb_intern("instance"), 0);
|
46
|
+
VALUE handler = rb_iv_get(self, "@events");
|
47
|
+
|
48
|
+
rb_funcall(handler, rb_intern("write"), 1, rb_str_new(str, strlen));
|
49
|
+
return strlen;
|
50
|
+
}
|
51
|
+
|
52
|
+
static int phuby_header_handler(
|
53
|
+
sapi_header_struct *sapi_header,
|
54
|
+
sapi_header_op_enum op,
|
55
|
+
sapi_headers_struct *sapi_headers)
|
56
|
+
{
|
57
|
+
VALUE header = sapi_header->header ?
|
58
|
+
rb_str_new2(sapi_header->header) :
|
59
|
+
Qnil;
|
60
|
+
|
61
|
+
VALUE rb_op = Qnil;
|
62
|
+
|
63
|
+
int return_value = 0;
|
64
|
+
|
65
|
+
switch(op) {
|
66
|
+
case SAPI_HEADER_DELETE_ALL:
|
67
|
+
rb_op = ID2SYM(rb_intern("delete_all"));
|
68
|
+
break;
|
69
|
+
case SAPI_HEADER_DELETE:
|
70
|
+
rb_op = ID2SYM(rb_intern("delete"));
|
71
|
+
break;
|
72
|
+
case SAPI_HEADER_ADD:
|
73
|
+
case SAPI_HEADER_REPLACE:
|
74
|
+
rb_op = ID2SYM(rb_intern("store"));
|
75
|
+
return_value = SAPI_HEADER_ADD;
|
76
|
+
break;
|
77
|
+
default:
|
78
|
+
rb_raise(rb_eRuntimeError, "header_handler huh?");
|
79
|
+
}
|
80
|
+
|
81
|
+
VALUE self = rb_funcall(cPhubyRuntime, rb_intern("instance"), 0);
|
82
|
+
VALUE handler = rb_iv_get(self, "@events");
|
83
|
+
|
84
|
+
rb_funcall(handler, rb_intern("header"), 2, header, rb_op);
|
85
|
+
|
86
|
+
return return_value;
|
87
|
+
}
|
88
|
+
|
89
|
+
static int phuby_send_headers(sapi_headers_struct *sapi_headers)
|
90
|
+
{
|
91
|
+
VALUE self = rb_funcall(cPhubyRuntime, rb_intern("instance"), 0);
|
92
|
+
VALUE handler = rb_iv_get(self, "@events");
|
93
|
+
|
94
|
+
int rc = sapi_headers->http_response_code == 0 ?
|
95
|
+
200 :
|
96
|
+
sapi_headers->http_response_code;
|
97
|
+
|
98
|
+
rb_funcall(handler, rb_intern("send_headers"), 1, INT2NUM(rc));
|
99
|
+
|
100
|
+
return SAPI_HEADER_SENT_SUCCESSFULLY;
|
101
|
+
}
|
102
|
+
|
103
|
+
static void phuby_flush(void *server_context)
|
104
|
+
{
|
105
|
+
sapi_send_headers();
|
106
|
+
}
|
107
|
+
|
108
|
+
static VALUE start(VALUE self)
|
109
|
+
{
|
110
|
+
VALUE mutex = rb_iv_get(self, "@mutex");
|
111
|
+
rb_funcall(mutex, rb_intern("lock"), 0);
|
112
|
+
|
113
|
+
/* FIXME:
|
114
|
+
* I got these from the book. I don't know wtf they're for yet. */
|
115
|
+
int argc = 1;
|
116
|
+
char *argv[2] = { "embed4", NULL };
|
117
|
+
/* end FIXME */
|
118
|
+
|
119
|
+
php_embed_module.ub_write = phuby_ub_write;
|
120
|
+
php_embed_module.header_handler = phuby_header_handler;
|
121
|
+
php_embed_module.send_headers = phuby_send_headers;
|
122
|
+
php_embed_module.flush = phuby_flush;
|
123
|
+
|
124
|
+
php_embed_init(argc, argv);
|
125
|
+
zend_class_entry ce;
|
126
|
+
INIT_CLASS_ENTRY(ce, "RubyProxy", php_ruby_functions);
|
127
|
+
|
128
|
+
php_ruby_proxy = zend_register_internal_class(&ce);
|
129
|
+
|
130
|
+
SG(headers_sent) = 0;
|
131
|
+
SG(request_info).no_headers = 0;
|
132
|
+
|
133
|
+
return Qnil;
|
134
|
+
}
|
135
|
+
|
136
|
+
static VALUE stop(VALUE self)
|
137
|
+
{
|
138
|
+
php_embed_shutdown();
|
139
|
+
|
140
|
+
VALUE mutex = rb_iv_get(self, "@mutex");
|
141
|
+
rb_funcall(mutex, rb_intern("unlock"), 0);
|
142
|
+
|
143
|
+
return Qnil;
|
144
|
+
}
|
145
|
+
|
146
|
+
/* IO reader callback for PHP IO streams */
|
147
|
+
static size_t rb_io_reader(void *handle, char *buf, size_t len)
|
148
|
+
{
|
149
|
+
VALUE io = (VALUE)handle;
|
150
|
+
VALUE string = rb_funcall(io, rb_intern("read"), 1, INT2NUM(len));
|
151
|
+
|
152
|
+
if(Qnil == string) return 0;
|
153
|
+
|
154
|
+
memcpy(buf, StringValuePtr(string), (unsigned int)RSTRING_LEN(string));
|
155
|
+
|
156
|
+
return RSTRING_LEN(string);
|
157
|
+
}
|
158
|
+
|
159
|
+
static size_t rb_io_sizer(void *handle)
|
160
|
+
{
|
161
|
+
}
|
162
|
+
|
163
|
+
static void rb_io_closer(void *handle)
|
164
|
+
{
|
165
|
+
// We'll let the caller close their own IO
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
|
169
|
+
static VALUE native_eval_io(VALUE self, VALUE io, VALUE filename)
|
170
|
+
{
|
171
|
+
zend_file_handle script;
|
172
|
+
|
173
|
+
script.type = ZEND_HANDLE_STREAM;
|
174
|
+
script.filename = StringValuePtr(filename);
|
175
|
+
script.opened_path = NULL;
|
176
|
+
|
177
|
+
memset(&script.handle.stream.mmap, 0, sizeof(zend_mmap));
|
178
|
+
|
179
|
+
script.handle.stream.handle = (void *)io;
|
180
|
+
script.handle.stream.isatty = 0;
|
181
|
+
|
182
|
+
script.handle.stream.reader = rb_io_reader;
|
183
|
+
script.handle.stream.fsizer = rb_io_sizer;
|
184
|
+
script.handle.stream.closer = rb_io_closer;
|
185
|
+
|
186
|
+
script.free_filename = 0;
|
187
|
+
|
188
|
+
php_execute_script(&script);
|
189
|
+
|
190
|
+
return Qnil;
|
191
|
+
}
|
192
|
+
|
193
|
+
static VALUE native_eval(VALUE self, VALUE string, VALUE filename)
|
194
|
+
{
|
195
|
+
|
196
|
+
zval return_value;
|
197
|
+
|
198
|
+
zend_first_try {
|
199
|
+
zend_eval_string(
|
200
|
+
StringValuePtr(string),
|
201
|
+
NULL,
|
202
|
+
StringValuePtr(filename)
|
203
|
+
);
|
204
|
+
} zend_end_try();
|
205
|
+
|
206
|
+
return Qnil;
|
207
|
+
}
|
208
|
+
|
209
|
+
static VALUE get(VALUE self, VALUE key)
|
210
|
+
{
|
211
|
+
zval **value;
|
212
|
+
|
213
|
+
if(zend_hash_find(EG(active_symbol_table),
|
214
|
+
StringValuePtr(key),
|
215
|
+
RSTRING_LEN(key) + 1,
|
216
|
+
(void **)&value) == SUCCESS) {
|
217
|
+
return ZVAL2VALUE(self, *value);
|
218
|
+
}
|
219
|
+
|
220
|
+
return Qnil;
|
221
|
+
}
|
222
|
+
|
223
|
+
static VALUE set(VALUE self, VALUE key, VALUE value)
|
224
|
+
{
|
225
|
+
zval *php_value = VALUE2ZVAL(self, value);
|
226
|
+
|
227
|
+
ZEND_SET_SYMBOL(EG(active_symbol_table), StringValuePtr(key), php_value);
|
228
|
+
|
229
|
+
return value;
|
230
|
+
}
|
231
|
+
|
232
|
+
void init_phuby_runtime()
|
233
|
+
{
|
234
|
+
cPhubyRuntime = rb_define_class_under(mPhuby, "Runtime", rb_cObject);
|
235
|
+
|
236
|
+
rb_define_method(cPhubyRuntime, "start", start, 0);
|
237
|
+
rb_define_method(cPhubyRuntime, "stop", stop, 0);
|
238
|
+
|
239
|
+
rb_define_private_method(cPhubyRuntime, "get", get, 1);
|
240
|
+
rb_define_private_method(cPhubyRuntime, "set", set, 2);
|
241
|
+
rb_define_private_method(cPhubyRuntime, "native_eval", native_eval, 2);
|
242
|
+
rb_define_private_method(cPhubyRuntime, "native_eval_io", native_eval_io, 2);
|
243
|
+
}
|
data/lib/phuby.rb
ADDED
data/lib/phuby/array.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Phuby
|
2
|
+
class Array
|
3
|
+
def [] key
|
4
|
+
get key
|
5
|
+
end
|
6
|
+
|
7
|
+
def []= key, value
|
8
|
+
set key, value
|
9
|
+
end
|
10
|
+
|
11
|
+
def each &block
|
12
|
+
0.upto(length - 1) do |i|
|
13
|
+
block.call get(i)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
tmp = []
|
19
|
+
each { |x| tmp << x }
|
20
|
+
|
21
|
+
tmp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/phuby/events.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Phuby
|
2
|
+
class Events
|
3
|
+
###
|
4
|
+
# Called when PHP wants to write something out
|
5
|
+
def write string
|
6
|
+
$stdout.write string
|
7
|
+
end
|
8
|
+
|
9
|
+
###
|
10
|
+
# Called when PHP wants to manipulate headers
|
11
|
+
def header header, op
|
12
|
+
end
|
13
|
+
|
14
|
+
###
|
15
|
+
# Called when PHP wants to send response headers with +response_code+
|
16
|
+
def send_headers response_code
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'webrick'
|
2
|
+
require 'cgi'
|
3
|
+
require 'iconv'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module Phuby
|
7
|
+
class PHPHandler < WEBrick::HTTPServlet::FileHandler
|
8
|
+
class Events < Phuby::Events
|
9
|
+
def initialize req, res
|
10
|
+
super()
|
11
|
+
@req = req
|
12
|
+
@res = res
|
13
|
+
end
|
14
|
+
|
15
|
+
def header value, op
|
16
|
+
k, v = *value.split(':', 2)
|
17
|
+
if k.downcase == 'set-cookie'
|
18
|
+
@res.cookies << v.strip
|
19
|
+
else
|
20
|
+
@res[k] = v.strip
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write string
|
25
|
+
@res.body ||= ''
|
26
|
+
@res.body << string
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_headers response_code
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize server, root = server.config[:DocumentRoot] || Dir.pwd, *args
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def do_POST req, res
|
38
|
+
req.path << "index.php" if req.path =~ /\/$/
|
39
|
+
|
40
|
+
return super(req, res) unless req.path =~ /\.php$/
|
41
|
+
|
42
|
+
process :POST, req, res
|
43
|
+
end
|
44
|
+
|
45
|
+
def do_GET req, res
|
46
|
+
req.path << "index.php" if req.path =~ /\/$/
|
47
|
+
|
48
|
+
return super(req, res) unless req.path =~ /\.php$/
|
49
|
+
|
50
|
+
process :GET, req, res
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def process verb, req, res
|
55
|
+
file = File.join(@root, req.path)
|
56
|
+
|
57
|
+
Dir.chdir(File.dirname(file)) do
|
58
|
+
Phuby::Runtime.php do |rt|
|
59
|
+
rt.eval("date_default_timezone_set('America/Los_Angeles');")
|
60
|
+
|
61
|
+
rt['logger'] = Logger.new($stdout)
|
62
|
+
req.request_uri.query.split('&').each do |pair|
|
63
|
+
k, v = pair.split '='
|
64
|
+
rt["_GET"][k] = v
|
65
|
+
end if req.request_uri.query
|
66
|
+
|
67
|
+
req.query.each do |k,v|
|
68
|
+
rt["_#{verb}"][k] = v
|
69
|
+
end if :POST == verb
|
70
|
+
|
71
|
+
# Set CGI server options
|
72
|
+
req.meta_vars.each do |k,v|
|
73
|
+
rt["_SERVER"][k] = v || ''
|
74
|
+
end
|
75
|
+
rt["_SERVER"]['REQUEST_URI'] = req.request_uri.path
|
76
|
+
|
77
|
+
req.cookies.each do |cookie|
|
78
|
+
rt["_COOKIE"][cookie.name] = CGI.unescape(cookie.value)
|
79
|
+
end
|
80
|
+
|
81
|
+
events = Events.new req, res
|
82
|
+
|
83
|
+
rt.with_events(events) do
|
84
|
+
File.open(file, 'rb') { |f|
|
85
|
+
rt.eval f
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if res['Location']
|
91
|
+
res['Location'] = CGI.unescape res['Location']
|
92
|
+
res.status = 302
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/phuby/rails.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'phuby'
|
2
|
+
|
3
|
+
class PHPHandler < ActionView::TemplateHandler
|
4
|
+
class Events < Struct.new(:code, :headers, :body)
|
5
|
+
def write string; body << string; end
|
6
|
+
def send_headers response_code; end
|
7
|
+
|
8
|
+
def header value, op
|
9
|
+
k, v = value.split(': ', 2)
|
10
|
+
self.code = 302 if k == 'Location'
|
11
|
+
headers[k] = [headers[k], Rack::Utils.unescape(v)].compact.join "\n"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ControllerProxy
|
16
|
+
def initialize controller
|
17
|
+
@controller = controller
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing name
|
21
|
+
@controller.instance_variable_get :"@#{name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize view
|
26
|
+
@controller = view.controller
|
27
|
+
@proxy = ControllerProxy.new @controller
|
28
|
+
end
|
29
|
+
|
30
|
+
def render template, *args
|
31
|
+
filename = File.join template.load_path, template.template_path
|
32
|
+
events = Events.new(200, {}, '')
|
33
|
+
Dir.chdir(File.dirname(filename)) do
|
34
|
+
Phuby::Runtime.php do |rt|
|
35
|
+
rt.eval "date_default_timezone_set('America/Los_Angeles');"
|
36
|
+
rt['at'] = @proxy
|
37
|
+
rt.with_events(events) do
|
38
|
+
open(filename) { |f| rt.eval f }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
events.body
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
ActionView::Template.register_template_handler('php', PHPHandler)
|