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.
@@ -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)