phuby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }
@@ -0,0 +1,11 @@
1
+ #ifndef PHUBY_RUNTIME
2
+ #define PHUBY_RUNTIME
3
+
4
+ #include <phuby.h>
5
+
6
+ extern VALUE cPhubyRuntime;
7
+ extern zend_class_entry *php_ruby_proxy;
8
+
9
+ void init_phuby_runtime();
10
+
11
+ #endif
@@ -0,0 +1,9 @@
1
+ require 'phuby/phuby'
2
+ require 'phuby/events'
3
+ require 'phuby/runtime'
4
+ require 'phuby/array'
5
+ require 'phuby/php_handler'
6
+
7
+ module Phuby
8
+ VERSION = '1.0.0'
9
+ end
@@ -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
@@ -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
@@ -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)