Skip to content

Commit 734f144

Browse files
authored
Fix SAPI shutdown (#19)
1 parent ec372bc commit 734f144

File tree

4 files changed

+145
-87
lines changed

4 files changed

+145
-87
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ console.log(response.body.toString())
5858
### `new Php(config)`
5959

6060
* `config` {Object} Configuration object
61+
* `argv` {String[]} Process arguments. **Default:** []
6162
* `docroot` {String} Document root for PHP. **Default:** process.cwd()
6263
* Returns: {Php}
6364

@@ -67,6 +68,7 @@ Construct a new PHP instance to which to dispatch requests.
6768
import { Php } from '@platformatic/php-node'
6869

6970
const php = new Php({
71+
argv: process.argv,
7072
docroot: process.cwd()
7173
})
7274
````

__test__/handler.spec.mjs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ test('Support input/output streams', async (t) => {
1515
t.teardown(() => mockroot.clean())
1616

1717
const php = new Php({
18-
argv: process.argv,
1918
docroot: mockroot.path
2019
})
2120

2221
const req = new Request({
23-
method: 'GET',
22+
method: 'POST',
2423
url: 'http://example.com/index.php',
2524
body: Buffer.from('Hello, from Node.js!')
2625
})
@@ -39,12 +38,10 @@ test('Capture logs', async (t) => {
3938
t.teardown(() => mockroot.clean())
4039

4140
const php = new Php({
42-
argv: process.argv,
4341
docroot: mockroot.path
4442
})
4543

4644
const req = new Request({
47-
method: 'GET',
4845
url: 'http://example.com/index.php'
4946
})
5047

@@ -62,12 +59,10 @@ test('Capture exceptions', async (t) => {
6259
t.teardown(() => mockroot.clean())
6360

6461
const php = new Php({
65-
argv: process.argv,
6662
docroot: mockroot.path
6763
})
6864

6965
const req = new Request({
70-
method: 'GET',
7166
url: 'http://example.com/index.php'
7267
})
7368

@@ -89,12 +84,10 @@ test('Support request and response headers', async (t) => {
8984
t.teardown(() => mockroot.clean())
9085

9186
const php = new Php({
92-
argv: process.argv,
9387
docroot: mockroot.path
9488
})
9589

9690
const req = new Request({
97-
method: 'GET',
9891
url: 'http://example.com/index.php',
9992
headers: {
10093
'X-Test': ['Hello, from Node.js!']
@@ -106,3 +99,33 @@ test('Support request and response headers', async (t) => {
10699
t.is(res.body.toString(), 'Hello, from Node.js!')
107100
t.is(res.headers.get('X-Test'), 'Hello, from PHP!')
108101
})
102+
103+
test('Has expected args', async (t) => {
104+
const mockroot = await MockRoot.from({
105+
'index.php': `<?php
106+
echo "[";
107+
$first = true;
108+
foreach ($argv as $value) {
109+
if ($first) { $first = false; }
110+
else { echo ","; }
111+
echo "\\"$value\\"";
112+
}
113+
echo "]";
114+
?>`
115+
})
116+
t.teardown(() => mockroot.clean())
117+
118+
const php = new Php({
119+
argv: process.argv,
120+
docroot: mockroot.path
121+
})
122+
123+
const req = new Request({
124+
url: 'http://example.com/index.php'
125+
})
126+
127+
const res = await php.handleRequest(req)
128+
t.is(res.status, 200)
129+
130+
t.is(res.body.toString('utf8'), JSON.stringify(process.argv))
131+
})

crates/php/src/embed.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use std::{
22
env::Args,
3-
ffi::c_char,
3+
ffi::{c_char, CString},
44
path::{Path, PathBuf},
5+
sync::Arc,
56
};
67

78
use ext_php_rs::{
@@ -25,6 +26,14 @@ use crate::{
2526
#[derive(Debug)]
2627
pub struct Embed {
2728
docroot: PathBuf,
29+
30+
// TODO: Do something with this...
31+
#[allow(dead_code)]
32+
args: Vec<String>,
33+
34+
// NOTE: This needs to hold the SAPI to keep it alive
35+
#[allow(dead_code)]
36+
sapi: Arc<Sapi>,
2837
}
2938

3039
// An embed instance may be constructed on the main thread and then shared
@@ -90,14 +99,16 @@ impl Embed {
9099
C: AsRef<Path>,
91100
S: AsRef<str> + std::fmt::Debug,
92101
{
93-
ensure_sapi(argv)?;
94-
95102
let docroot = docroot
96103
.as_ref()
97104
.canonicalize()
98105
.map_err(|_| EmbedException::DocRootNotFound(docroot.as_ref().display().to_string()))?;
99106

100-
Ok(Embed { docroot })
107+
Ok(Embed {
108+
docroot,
109+
args: argv.iter().map(|v| v.as_ref().to_string()).collect(),
110+
sapi: ensure_sapi()?,
111+
})
101112
}
102113

103114
/// Get the docroot used for this Embed instance
@@ -152,12 +163,8 @@ impl Handler for Embed {
152163
/// //assert_eq!(response.body(), "Hello, world!");
153164
/// ```
154165
fn handle(&self, request: Request) -> Result<Response, Self::Error> {
155-
unsafe {
156-
ext_php_rs::embed::ext_php_rs_sapi_per_thread_init();
157-
}
158-
159166
// Initialize the SAPI module
160-
Sapi::startup()?;
167+
self.sapi.startup()?;
161168

162169
let url = request.url();
163170

@@ -182,6 +189,14 @@ impl Handler for Embed {
182189
.unwrap_or(0);
183190
let cookie_data = nullable_cstr(headers.get("Cookie"))?;
184191

192+
let argc = self.args.len() as i32;
193+
let mut argv_ptrs = vec![];
194+
for arg in self.args.iter() {
195+
let string = CString::new(arg.as_bytes())
196+
.map_err(|_| EmbedException::CStringEncodeFailed(arg.to_owned()))?;
197+
argv_ptrs.push(string.into_raw());
198+
}
199+
185200
// Prepare memory stream of the code
186201
let mut file_handle = unsafe {
187202
let mut file_handle = zend_file_handle {
@@ -214,8 +229,8 @@ impl Handler for Embed {
214229

215230
// Reset state
216231
globals.request_info.proto_num = 110;
217-
globals.request_info.argc = 0;
218-
globals.request_info.argv = std::ptr::null_mut();
232+
globals.request_info.argc = argc;
233+
globals.request_info.argv = argv_ptrs.as_mut_ptr();
219234
globals.request_info.headers_read = false;
220235
globals.sapi_headers.http_response_code = 200;
221236

0 commit comments

Comments
 (0)