Skip to content

Commit 9ec3e41

Browse files
committed
feat: initial commit
0 parents  commit 9ec3e41

14 files changed

+653
-0
lines changed

.github/workflows/ci.yml

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: ext-picohttpparser CI
2+
on:
3+
push:
4+
branches:
5+
- develop
6+
jobs:
7+
build:
8+
runs-on: 'ubuntu-latest'
9+
strategy:
10+
fail-fast: false
11+
matrix:
12+
php: ['8.1', '8.2', '8.3', '8.4']
13+
name: PHP ${{ matrix.php }}
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: Install PHP
17+
uses: shivammathur/setup-php@master
18+
with:
19+
php-version: ${{ matrix.php }}
20+
- name: Run tests
21+
run: |
22+
git clone https://github.com/h2o/picohttpparser.git && \
23+
phpize && ./configure --with-picohttpparser="$(pwd)/picohttpparser" && \
24+
make && make test

.gitignore

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
*.lo
2+
*.loT
3+
*.la
4+
*.dep
5+
*.in~
6+
*.libs
7+
*acinclude.m4
8+
*aclocal.m4
9+
*autom4te.cache
10+
*build
11+
*config.guess
12+
*config.h
13+
*config.h.in
14+
*config.log
15+
*config.nice
16+
*config.status
17+
*config.sub
18+
*configure
19+
*configure.ac
20+
*configure.in
21+
*include
22+
*install-sh
23+
*libtool
24+
*ltmain.sh
25+
*Makefile
26+
*Makefile.fragments
27+
*Makefile.global
28+
*Makefile.objects
29+
*missing
30+
*mkinstalldirs
31+
*modules
32+
*php_test_results_*.txt
33+
*phpt.*
34+
*run-test-info.php
35+
*run-tests.php
36+
*tests/**/*.diff
37+
*tests/**/*.out
38+
*tests/**/*.exp
39+
*tests/**/*.log
40+
*tests/**/*.sh
41+
*tests/**/*.db
42+
*tests/**/*.mem
43+
*tmp-php.ini
44+
*TODO

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Lochemem Bruno Michael
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

