From 79778ceb52dedff41b4b5e054e75b23c78a0f56f Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Mon, 26 May 2025 18:58:06 +0800 Subject: [PATCH] Improve docs --- LICENSE | 201 +++++++++++ README.md | 571 +++++++++++++++++++++++++++++--- crates/php_node/src/headers.rs | 37 +-- crates/php_node/src/request.rs | 7 +- crates/php_node/src/response.rs | 11 +- crates/php_node/src/runtime.rs | 33 +- index.d.ts | 33 +- 7 files changed, 802 insertions(+), 91 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 8f2f7019..8ba8a098 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,16 @@ -# @platformatic/php +# @platformatic/php-node -Delegate handling of HTTP requests to a thread pool of PHP instances. +With `@platformatic/php-node` you can run PHP applications within the same +process as a Node.js application, allowing for communication between Node.js +and PHP without any network connection in the middle. This allows for some +interesting possibilities, like running Wordpress with a Next.js frontend. ## Requirements +Presently support is provided for x64 Linux and both x64 and arm64 macOS. More +platforms will come as needs arise. Please open an issue if we're missing a +platform you would like supported! + PHP dynamically links against several system libraries. These must be installed as listed below: @@ -24,55 +31,541 @@ brew install openssl@3 curl sqlite libxml2 oniguruma ## Install ```sh -npm install @platformatic/php +npm install @platformatic/php-node ``` ## Usage ```js -import { Php, Request } from '@platformatic/php' +import { Php, Request } from '@platformatic/php-node' -// Construct a PHP environment for handling requests. -// This corresponds to a single entrypoint file. -// Presently the file contents must be passed in as a string, -// but it could be made to take only a filename and read the file -// contents itself. -const php = new Php({ - file: 'index.php', - code: `` -}) +const php = new Php() -// This is a container to help translate Node.js requests into PHP requests. -const req = new Request({ - method: 'GET', - url: 'http://example.com/test.php', +const request = new Request({ + url: 'http://example.com/foo/bar', headers: { 'X-Test': ['Hello, from Node.js!'] } }) -// The request container gets passed into the PHP environment which processes -// it and returns a response. Request processing is handled by the NodePlatform -// worker pool to avoid blocking the Node.js thread. -const res = await php.handleRequest(req) - -// PHP requests can also be processed synchronously, though this is not -// recommended as it will block the Node.js thread for the entire life of the -// PHP request. It may be useful in some cases though. -const res = php.handleRequestSync(req) - -// Properties available on Response objects: -console.log({ - status: res.status, // status code - headers: new Map(res.headers.entries()), // headers is a Headers object - body: res.body.toString(), // body is a Buffer - log: res.log.toString(), // log is a Buffer - exception: res.exception // exception is a string +const response = await php.handleRequest(request) + +console.log(response.body.toString()) +``` + +## API + +### `new Php(config)` + +* `config` {Object} Configuration object + * `docroot` {String} Document root for PHP. **Default:** process.cwd() +* Returns: {Php} + +Construct a new PHP instance to which to dispatch requests. + +```js +import { Php } from '@platformatic/php-node' + +const php = new Php({ + docroot: process.cwd() +}) +```` + +### `php.handleRequest(request)` + +* `request` {Request} A request to dispatch to the PHP instance. +* Returns: {Promise} + +When the request completes, the returned promise will resolve with the response +object. Request processing is handled by the NodePlatform worker pool to avoid +blocking the Node.js thread. + +```js +import { Php, Request } from '@platformatic/php-node' + +const php = new Php() +const request = new Request({ + url: 'http://example.com/foo/bar' +}) + +const response = await php.handleRequest(request) +console.log(response.body.toString()) +```` + +### `php.handleRequestSync(request)` + +* `request` {Request} A request to dispatch to the PHP instance. +* Returns: {Response} + +Requests may also be processed synchronously, though this is not recommended as +it will block the Node.js thread for the entire life of the PHP request. + +This may be useful for one-off scripts. It's only included because it's trivial +to do so, but it's not recommended for use within HTTP requests. + +```js +import { Php, Request } from '@platformatic/php-node' + +const php = new Php() +const request = new Request({ + url: 'http://example.com/foo/bar' +}) + +const response = php.handleRequestSync(request) +console.log(response.body.toString()) +``` + +### `new Request(input)` + +* `input` + * `method` {String} HTTP method **Default:** `GET` + * `url` {String} Full request URL + * `headers` {Object} HTTP request headers. Each must be an array of strings + * `body` {Buffer|UInt8Array} Request body +* Returns: {Request} + +Construct a request which may be dispatched to a PHP instance. + +```js +import { Request } from '@platformatic/php-node' + +const request = new Request({ + method: 'POST', + url: 'http://example.com/foo/bar', + headers: { + 'Content-Type': ['application/json'] + }, + body: Buffer.from(JSON.stringify({ + hello: 'world' + })) +}) +``` + +### `request.method` + +* {String} + +The HTTP method to use when dispatching this request. + +```js +import { Request } from '@platformatic/php-node' + +const request = new Request({ + url: 'http://example.com/foo/bar', }) -// Headers is a multimap which implements all the standard Map methods plus -// some additional helpers. See the tests in __test__ for more details. +console.log(request.method) // GET +``` + +### `request.url` + +* {String} + +The URL to use when dispatching this request. + +```js +import { Request } from '@platformatic/php-node' + +const request = new Request({ + url: 'http://example.com/foo/bar', +}) + +console.log(request.url) // http://example.com/foo/bar +``` + +### `request.headers` + +* {Headers} + +The HTTP headers to use when dispatching this request. + +```js +import { Request } from '@platformatic/php-node' + +const request = new Request({ + url: 'http://example.com/foo/bar', +}) + +console.log(request.headers) // [Headers] +``` + +### `request.body` + +* {Buffer} + +The body to use when dispatching this request. + +```js +import { Request } from '@platformatic/php-node' + +const request = new Request({ + url: 'http://example.com/foo/bar', + body: Buffer.from('Hello, world!') +}) + +console.log(request.body.toString()) // Hello, world! +``` + +### `new Response(input)` + +* `input` {Object} Response values. + * `status` {Number} HTTP Response status code + * `headers` {Object} HTTP Response headers. Each must be an array of strings + * `body` {Buffer} HTTP Response body + * `log` {String} Log output of this request +* Returns: {Response} + +Responses may be constructed manually. This is mainly just for testing, but may +have other uses, like short-circuiting the PHP instance run entirely in certain +cases. + +```js +import { Response } from '@platformatic/php-node' + +const response = new Response({ + status: 500, + headers: { + 'Content-Type': ['application/json'] + }, + body: Buffer.from(JSON.stringify({ + error: 'bad stuff' + })) +}) +``` + +### `response.status` + +* {Number} + +The HTTP status code included in the response. + +```js +import { Response } from '@platformatic/php-node' + +const response = new Response({ + status: 500 +}) + +console.log(response.status) // 500 +``` + +### `response.headers` + +* {Headers} + +The HTTP headers included in the response. + +```js +import { Response } from '@platformatic/php-node' + +const response = new Response({ + headers: { + 'Content-Type': ['application/json'] + }, +}) + +console.log(response.headers) // [Headers] +``` + +### `response.body` + +* {Buffer} + +The HTTP response body. + +```js +import { Response } from '@platformatic/php-node' + +const response = new Response({ + body: Buffer.from(JSON.stringify({ + error: 'bad stuff' + })) +}) + +console.log(response.body.toString()) // {"error":"bad stuff"} +``` + +### `response.log` + +* {Buffer} + +Any logs captured during the request. + +```js +import { Response } from '@platformatic/php-node' + +const response = new Response({ + log: Buffer.from('some log message') +}) + +console.log(response.log.toString()) // some log message +``` + +### `new Headers()` + +* Returns: {Headers} + +Construct a Headers object to manage HTTP headers. Note that this is currently +only useful for reading _from_ Request and Response types, not passing _into_ +them. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() +``` + +### `headers.set(name, value)` + +* `name` {String} The header name for which to set a value. +* `value` {String} The value to set for the named header. + +This will set the value of the named header. If any prior headers have been +set with this name they will be discarded. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +``` + +### `headers.add(name, value)` + +* `name` {String} The header name for which to add a value. +* `value` {String} The value to add for the named header. + +This will add to the associated values of the named header. If any prior +headers have been set with this name they will also be kept. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') +``` + +### `headers.has(name)` + +* Returns: {bool} + +Checks if there are any values currently associated with the header name. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.has('Content-Type') // false +``` + +### `headers.get(name)` + +* Returns: {string|undefined} + +Retrieves the last value associated with the given header name. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +headers.get('Accept') // text/html +``` + +### `headers.getAll(name)` + +* Returns: {String[]} + +Retrieves all values associated with the given header name. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +headers.getAll('Accept') // ['application/json', 'text/html'] ``` + +### `headers.getLine(name)` + +* Returns: {String|undefined} + +Merges all associated values into one header line. Note that his may be +incorrect for some header types which require separate header lines such as +the `Set-Cookie` header. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +headers.getLine('Accept') // application/json, text/html +``` + +### `headers.delete(name)` + +Delete all values associated with the given header name. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') +headers.delete('Accept') + +headers.get('Accept') // undefined +``` + +### `headers.clear()` + +Remove all contained headers. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.clear() + +headers.get('Content-Type') // undefined +headers.get('Accept') // undefined +``` + +### `headers.size` + +* {Number} + +The number of header names present. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +headers.size // 3 +``` + +### `headers.entries()` + +* {Iterator} + +Returns an iterator containing a `(name, value)` tuple of header entries. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +for (const (name, value) of headers.entries()) { + // ('Content-Type', 'application/json') + // ('Accept', 'application/json') + // ('Accept', 'text/html') +} +``` + +### `headers.keys()` + +* {Iterator} + +Returns an iterator of header names. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +for (const name of headers.keys()) { + // 'Content-Type' + // 'Accept' +} +``` + +### `headers.values()` + +* {Iterator} + +Returns an iterator of header values. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +for (const value of headers.values()) { + // 'application/json' + // 'application/json' + // 'text/html' +} +``` + +### `headers.forEach(fn)` + +* `fn` {Function} Callback to call for each header entry + * `value` {String} The value of the header entry. + * `name` {String} The name of the header entry. + * `headers` {Headers} The Header instance + +Iterate over each header entry with a given callback. + +```js +import { Headers } from '@platformatic/php-node' + +const headers = new Headers() + +headers.set('Content-Type', 'application/json') +headers.add('Accept', 'application/json') +headers.add('Accept', 'text/html') + +headers.forEach((value, name, headers) => { + // ('application/json', 'Content-Type', headers) + // ('application/json', 'Accept', headers) + // ('text/html', 'Accept', headers) +}) +``` + +## Contributing + +This project is part of the [Platformatic](https://github.com/platformatic) ecosystem. Please refer to the main repository for contribution guidelines. + +## License + +Apache-2.0 + +## Support + +- [GitHub Issues](https://github.com/platformatic/php-node/issues) +- [Platformatic Documentation](https://docs.platformatic.dev/) +- [Community Discord](https://discord.gg/platformatic) diff --git a/crates/php_node/src/headers.rs b/crates/php_node/src/headers.rs index 4928e1ac..4bc2cb5c 100644 --- a/crates/php_node/src/headers.rs +++ b/crates/php_node/src/headers.rs @@ -236,23 +236,21 @@ impl PhpHeaders { /// headers.set('Content-Type', 'application/json'); /// headers.set('Accept', 'application/json'); /// - /// for (const [key, values] of headers.entries()) { - /// console.log(`${key}: ${values.join(', ')}`); + /// for (const [name, value] of headers.entries()) { + /// console.log(`${name}: ${value}`); /// } /// ``` #[napi] - pub fn entries(&self) -> Vec>> { + pub fn entries(&self) -> Vec> { self .headers .iter() - .map(|(k, v)| { - Entry( - k.to_owned(), - match v { - Header::Single(value) => vec![value.clone()], - Header::Multiple(vec) => vec.clone(), - }, - ) + .flat_map(|(k, v)| match v { + Header::Single(value) => vec![Entry(k.to_owned(), value.clone())], + Header::Multiple(vec) => vec + .iter() + .map(|value| Entry(k.to_owned(), value.clone())) + .collect::>>(), }) .collect() } @@ -266,8 +264,8 @@ impl PhpHeaders { /// headers.set('Content-Type', 'application/json'); /// headers.set('Accept', 'application/json'); /// - /// for (const key of headers.keys()) { - /// console.log(key); + /// for (const name of headers.keys()) { + /// console.log(name); /// } /// ``` #[napi] @@ -290,11 +288,7 @@ impl PhpHeaders { /// ``` #[napi] pub fn values(&self) -> Vec { - self - .entries() - .into_iter() - .flat_map(|entry| entry.1) - .collect() + self.entries().into_iter().map(|entry| entry.1).collect() } /// Execute a callback for each header entry. @@ -306,18 +300,17 @@ impl PhpHeaders { /// headers.set('Content-Type', 'application/json'); /// headers.set('Accept', 'application/json'); /// - /// headers.forEach(([key, values]) => { - /// console.log(`${key}: ${values.join(', ')}`); + /// headers.forEach((value, name, headers) => { + /// console.log(`${name}: ${value}`); /// }); /// ``` #[napi] - pub fn for_each, String, &This) -> Result<()>>( + pub fn for_each Result<()>>( &self, this: This, callback: F, ) -> Result<()> { for entry in self.entries() { - let entry = Entry(entry.0, entry.1); callback(entry.1, entry.0, &this)?; } Ok(()) diff --git a/crates/php_node/src/request.rs b/crates/php_node/src/request.rs index 3a9c19c4..e9e9c1a3 100644 --- a/crates/php_node/src/request.rs +++ b/crates/php_node/src/request.rs @@ -25,7 +25,7 @@ pub struct PhpRequestSocketOptions { #[derive(Default)] pub struct PhpRequestOptions { /// The HTTP method for the request. - pub method: String, + pub method: Option, /// The URL for the request. pub url: String, /// The headers for the request. @@ -79,10 +79,13 @@ impl PhpRequest { #[napi(constructor)] pub fn constructor(options: PhpRequestOptions) -> Result { let mut builder: RequestBuilder = Request::builder() - .method(options.method) .url(&options.url) .map_err(|_| Error::from_reason(format!("Invalid URL \"{}\"", options.url)))?; + if let Some(method) = options.method { + builder = builder.method(method) + } + if let Some(socket) = options.socket { let local_socket = format!("{}:{}", socket.local_address, socket.local_port); let remote_socket = format!("{}:{}", socket.remote_address, socket.remote_port); diff --git a/crates/php_node/src/response.rs b/crates/php_node/src/response.rs index 48494e99..2ae9f77a 100644 --- a/crates/php_node/src/response.rs +++ b/crates/php_node/src/response.rs @@ -9,9 +9,10 @@ use crate::PhpHeaders; /// Options for creating a new PHP response. #[napi(object)] +#[derive(Default)] pub struct PhpResponseOptions { /// The HTTP status code for the response. - pub status: i32, + pub status: Option, /// The headers for the response. /// TODO: Figure out how to accept a Headers instance /// TODO: Figure out how to support both single values without array wrap @@ -53,9 +54,13 @@ impl PhpResponse { /// }); /// ``` #[napi(constructor)] - pub fn constructor(options: PhpResponseOptions) -> Result { + pub fn constructor(options: Option) -> Result { + let options = options.unwrap_or_default(); let mut builder = Response::builder(); - builder.status(options.status); + + if let Some(status) = options.status { + builder.status(status); + } if let Some(headers) = options.headers { for key in headers.keys() { diff --git a/crates/php_node/src/runtime.rs b/crates/php_node/src/runtime.rs index 6eb424a2..f0e02901 100644 --- a/crates/php_node/src/runtime.rs +++ b/crates/php_node/src/runtime.rs @@ -12,9 +12,9 @@ use crate::{PhpRequest, PhpResponse}; #[derive(Clone, Default)] pub struct PhpOptions { /// The command-line arguments for the PHP instance. - pub argv: Vec, + pub argv: Option>, /// The document root for the PHP instance. - pub docroot: String, + pub docroot: Option, } /// A PHP instance. @@ -47,16 +47,27 @@ impl PhpRuntime { /// /// ```js /// const php = new Php({ - /// code: 'echo "Hello, world!";' + /// docroot: process.cwd(), + /// argv: process.argv /// }); /// ``` #[napi(constructor)] - pub fn new(options: PhpOptions) -> Result { - let docroot = options.docroot.clone(); - let argv = options.argv.clone(); + pub fn new(options: Option) -> Result { + let PhpOptions { docroot, argv } = options.unwrap_or_default(); - let embed = - Embed::new_with_argv(docroot, argv).map_err(|err| Error::from_reason(err.to_string()))?; + let docroot = docroot + .ok_or_else(|| { + std::env::current_dir() + .map(|s| s.display().to_string()) + .ok() + }) + .map_err(|_| Error::from_reason("Could not determine docroot"))?; + + let embed = match argv { + Some(argv) => Embed::new_with_argv(docroot, argv), + None => Embed::new(docroot), + } + .map_err(|err| Error::from_reason(err.to_string()))?; Ok(Self { embed: Arc::new(embed), @@ -69,7 +80,8 @@ impl PhpRuntime { /// /// ```js /// const php = new Php({ - /// code: 'echo "Hello, world!";' + /// docroot: process.cwd(), + /// argv: process.argv /// }); /// /// const response = php.handleRequest(new Request({ @@ -94,7 +106,8 @@ impl PhpRuntime { /// /// ```js /// const php = new Php({ - /// code: 'echo "Hello, world!";' + /// docroot: process.cwd(), + /// argv: process.argv /// }); /// /// const response = php.handleRequestSync(new Request({ diff --git a/index.d.ts b/index.d.ts index 401e4b95..8b99b670 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,7 +16,7 @@ export interface PhpRequestSocketOptions { /** Options for creating a new PHP request. */ export interface PhpRequestOptions { /** The HTTP method for the request. */ - method: string + method?: string /** The URL for the request. */ url: string /** @@ -33,7 +33,7 @@ export interface PhpRequestOptions { /** Options for creating a new PHP response. */ export interface PhpResponseOptions { /** The HTTP status code for the response. */ - status: number + status?: number /** * The headers for the response. * TODO: Figure out how to accept a Headers instance @@ -50,9 +50,9 @@ export interface PhpResponseOptions { /** Options for creating a new PHP instance. */ export interface PhpOptions { /** The command-line arguments for the PHP instance. */ - argv: Array + argv?: Array /** The document root for the PHP instance. */ - docroot: string + docroot?: string } export type PhpHeaders = Headers /** @@ -215,8 +215,8 @@ export declare class Headers { * headers.set('Content-Type', 'application/json'); * headers.set('Accept', 'application/json'); * - * for (const [key, values] of headers.entries()) { - * console.log(`${key}: ${values.join(', ')}`); + * for (const [name, value] of headers.entries()) { + * console.log(`${name}: ${value}`); * } * ``` */ @@ -231,8 +231,8 @@ export declare class Headers { * headers.set('Content-Type', 'application/json'); * headers.set('Accept', 'application/json'); * - * for (const key of headers.keys()) { - * console.log(key); + * for (const name of headers.keys()) { + * console.log(name); * } * ``` */ @@ -263,12 +263,12 @@ export declare class Headers { * headers.set('Content-Type', 'application/json'); * headers.set('Accept', 'application/json'); * - * headers.forEach(([key, values]) => { - * console.log(`${key}: ${values.join(', ')}`); + * headers.forEach((value, name, headers) => { + * console.log(`${name}: ${value}`); * }); * ``` */ - forEach(this: this, callback: (arg0: Array, arg1: string, arg2: this) => void): void + forEach(this: this, callback: (arg0: string, arg1: string, arg2: this) => void): void } export type PhpRequest = Request /** @@ -488,11 +488,12 @@ export declare class Php { * * ```js * const php = new Php({ - * code: 'echo "Hello, world!";' + * docroot: process.cwd(), + * argv: process.argv * }); * ``` */ - constructor(options: PhpOptions) + constructor(options?: PhpOptions | undefined | null) /** * Handle a PHP request. * @@ -500,7 +501,8 @@ export declare class Php { * * ```js * const php = new Php({ - * code: 'echo "Hello, world!";' + * docroot: process.cwd(), + * argv: process.argv * }); * * const response = php.handleRequest(new Request({ @@ -520,7 +522,8 @@ export declare class Php { * * ```js * const php = new Php({ - * code: 'echo "Hello, world!";' + * docroot: process.cwd(), + * argv: process.argv * }); * * const response = php.handleRequestSync(new Request({