Skip to content

Commit 7fa1e96

Browse files
committed
Allow passing "command line arguments" to the request.
This allows us to invoke CLI scripts from within node.
1 parent 670e602 commit 7fa1e96

File tree

4 files changed

+91
-15
lines changed

4 files changed

+91
-15
lines changed

lib/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ exports.request = function(options, cb) {
123123
}
124124
var stream = new StreamWrapper(options.stream || process.stdout);
125125
var context = options.context;
126+
var args = options.args || [];
126127
// This function initializes $_SERVER inside the PHP request.
127128
var initServer = function(server, cb) {
128129
server.CONTEXT = context;
@@ -171,7 +172,7 @@ exports.request = function(options, cb) {
171172
cb();
172173
}
173174
};
174-
return request(source, stream, initServer).tap(function() {
175+
return request(source, stream, args, initServer).tap(function() {
175176
// Ensure the stream is flushed before promise is resolved.
176177
return new Promise(function(resolve, reject) {
177178
stream.write(new Buffer(0), function(e) {

lib/php.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
html_errors=1
2-
register_argc_argv=0
3-
implicit_flush=1
2+
register_argc_argv=1
3+
implicit_flush=0
44
max_execution_time=0
5-
max_input_time=-1
5+
max_input_time=0

src/node_php_embed.cc

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,28 @@ ZEND_DECLARE_MODULE_GLOBALS(node_php_embed);
5151
class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
5252
public:
5353
PhpRequestWorker(Nan::Callback *callback, v8::Local<v8::String> source,
54-
v8::Local<v8::Object> stream, v8::Local<v8::Value> init_func)
55-
: AsyncMessageWorker(callback), result_(), stream_(), init_func_() {
54+
v8::Local<v8::Object> stream, v8::Local<v8::Array> args,
55+
v8::Local<v8::Value> init_func)
56+
: AsyncMessageWorker(callback), result_(), stream_(), init_func_(),
57+
argc_(args->Length()), argv_(new char*[args->Length()]) {
5658
JsStartupMapper mapper(this);
5759
source_.Set(&mapper, source);
5860
stream_.Set(&mapper, stream);
5961
init_func_.Set(&mapper, init_func);
62+
// Turn the JS array into a char** suitable for PHP.
63+
for (uint32_t i = 0; i < argc_; i++) {
64+
Nan::Utf8String s(
65+
Nan::Get(args, i)
66+
.FromMaybe(static_cast< v8::Local<v8::Value> >(Nan::EmptyString())));
67+
argv_[i] = strdup(*s ? *s : "");
68+
}
69+
}
70+
virtual ~PhpRequestWorker() {
71+
for (uint32_t i = 0; i < argc_; i++) {
72+
free(argv_[i]);
73+
}
74+
delete[] argv_;
6075
}
61-
virtual ~PhpRequestWorker() { }
6276
const Value &GetStream() { return stream_; }
6377
const Value &GetInitFunc() { return init_func_; }
6478

@@ -67,6 +81,8 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
6781
// should go on `this`.
6882
void Execute(MapperChannel *channel TSRMLS_DC) override {
6983
TRACE("> PhpRequestWorker");
84+
SG(request_info).argc = argc_;
85+
SG(request_info).argv = argv_;
7086
if (php_request_startup(TSRMLS_C) == FAILURE) {
7187
Nan::ThrowError("can't create request");
7288
return;
@@ -117,6 +133,8 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
117133
NODE_PHP_EMBED_G(channel) = nullptr;
118134
TRACE("- request shutdown");
119135
php_request_shutdown(nullptr);
136+
SG(request_info).argc = 0;
137+
SG(request_info).argv = nullptr;
120138
TRACE("< PhpRequestWorker");
121139
}
122140
// Executed when the async work is complete.
@@ -138,6 +156,8 @@ class node_php_embed::PhpRequestWorker : public AsyncMessageWorker {
138156
Value result_;
139157
Value stream_;
140158
Value init_func_;
159+
uint32_t argc_;
160+
char **argv_;
141161
};
142162

143163
/* PHP extension metadata */
@@ -291,20 +311,24 @@ NAN_METHOD(request) {
291311
if (!info[1]->IsObject()) {
292312
return Nan::ThrowTypeError("stream expected");
293313
}
294-
if (!info[2]->IsFunction()) {
295-
return Nan::ThrowTypeError("init function expected");
314+
if (!info[2]->IsArray()) {
315+
return Nan::ThrowTypeError("argument array expected");
296316
}
297317
if (!info[3]->IsFunction()) {
318+
return Nan::ThrowTypeError("init function expected");
319+
}
320+
if (!info[4]->IsFunction()) {
298321
return Nan::ThrowTypeError("callback expected");
299322
}
300323
v8::Local<v8::String> source = info[0].As<v8::String>();
301324
v8::Local<v8::Object> stream = info[1].As<v8::Object>();
302-
v8::Local<v8::Value> init_func = info[2];
303-
Nan::Callback *callback = new Nan::Callback(info[3].As<v8::Function>());
325+
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>());
304328

305329
node_php_embed_ensure_init();
306330
Nan::AsyncQueueWorker(new PhpRequestWorker(callback, source, stream,
307-
init_func));
331+
args, init_func));
308332
TRACE("<");
309333
}
310334

@@ -367,9 +391,7 @@ static void node_php_embed_ensure_init(void) {
367391
}
368392
TRACE(">");
369393
node_php_embed_inited = true;
370-
char *argv[] = { nullptr };
371-
int argc = 0;
372-
php_embed_init(argc, argv PTSRMLS_CC);
394+
php_embed_init(0, nullptr PTSRMLS_CC);
373395
// Shutdown the initially-created request; we'll create our own request
374396
// objects inside PhpRequestWorker.
375397
php_request_shutdown(nullptr);

test/args.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require('should');
2+
3+
var StringStream = require('../test-stream.js');
4+
5+
describe('Passing command-line arguments to PHP request', function() {
6+
var php = require('../');
7+
it('$argc should not be present normally', function() {
8+
var out = new StringStream();
9+
return php.request({
10+
source: 'var_dump($argc)',
11+
stream: out,
12+
}).then(function(v, o) {
13+
out.toString().should.equal('NULL\n');
14+
});
15+
});
16+
it('$argv should not be present normally', function() {
17+
var out = new StringStream();
18+
return php.request({
19+
source: 'var_dump($argv)',
20+
stream: out,
21+
}).then(function(v, o) {
22+
out.toString().should.equal('NULL\n');
23+
});
24+
});
25+
it('$argc should be present when passed', function() {
26+
var out = new StringStream();
27+
return php.request({
28+
source: 'var_dump($argc)',
29+
stream: out,
30+
args: [ 1, 'abc' ],
31+
}).then(function(v, o) {
32+
out.toString().should.equal('int(2)\n');
33+
});
34+
});
35+
it('$argv should be present when passed', function() {
36+
var out = new StringStream();
37+
return php.request({
38+
source: 'var_dump($argv)',
39+
stream: out,
40+
args: [ 1, 'abc' ],
41+
}).then(function(v, o) {
42+
out.toString().should.equal([
43+
'array(2) {',
44+
' [0]=>',
45+
' string(1) "1"',
46+
' [1]=>',
47+
' string(3) "abc"',
48+
'}',
49+
'',
50+
].join('\n'));
51+
});
52+
});
53+
});

0 commit comments

Comments
 (0)