+165
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# ext-picohttpparser
2+
3+
A PHP port of the fast HTTP parser used in the [H2O project](https://github.com/h2o/h2o).
4+
5+
## Rationale
6+
7+
The picohttpparser project is a tiny, (near) stateless HTTP parser. The performance gains associated with the library are largely attributable to its zero-copy internals: it is almost entirely reliant on stack-allocated data structures. `ext-picohttpparser` is an attempt to expose the API of the aforedescribed eponymous lightweight, efficient parser to the PHP userspace.
8+
9+
## Requirements
10+
11+
- PHP 8.1 or newer
12+
- [picohttpparser](https://github.com/h2o/picohttpparser)
13+
14+
## Installation
15+
16+
It is important to have all the aforelisted requirements at the ready before attempting to install `ext-picohttpparser`. The directives in the snippet to follow should allow you to build the extension's shared object file (`picohttpparser.so`).
17+
18+
```sh
19+
$ git clone https://github.com/h2o/picohttpparser.git <picohttp-dir>
20+
$ git clone https://github.com/ringphp/php-picohttpparser.git <dir>
21+
$ cd <dir>
22+
$ phpize
23+
$ ./configure --with-picohttpparser=<picohttp-dir>
24+
$ make && sudo make install
25+
```
26+
27+
After successfully building the shared object, proceed to operationalize the extension by adding the line `extension=picohttpparser` to your `php.ini` file. If you prefer to perform the said operationalization via command line interface, the following should suffice.
28+
29+
```sh
30+
$ printf "\nextension=picohttpparser\n" >> "$(php-config --ini-path)/php.ini"
31+
```
32+
33+
## API Synopsis
34+
35+
```php
36+
/* core functions */
37+
picohttp_parse_request(string $request, int $header_limit = 100): array
38+
picohttp_parse_response(string $response, int $header_limit = 100): array
39+
```
40+
41+
- [picohttp_parse_request](#picohttp_parse_request)
42+
- [picohttp_parse_response](#picohttp_parse_response)
43+
44+
### `parse_http_request`
45+
46+
```php
47+
picohttp_parse_request(string $request, int $header_limit = 100): array
48+
```
49+
50+
Parses an HTTP request.
51+
52+
**Parameter(s)**
53+
54+
- **request** (string) - The HTTP request to parse.
55+
- **header_limit** (int) - The number of headers to parse.
56+
> The default limit is `100`.
57+
58+
**Return value(s)**
59+
60+
The parser will throw an exception in the event that an invalid HTTP request is encountered and will output a hashtable with the contents enumerated below otherwise.
61+
62+
- **body** (string) - The request body.
63+
- **headers** (iterable) - An associative array containing request headers.
64+
- **method** (string) - The request method.
65+
- **path** (string) - The request path.
66+
67+
```php
68+
$request = <<<EOF
69+
GET / HTTP/1.1
70+
host: localhost:8080
71+
accept: */*
72+
73+
74+
EOF;
75+
76+
var_dump(
77+
\picohttp_parse_request($request),
78+
);
79+
```
80+
81+
The example above will produce output similar to that in the snippet to follow.
82+
83+
```
84+
array(4) {
85+
["path"]=>
86+
string(1) "/"
87+
["method"]=>
88+
string(3) "GET"
89+
["body"]=>
90+
string(0) ""
91+
["headers"]=>
92+
array(2) {
93+
["host"]=>
94+
string(14) "localhost:8080"
95+
["accept"]=>
96+
string(3) "*/*"
97+
}
98+
}
99+
```
100+
101+
### `picohttp_parse_response`
102+
103+
```php
104+
picohttp_parse_response(string $response, int $header_limit = 100): array
105+
```
106+
107+
Parses an HTTP response.
108+
109+
**Parameter(s)**
110+
111+
- **response** (string) - The HTTP response to parse.
112+
- **header_limit** (int) - The number of headers to parse.
113+
> The default limit is `100`.
114+
115+
**Return value(s)**
116+
117+
The parser will throw an exception in the event that an invalid HTTP response is encountered and will output a hashtable with the contents enumerated below otherwise.
118+
119+
- **body** (string) - The response body.
120+
- **headers** (iterable) - An associative array containing response headers.
121+
- **status** (int) - The response status code.
122+
- **reason** (string) - The response reason phrase.
123+
124+
```php
125+
$response = <<<EOF
126+
HTTP/1.1 200 OK
127+
server: ringphp
128+
content-type: application/json; charset=utf-8
129+
content-length: 37
130+
131+
{
132+
"foo": "foo",
133+
"bar": "bar"
134+
}
135+
EOF;
136+
137+
var_dump(
138+
\picohttp_parse_response($response),
139+
);
140+
```
141+
142+
The example above will produce output similar to that in the snippet to follow.
143+
144+
```
145+
array(4) {
146+
["reason"]=>
147+
string(2) "OK"
148+
["status"]=>
149+
int(200)
150+
["body"]=>
151+
string(37) "{
152+
"foo": "foo",
153+
"bar": "bar"
154+
}"
155+
["headers"]=>
156+
array(3) {
157+
["server"]=>
158+
string(7) "ringphp"
159+
["content-type"]=>
160+
string(16) "application/json; charset=utf-8"
161+
["content-length"]=>
162+
string(2) "37"
163+
}
164+
}
165+
```

config.m4

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
dnl picohttpparser extension for PHP (c) 2024 Lochemem Bruno Michael
2+
3+
PHP_ARG_WITH([picohttpparser],
4+
[for picohttpparser library],
5+
[AS_HELP_STRING([--with-picohttpparser],
6+
[specify path to picohttpparser library])],
7+
[no])
8+
9+
if test "$PHP_PICOHTTPPARSER" != "no"; then
10+
PHP_VERSION=$($PHP_CONFIG --vernum)
11+
AC_MSG_CHECKING([PHP version])
12+
if test $PHP_VERSION -lt 80100; then
13+
AC_MSG_ERROR([ext-picohttpparser requires PHP 8.1+])
14+
else
15+
AC_MSG_RESULT([ok])
16+
fi
17+
18+
AC_MSG_CHECKING([for picohttpparser library])
19+
if test -s "$PHP_PICOHTTPPARSER/picohttpparser.c"; then
20+
AC_MSG_RESULT(found picohttpparser library)
21+
else
22+
AC_MSG_RESULT(picohttpparser is not downloaded)
23+
AC_MSG_ERROR(Please download picohttpparser)
24+
fi
25+
26+
CFLAGS="-g -O3 -I$PHP_PICOHTTPPARSER/"
27+
AC_DEFINE(HAVE_PICOHTTPPARSER, 1, [Have picohttpparser support])
28+
29+
PHP_NEW_EXTENSION(picohttpparser, php_picohttpparser.c, $ext_shared)
30+
fi

php_picohttpparser.c

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* picohttpparser extension for PHP (c) 2024 Lochemem Bruno Michael */
2+
#ifdef HAVE_CONFIG_H
3+
#include "config.h"
4+
#endif
5+
6+
#include "src/pico.c"
7+
#include "php_picohttpparser.h"
8+
#include "picohttpparser_arginfo.h"
9+
10+
/* {{{ picohttp_parse_request( string request [, int header_limit = 100 ] ) */
11+
PHP_FUNCTION(picohttp_parse_request)
12+
{
13+
parse_http_request(INTERNAL_FUNCTION_PARAM_PASSTHRU);
14+
}
15+
/* }}} */
16+
17+
/* {{{ picohttp_parse_response( string request [, int header_limit = 100 ] ) */
18+
PHP_FUNCTION(picohttp_parse_response)
19+
{
20+
parse_http_response(INTERNAL_FUNCTION_PARAM_PASSTHRU);
21+
}
22+
/* }}} */
23+
24+
/* {{{ PHP_MINIT_FUNCTION */
25+
PHP_MINIT_FUNCTION(picohttpparser)
26+
{
27+
return SUCCESS;
28+
}
29+
/* }}} */
30+
31+
/* {{{ PHP_RINIT_FUNCTION */
32+
PHP_RINIT_FUNCTION(picohttpparser)
33+
{
34+
#if defined(ZTS) && defined(COMPILE_DL_PICOHTTPPARSER)
35+
ZEND_TSRMLS_CACHE_UPDATE();
36+
#endif
37+
38+
return SUCCESS;
39+
}
40+
/* }}} */
41+
42+
/* {{{ PHP_MINFO_FUNCTION */
43+
PHP_MINFO_FUNCTION(picohttpparser)
44+
{
45+
php_info_print_table_start();
46+
php_info_print_table_header(2, "picohttpparser support", "enabled");
47+
php_info_print_table_header(2, "picohttpparser version", PHP_PICOHTTPPARSER_VERSION);
48+
php_info_print_table_header(2, "picohttpparser author", PHP_PICOHTTPPARSER_AUTHOR);
49+
php_info_print_table_end();
50+
}
51+
/* }}} */
52+
53+
zend_module_entry picohttpparser_module_entry = {
54+
STANDARD_MODULE_HEADER,
55+
"picohttpparser", /* extension name */
56+
picohttpparser_functions, /* zend_function_entry */
57+
NULL, /* PHP_MINIT - module initialization */
58+
NULL, /* PHP_MSHUTDOWN - module shutdown */
59+
PHP_RINIT(picohttpparser), /* PHP_RINIT - request initialization */
60+
NULL, /* PHP_RSHUTDOWN - request shutdown */
61+
PHP_MINFO(picohttpparser), /* PHP_MINFO - module information */
62+
PHP_PICOHTTPPARSER_VERSION, /* module version */
63+
STANDARD_MODULE_PROPERTIES};
64+
65+
#ifdef COMPILE_DL_PICOHTTPPARSER
66+
#ifdef ZTS
67+
ZEND_TSRMLS_CACHE_DEFINE()
68+
#endif
69+
ZEND_GET_MODULE(picohttpparser)
70+
#endif

php_picohttpparser.h

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* picohttpparser extension for PHP (c) 2024 Lochemem Bruno Michael */
2+
#ifndef PHP_PICOHTTPPARSER_H
3+
#define PHP_PICOHTTPPARSER_H
4+
5+
extern zend_module_entry picohttpparser_module_entry;
6+
7+
#define PHP_PICOHTTPPARSER_VERSION "0.1.0"
8+
#define PHP_PICOHTTPPARSER_AUTHOR "Lochemem Bruno Michael"
9+
10+
#if defined(ZTS) && defined(COMPILE_DL_PICOHTTPPARSER)
11+
ZEND_TSRMLS_CACHE_EXTERN()
12+
#endif
13+
14+
#endif /* PHP_PICOHTTPPARSER_H */

picohttpparser_arginfo.h

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* picohttpparser extension for PHP (c) 2024 Lochemem Bruno Michael */
2+
ZEND_BEGIN_ARG_INFO_EX(arginfo_picohttp_parse_request, 0, 0, 2)
3+
ZEND_ARG_TYPE_INFO(0, request, IS_STRING, 0)
4+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, header_limit, IS_LONG, 0, "100")
5+
ZEND_END_ARG_INFO()
6+
7+
ZEND_BEGIN_ARG_INFO_EX(arginfo_picohttp_parse_response, 0, 0, 2)
8+
ZEND_ARG_TYPE_INFO(0, response, IS_STRING, 0)
9+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, header_limit, IS_LONG, 0, "100")
10+
ZEND_END_ARG_INFO()
11+
12+
ZEND_FUNCTION(picohttp_parse_request);
13+
ZEND_FUNCTION(picohttp_parse_response);
14+
15+
static const zend_function_entry picohttpparser_functions[] = {
16+
ZEND_FE(picohttp_parse_request, arginfo_picohttp_parse_request)
17+
ZEND_FE(picohttp_parse_response, arginfo_picohttp_parse_response)
18+
PHP_FE_END};

0 commit comments

Comments
 (0)