55// Copyright (c) 2015 C. Scott Ananian <cscott@cscott.net>
66#include " src/node_php_embed.h"
77
8+ #include < string>
9+ #include < unordered_map>
10+
811#include " nan.h"
912
1013extern " C" {
@@ -52,9 +55,11 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
5255 public:
5356 PhpRequestWorker (Nan::Callback *callback, v8::Local<v8::String> source,
5457 v8::Local<v8::Object> stream, v8::Local<v8::Array> args,
58+ v8::Local<v8::Object> server_vars,
5559 v8::Local<v8::Value> init_func)
5660 : AsyncMessageWorker(callback), result_(), stream_(), init_func_(),
57- argc_ (args->Length ()), argv_(new char *[args->Length ()]) {
61+ argc_ (args->Length ()), argv_(new char *[args->Length ()]),
62+ server_vars_() {
5863 JsStartupMapper mapper (this );
5964 source_.Set (&mapper, source);
6065 stream_.Set (&mapper, stream);
@@ -66,6 +71,18 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
6671 .FromMaybe (static_cast < v8::Local<v8::Value> >(Nan::EmptyString ())));
6772 argv_[i] = strdup (*s ? *s : " " );
6873 }
74+ // Turn the server_vars object into a c++ string->string map.
75+ v8::Local<v8::Array> names = Nan::GetPropertyNames (server_vars)
76+ .FromMaybe (Nan::New<v8::Array>(0 ));
77+ for (uint32_t i = 0 ; i < names->Length (); i++) {
78+ v8::Local<v8::Value> key = Nan::Get (names, i).ToLocalChecked ();
79+ v8::Local<v8::Value> value = Nan::Get (server_vars, key)
80+ .FromMaybe (static_cast <v8::Local<v8::Value> >(Nan::Undefined ()));
81+ if (!value->IsString ()) { continue ; }
82+ Nan::Utf8String k (key), v (value);
83+ if (!(*k && *v)) { continue ; }
84+ server_vars_.emplace (*k, *v);
85+ }
6986 }
7087 virtual ~PhpRequestWorker () {
7188 for (uint32_t i = 0 ; i < argc_; i++) {
@@ -81,8 +98,29 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
8198 // should go on `this`.
8299 void Execute (MapperChannel *channel TSRMLS_DC) override {
83100 TRACE (" > PhpRequestWorker" );
101+ // Certain fields in request_info need to be set up before
102+ // php_request_startup is invoked.
84103 SG (request_info).argc = argc_;
85104 SG (request_info).argv = argv_;
105+ #define SET_REQUEST_INFO (envvar, requestvar ) \
106+ if (server_vars_.count (envvar)) { \
107+ SG (request_info).requestvar = estrdup ((server_vars_[envvar]).c_str ()); \
108+ } else { \
109+ SG (request_info).requestvar = nullptr ; \
110+ }
111+ #define FREE_REQUEST_INFO (requestvar ) /* for later */ \
112+ if (SG (request_info).requestvar ) { \
113+ efree (const_cast <char *>(SG (request_info).requestvar )); \
114+ SG (request_info).requestvar = nullptr ; \
115+ }
116+ SET_REQUEST_INFO (" REQUEST_METHOD" , request_method);
117+ SET_REQUEST_INFO (" QUERY_STRING" , query_string);
118+ SET_REQUEST_INFO (" PATH_TRANSLATED" , path_translated);
119+ SET_REQUEST_INFO (" REQUEST_URI" , request_uri);
120+ // xxx set proto_num ?
121+ // xxx set cookie_data ?
122+ server_vars_.clear (); // We don't need to keep this around any more.
123+
86124 if (php_request_startup (TSRMLS_C) == FAILURE) {
87125 Nan::ThrowError (" can't create request" );
88126 return ;
@@ -132,9 +170,13 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
132170 NODE_PHP_EMBED_G (worker) = nullptr ;
133171 NODE_PHP_EMBED_G (channel) = nullptr ;
134172 TRACE (" - request shutdown" );
135- php_request_shutdown (nullptr );
136173 SG (request_info).argc = 0 ;
137174 SG (request_info).argv = nullptr ;
175+ FREE_REQUEST_INFO (request_method);
176+ FREE_REQUEST_INFO (query_string);
177+ FREE_REQUEST_INFO (path_translated);
178+ FREE_REQUEST_INFO (request_uri);
179+ php_request_shutdown (nullptr );
138180 TRACE (" < PhpRequestWorker" );
139181 }
140182 // Executed when the async work is complete.
@@ -158,6 +200,7 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
158200 Value init_func_;
159201 uint32_t argc_;
160202 char **argv_;
203+ std::unordered_map<std::string, std::string> server_vars_;
161204};
162205
163206/* PHP extension metadata */
@@ -314,21 +357,25 @@ NAN_METHOD(request) {
314357 if (!info[2 ]->IsArray ()) {
315358 return Nan::ThrowTypeError (" argument array expected" );
316359 }
317- if (!info[3 ]->IsFunction ()) {
318- return Nan::ThrowTypeError (" init function expected" );
360+ if (!info[3 ]->IsObject ()) {
361+ return Nan::ThrowTypeError (" server vars object expected" );
319362 }
320363 if (!info[4 ]->IsFunction ()) {
364+ return Nan::ThrowTypeError (" init function expected" );
365+ }
366+ if (!info[5 ]->IsFunction ()) {
321367 return Nan::ThrowTypeError (" callback expected" );
322368 }
323369 v8::Local<v8::String> source = info[0 ].As <v8::String>();
324370 v8::Local<v8::Object> stream = info[1 ].As <v8::Object>();
325371 v8::Local<v8::Array> args = info[2 ].As <v8::Array>();
326- v8::Local<v8::Value> init_func = info[3 ];
327- Nan::Callback *callback = new Nan::Callback (info[4 ].As <v8::Function>());
372+ v8::Local<v8::Object> server_vars = info[3 ].As <v8::Object>();
373+ v8::Local<v8::Value> init_func = info[4 ];
374+ Nan::Callback *callback = new Nan::Callback (info[5 ].As <v8::Function>());
328375
329376 node_php_embed_ensure_init ();
330377 Nan::AsyncQueueWorker (new PhpRequestWorker (callback, source, stream,
331- args, init_func));
378+ args, server_vars, init_func));
332379 TRACE (" <" );
333380}
334381
@@ -395,6 +442,15 @@ static void node_php_embed_ensure_init(void) {
395442 // Shutdown the initially-created request; we'll create our own request
396443 // objects inside PhpRequestWorker.
397444 php_request_shutdown (nullptr );
445+ #define CHECK_NULL (requestvar ) \
446+ if (SG (request_info).requestvar ) { \
447+ NPE_ERROR (" OOPS! " #requestvar " is set!" ); \
448+ SG (request_info).requestvar = nullptr ; \
449+ }
450+ CHECK_NULL (request_method);
451+ CHECK_NULL (query_string);
452+ CHECK_NULL (path_translated);
453+ CHECK_NULL (request_uri);
398454 node::AtExit (ModuleShutdown, nullptr );
399455 TRACE (" <" );
400456}
0 commit comments