mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Merge branch 'PHP-7.4'
* PHP-7.4: [ci skip] Fix CS in Markdown files
This commit is contained in:
commit
cbca8a8df4
11 changed files with 1004 additions and 904 deletions
21
README.md
21
README.md
|
@ -48,23 +48,29 @@ PHP uses autotools on Unix systems to configure the build:
|
||||||
|
|
||||||
*See `make -h` for make options.*
|
*See `make -h` for make options.*
|
||||||
|
|
||||||
The `-j` option shall set the maximum number of jobs `make` can use for the build:
|
The `-j` option shall set the maximum number of jobs `make` can use for the
|
||||||
|
build:
|
||||||
|
|
||||||
make -j4
|
make -j4
|
||||||
|
|
||||||
Shall run `make` with a maximum of 4 concurrent jobs: Generally the maximum number of jobs should not exceed the number of cores available.
|
Shall run `make` with a maximum of 4 concurrent jobs: Generally the maximum
|
||||||
|
number of jobs should not exceed the number of cores available.
|
||||||
|
|
||||||
## Testing PHP source code
|
## Testing PHP source code
|
||||||
|
|
||||||
PHP ships with an extensive test suite, the command `make test` is used after successful compilation of the sources to run this test suite.
|
PHP ships with an extensive test suite, the command `make test` is used after
|
||||||
|
successful compilation of the sources to run this test suite.
|
||||||
|
|
||||||
It is possible to run tests using multiple cores by setting `-jN` in `TEST_PHP_ARGS`:
|
It is possible to run tests using multiple cores by setting `-jN` in
|
||||||
|
`TEST_PHP_ARGS`:
|
||||||
|
|
||||||
make TEST_PHP_ARGS=-j4 test
|
make TEST_PHP_ARGS=-j4 test
|
||||||
|
|
||||||
Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum number of jobs should not exceed the number of cores available.
|
Shall run `make test` with a maximum of 4 concurrent jobs: Generally the maximum
|
||||||
|
number of jobs should not exceed the number of cores available.
|
||||||
|
|
||||||
The [qa.php.net](https://qa.php.net) site provides more detailed info about testing and quality assurance.
|
The [qa.php.net](https://qa.php.net) site provides more detailed info about
|
||||||
|
testing and quality assurance.
|
||||||
|
|
||||||
## Installing PHP built from source
|
## Installing PHP built from source
|
||||||
|
|
||||||
|
@ -72,7 +78,8 @@ After a successful build (and test), PHP may be installed with:
|
||||||
|
|
||||||
make install
|
make install
|
||||||
|
|
||||||
Depending on your permissions and prefix, `make install` may need super user permissions.
|
Depending on your permissions and prefix, `make install` may need super user
|
||||||
|
permissions.
|
||||||
|
|
||||||
## PHP extensions
|
## PHP extensions
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,23 @@
|
||||||
# Input Filter Support in PHP 5
|
# Input filter support in PHP
|
||||||
|
|
||||||
XSS (Cross Site Scripting) hacks are becoming more and more prevalent,
|
XSS (Cross Site Scripting) hacks are becoming more and more prevalent, and can
|
||||||
and can be quite difficult to prevent. Whenever you accept user data
|
be quite difficult to prevent. Whenever you accept user data and somehow display
|
||||||
and somehow display this data back to users, you are likely vulnerable
|
this data back to users, you are likely vulnerable to XSS hacks.
|
||||||
to XSS hacks.
|
|
||||||
|
|
||||||
The Input Filter support in PHP 5 is aimed at providing the framework
|
The Input Filter support in PHP is aimed at providing the framework through
|
||||||
through which a company-wide or site-wide security policy can be
|
which a company-wide or site-wide security policy can be enforced. It is
|
||||||
enforced. It is implemented as a SAPI hook and is called from the
|
implemented as a SAPI hook and is called from the `treat_data` and post handler
|
||||||
treat_data and post handler functions. To implement your own security
|
functions. To implement your own security policy you will need to write a
|
||||||
policy you will need to write a standard PHP extension. There is also
|
standard PHP extension. There is also a powerful standard implementation in
|
||||||
a powerful standard implementation in ext/filter that should suit most
|
`ext/filter` that should suit most peoples' needs. However, if you want to
|
||||||
peoples' needs. However, if you want to implement your own security
|
implement your own security policy, read on.
|
||||||
policy, read on.
|
|
||||||
|
|
||||||
A simple implementation might look like the following. This stores the
|
A simple implementation might look like the following. This stores the original
|
||||||
original raw user data and adds a my_get_raw() function while the normal
|
raw user data and adds a `my_get_raw()` function while the normal `$_POST`,
|
||||||
$_POST, $_GET and $_COOKIE arrays are only populated with stripped
|
`$_GET` and `$_COOKIE` arrays are only populated with stripped data. In this
|
||||||
data. In this simple example all I am doing is calling strip_tags() on
|
simple example all I am doing is calling `strip_tags()` on the data.
|
||||||
the data.
|
|
||||||
|
|
||||||
```
|
```c
|
||||||
ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
|
ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
|
||||||
zval *post_array;
|
zval *post_array;
|
||||||
zval *get_array;
|
zval *get_array;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Mailinglist Rules
|
# Mailinglist rules
|
||||||
|
|
||||||
This is the first file you should be reading before doing any posts on PHP
|
This is the first file you should be reading before doing any posts on PHP
|
||||||
mailinglists. Following these rules is considered imperative to the success of
|
mailinglists. Following these rules is considered imperative to the success of
|
||||||
|
@ -19,7 +19,6 @@ following some basic rules with regards to mailinglist usage will:
|
||||||
|
|
||||||
d. Increase the general level of good will on planet Earth.
|
d. Increase the general level of good will on planet Earth.
|
||||||
|
|
||||||
|
|
||||||
Having said that, here are the organizational rules:
|
Having said that, here are the organizational rules:
|
||||||
|
|
||||||
1. Respect other people working on the project.
|
1. Respect other people working on the project.
|
||||||
|
@ -28,9 +27,9 @@ Having said that, here are the organizational rules:
|
||||||
your post after a good breather or a good nights sleep.
|
your post after a good breather or a good nights sleep.
|
||||||
|
|
||||||
3. Make sure you pick the right mailinglist for your posting. Please review
|
3. Make sure you pick the right mailinglist for your posting. Please review
|
||||||
the descriptions on the mailinglist overview page
|
the descriptions on the
|
||||||
(http://www.php.net/mailing-lists.php). When in doubt ask a friend or
|
[mailinglist overview page](https://www.php.net/mailing-lists.php). When
|
||||||
someone you trust on IRC.
|
in doubt ask a friend or someone you trust on IRC.
|
||||||
|
|
||||||
4. Make sure you know what you are talking about. PHP is a very large project
|
4. Make sure you know what you are talking about. PHP is a very large project
|
||||||
that strives to be very open. The flip side is that the core developers
|
that strives to be very open. The flip side is that the core developers
|
||||||
|
@ -70,7 +69,7 @@ The next few rules are more some general hints:
|
||||||
new thread.
|
new thread.
|
||||||
|
|
||||||
Finally, additional hints on how to behave inside the virtual community can be
|
Finally, additional hints on how to behave inside the virtual community can be
|
||||||
found in RFC 1855 (http://www.faqs.org/rfcs/rfc1855.html).
|
found in [RFC 1855](http://www.faqs.org/rfcs/rfc1855.html).
|
||||||
|
|
||||||
Happy hacking,
|
Happy hacking,
|
||||||
|
|
||||||
|
|
|
@ -1,139 +1,136 @@
|
||||||
API adjustment to the old output control code:
|
# API adjustment to the old output control code
|
||||||
|
|
||||||
Everything now resides beneath the php_output namespace,
|
Everything now resides beneath the php_output namespace, and there's an API call
|
||||||
and there's an API call for every output handler op.
|
for every output handler op.
|
||||||
|
|
||||||
Checking output control layers status:
|
Checking output control layers status:
|
||||||
// Using OG()
|
// Using OG()
|
||||||
php_output_get_status();
|
php_output_get_status();
|
||||||
|
|
||||||
Starting the default output handler:
|
Starting the default output handler:
|
||||||
// php_start_ob_buffer(NULL, 0, 1);
|
// php_start_ob_buffer(NULL, 0, 1);
|
||||||
php_output_start_default();
|
php_output_start_default();
|
||||||
|
|
||||||
Starting an user handler by zval:
|
Starting an user handler by zval:
|
||||||
// php_start_ob_buffer(zhandler, chunk_size, erase);
|
// php_start_ob_buffer(zhandler, chunk_size, erase);
|
||||||
php_output_start_user(zhandler, chunk_size, flags);
|
php_output_start_user(zhandler, chunk_size, flags);
|
||||||
|
|
||||||
Starting an internal handler without context:
|
Starting an internal handler without context:
|
||||||
// php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase);
|
// php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase);
|
||||||
php_output_start_internal(handler_name, handler_name_len, my_php_output_handler_func_t, chunk_size, flags);
|
php_output_start_internal(handler_name, handler_name_len, my_php_output_handler_func_t, chunk_size, flags);
|
||||||
|
|
||||||
Starting an internal handler with context:
|
Starting an internal handler with context:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_handler *h;
|
php_output_handler *h;
|
||||||
h = php_output_handler_create_internal(handler_name, handler_name_len, my_php_output_handler_context_func_t, chunk_size, flags);
|
h = php_output_handler_create_internal(handler_name, handler_name_len, my_php_output_handler_context_func_t, chunk_size, flags);
|
||||||
php_output_handler_set_context(h, my_context, my_context_dtor);
|
php_output_handler_set_context(h, my_context, my_context_dtor);
|
||||||
php_output_handler_start(h);
|
php_output_handler_start(h);
|
||||||
|
|
||||||
Testing whether a certain output handler has already been started:
|
Testing whether a certain output handler has already been started:
|
||||||
// php_ob_handler_used("output handler name");
|
// php_ob_handler_used("output handler name");
|
||||||
php_output_handler_started(handler_name, handler_name_len);
|
php_output_handler_started(handler_name, handler_name_len);
|
||||||
|
|
||||||
Flushing one output buffer:
|
Flushing one output buffer:
|
||||||
// php_end_ob_buffer(1, 1);
|
// php_end_ob_buffer(1, 1);
|
||||||
php_output_flush();
|
php_output_flush();
|
||||||
|
|
||||||
Flushing all output buffers:
|
Flushing all output buffers:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_flush_all();
|
php_output_flush_all();
|
||||||
|
|
||||||
Cleaning one output buffer:
|
Cleaning one output buffer:
|
||||||
// php_ob_end_buffer(0, 1);
|
// php_ob_end_buffer(0, 1);
|
||||||
php_output_clean();
|
php_output_clean();
|
||||||
|
|
||||||
Cleaning all output buffers:
|
Cleaning all output buffers:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_clean_all();
|
php_output_clean_all();
|
||||||
|
|
||||||
Discarding one output buffer:
|
Discarding one output buffer:
|
||||||
// php_ob_end_buffer(0, 0);
|
// php_ob_end_buffer(0, 0);
|
||||||
php_output_discard();
|
php_output_discard();
|
||||||
|
|
||||||
Discarding all output buffers:
|
Discarding all output buffers:
|
||||||
// php_ob_end_buffers(0);
|
// php_ob_end_buffers(0);
|
||||||
php_output_discard_all();
|
php_output_discard_all();
|
||||||
|
|
||||||
Stopping (and dropping) one output buffer:
|
Stopping (and dropping) one output buffer:
|
||||||
// php_ob_end_buffer(1, 0)
|
// php_ob_end_buffer(1, 0)
|
||||||
php_output_end();
|
php_output_end();
|
||||||
|
|
||||||
Stopping (and dropping) all output buffers:
|
Stopping (and dropping) all output buffers:
|
||||||
// php_ob_end_buffers(1, 0);
|
// php_ob_end_buffers(1, 0);
|
||||||
php_output_end_all();
|
php_output_end_all();
|
||||||
|
|
||||||
Retrieving output buffers contents:
|
Retrieving output buffers contents:
|
||||||
// php_ob_get_buffer(zstring);
|
// php_ob_get_buffer(zstring);
|
||||||
php_output_get_contents(zstring);
|
php_output_get_contents(zstring);
|
||||||
|
|
||||||
Retrieving output buffers length:
|
Retrieving output buffers length:
|
||||||
// php_ob_get_length(zlength);
|
// php_ob_get_length(zlength);
|
||||||
php_output_get_length(zlength);
|
php_output_get_length(zlength);
|
||||||
|
|
||||||
Retrieving output buffering level:
|
Retrieving output buffering level:
|
||||||
// OG(nesting_level);
|
// OG(nesting_level);
|
||||||
php_output_get_level();
|
php_output_get_level();
|
||||||
|
|
||||||
Issue a warning because of an output handler conflict:
|
Issue a warning because of an output handler conflict:
|
||||||
// php_ob_init_conflict("to be started handler name", "to be tested if already started handler name");
|
// php_ob_init_conflict("to be started handler name", "to be tested if already started handler name");
|
||||||
php_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len);
|
php_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len);
|
||||||
|
|
||||||
Registering a conflict checking function, which will be checked prior starting the handler:
|
Registering a conflict checking function, which will be checked prior starting the handler:
|
||||||
// not possible with old API, unless hardcoding into output.c
|
// not possible with old API, unless hardcoding into output.c
|
||||||
php_output_handler_conflict_register(handler_name, handler_name_len, my_php_output_handler_conflict_check_t);
|
php_output_handler_conflict_register(handler_name, handler_name_len, my_php_output_handler_conflict_check_t);
|
||||||
|
|
||||||
Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
|
Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_handler_reverse_conflict_register(foreign_handler_name, foreign_handler_name_len, my_php_output_handler_conflict_check_t);
|
php_output_handler_reverse_conflict_register(foreign_handler_name, foreign_handler_name_len, my_php_output_handler_conflict_check_t);
|
||||||
|
|
||||||
Facilitating a context from within an output handler callable with ob_start():
|
Facilitating a context from within an output handler callable with ob_start():
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
|
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
|
||||||
|
|
||||||
Disabling of the output handler by itself:
|
Disabling of the output handler by itself:
|
||||||
//not possible with old API
|
//not possible with old API
|
||||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
|
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
|
||||||
|
|
||||||
Marking an output handler immutable by itself because of irreversibility of its operation:
|
Marking an output handler immutable by itself because of irreversibility of its operation:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
|
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
|
||||||
|
|
||||||
Restarting the output handler because of a CLEAN operation:
|
Restarting the output handler because of a CLEAN operation:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
|
if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
|
||||||
|
|
||||||
Recognizing by the output handler itself if it gets discarded:
|
Recognizing by the output handler itself if it gets discarded:
|
||||||
// not possible with old API
|
// not possible with old API
|
||||||
if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
|
if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
|
||||||
|
|
||||||
|
## Output handler hooks
|
||||||
|
|
||||||
Output handler hooks
|
The output handler can change its abilities at runtime. Eg. the gz handler can
|
||||||
|
remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
|
||||||
|
or handlers implemented in C to be used with ob_start() can contain a non-global
|
||||||
|
context:
|
||||||
|
|
||||||
The output handler can change its abilities at runtime. Eg. the gz handler can
|
PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
|
||||||
remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
|
pass a void*** pointer as second arg to receive the address of a pointer
|
||||||
or handlers implemented in C to be used with ob_start() can contain a non-global
|
pointer to the opaque field of the output handler context
|
||||||
context:
|
PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
|
||||||
PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
|
pass a int* pointer as second arg to receive the flags set for the output handler
|
||||||
pass a void*** pointer as second arg to receive the address of a pointer
|
PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
|
||||||
pointer to the opaque field of the output handler context
|
pass a int* pointer as second arg to receive the level of this output handler
|
||||||
PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
|
(starts with 0)
|
||||||
pass a int* pointer as second arg to receive the flags set for the output handler
|
PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
|
||||||
PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
|
the second arg is ignored; marks the output handler to be neither cleanable
|
||||||
pass a int* pointer as second arg to receive the level of this output handler
|
nor removable
|
||||||
(starts with 0)
|
PHP_OUTPUT_HANDLER_HOOK_DISABLE
|
||||||
PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
|
the second arg is ignored; marks the output handler as disabled
|
||||||
the second arg is ignored; marks the output handler to be neither cleanable
|
|
||||||
nor removable
|
|
||||||
PHP_OUTPUT_HANDLER_HOOK_DISABLE
|
|
||||||
the second arg is ignored; marks the output handler as disabled
|
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
|
||||||
Open questions
|
* Should the userland API be adjusted and unified?
|
||||||
|
|
||||||
Should the userland API be adjusted and unified?
|
Many bits of the manual (and very first implementation) do not comply with the
|
||||||
|
behaviour of the current (to be obsoleted) code, thus should the manual or the
|
||||||
Many bits of the manual (and very first implementation) do not comply
|
behaviour be adjusted?
|
||||||
with the behaviour of the current (to be obsoleted) code, thus should
|
|
||||||
the manual or the behaviour be adjusted?
|
|
||||||
|
|
||||||
END
|
|
||||||
|
|
|
@ -1,133 +1,140 @@
|
||||||
Fast Parameter Parsing API
|
# Fast Parameter Parsing API
|
||||||
==========================
|
|
||||||
|
|
||||||
In PHP 7, a "Fast Parameter Parsing API" was introduced. See
|
In PHP 7, a "Fast Parameter Parsing API" was introduced. See
|
||||||
|
[RFC](https://wiki.php.net/rfc/fast_zpp).
|
||||||
|
|
||||||
https://wiki.php.net/rfc/fast_zpp
|
This API uses inlining to improve applications performance compared with the
|
||||||
|
`zend_parse_parameters()` function described below.
|
||||||
|
|
||||||
This API uses inlining to improve applications performance compared
|
## Parameter parsing functions
|
||||||
with the zend_parse_parameters() function described below.
|
|
||||||
|
|
||||||
|
Borrowing from Python's example, there is a set of functions that given the
|
||||||
|
string of type specifiers, can parse the input parameters and store the results
|
||||||
|
in the user specified variables. This avoids using `IS_*` checks and
|
||||||
|
`convert_to_*` conversions. The functions also check for the appropriate number
|
||||||
|
of parameters, and try to output meaningful error messages.
|
||||||
|
|
||||||
Parameter parsing functions
|
## Prototypes
|
||||||
===========================
|
|
||||||
|
|
||||||
Borrowing from Python's example, there is a set of functions that
|
```c
|
||||||
given the string of type specifiers, can parse the input parameters
|
|
||||||
and store the results in the user specified variables. This avoids
|
|
||||||
using IS_* checks and convert_to_* conversions. The functions also
|
|
||||||
check for the appropriate number of parameters, and try to output
|
|
||||||
meaningful error messages.
|
|
||||||
|
|
||||||
|
|
||||||
Prototypes
|
|
||||||
----------
|
|
||||||
/* Implemented. */
|
/* Implemented. */
|
||||||
int zend_parse_parameters(int num_args, char *type_spec, ...);
|
int zend_parse_parameters(int num_args, char *type_spec, ...);
|
||||||
int zend_parse_parameters_ex(int flags, int num_args, char *type_spec, ...);
|
int zend_parse_parameters_ex(int flags, int num_args, char *type_spec, ...);
|
||||||
|
```
|
||||||
|
|
||||||
The zend_parse_parameters() function takes the number of parameters
|
The `zend_parse_parameters()` function takes the number of parameters passed to
|
||||||
passed to the extension function, the type specifier string, and the
|
the extension function, the type specifier string, and the list of pointers to
|
||||||
list of pointers to variables to store the results in. The _ex() version
|
variables to store the results in. The _ex() version also takes 'flags' argument
|
||||||
also takes 'flags' argument -- current only ZEND_PARSE_PARAMS_QUIET can
|
-- current only `ZEND_PARSE_PARAMS_QUIET` can be used as 'flags' to specify that
|
||||||
be used as 'flags' to specify that the function should operate quietly
|
the function should operate quietly and not output any error messages.
|
||||||
and not output any error messages.
|
|
||||||
|
|
||||||
Both functions return SUCCESS or FAILURE depending on the result.
|
Both functions return `SUCCESS` or `FAILURE` depending on the result.
|
||||||
|
|
||||||
The auto-conversions are performed as necessary. Arrays, objects, and
|
The auto-conversions are performed as necessary. Arrays, objects, and resources
|
||||||
resources cannot be auto-converted.
|
cannot be auto-converted.
|
||||||
|
|
||||||
PHP 5.3 includes a new function (actually implemented as macro):
|
PHP 5.3 includes a new function (actually implemented as macro):
|
||||||
|
|
||||||
|
```c
|
||||||
int zend_parse_parameters_none();
|
int zend_parse_parameters_none();
|
||||||
|
```
|
||||||
|
|
||||||
This returns SUCCESS if no argument has been passed to the function,
|
This returns `SUCCESS` if no argument has been passed to the function, `FAILURE`
|
||||||
FAILURE otherwise.
|
otherwise.
|
||||||
|
|
||||||
PHP 5.5 includes a new function:
|
PHP 5.5 includes a new function:
|
||||||
|
|
||||||
|
```c
|
||||||
int zend_parse_parameter(int flags, int arg_num, zval **arg, const char *spec, ...);
|
int zend_parse_parameter(int flags, int arg_num, zval **arg, const char *spec, ...);
|
||||||
|
```
|
||||||
|
|
||||||
This function behaves like zend_parse_parameters_ex() except that instead of
|
This function behaves like `zend_parse_parameters_ex()` except that instead of
|
||||||
reading the arguments from the stack, it receives a single zval to convert
|
reading the arguments from the stack, it receives a single zval to convert
|
||||||
(passed with double indirection). The passed zval may be changed in place as
|
(passed with double indirection). The passed zval may be changed in place as
|
||||||
part of the conversion process.
|
part of the conversion process.
|
||||||
|
|
||||||
See also https://wiki.php.net/rfc/zpp_improv#expose_zend_parse_arg_as_zend_parse_parameter
|
See also
|
||||||
|
[Expose zend_parse_arg() as zend_parse_parameter()](https://wiki.php.net/rfc/zpp_improv#expose_zend_parse_arg_as_zend_parse_parameter).
|
||||||
|
|
||||||
|
## Type specifiers
|
||||||
|
|
||||||
Type specifiers
|
The following list shows the type specifier, its meaning and the parameter types
|
||||||
---------------
|
that need to be passed by address. All passed parameters are set if the PHP
|
||||||
The following list shows the type specifier, its meaning and the parameter
|
parameter is non optional and untouched if optional and the parameter is not
|
||||||
types that need to be passed by address. All passed parameters are set
|
present. The only exception is O where the zend_class_entry* has to be provided
|
||||||
if the PHP parameter is non optional and untouched if optional and the
|
on input and is used to verify the PHP parameter is an instance of that class.
|
||||||
parameter is not present. The only exception is O where the zend_class_entry*
|
|
||||||
has to be provided on input and is used to verify the PHP parameter is an
|
|
||||||
instance of that class.
|
|
||||||
|
|
||||||
a - array (zval*)
|
```txt
|
||||||
A - array or object (zval*)
|
a - array (zval*)
|
||||||
b - boolean (zend_bool)
|
A - array or object (zval*)
|
||||||
C - class (zend_class_entry*)
|
b - boolean (zend_bool)
|
||||||
d - double (double)
|
C - class (zend_class_entry*)
|
||||||
f - function or array containing php method call info (returned as
|
d - double (double)
|
||||||
zend_fcall_info and zend_fcall_info_cache)
|
f - function or array containing php method call info (returned as
|
||||||
h - array (returned as HashTable*)
|
zend_fcall_info and zend_fcall_info_cache)
|
||||||
H - array or HASH_OF(object) (returned as HashTable*)
|
h - array (returned as HashTable*)
|
||||||
l - long (zend_long)
|
H - array or HASH_OF(object) (returned as HashTable*)
|
||||||
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (zend_long, ZEND_LONG_MAX/ZEND_LONG_MIN)
|
l - long (zend_long)
|
||||||
o - object of any type (zval*)
|
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (zend_long, ZEND_LONG_MAX/ZEND_LONG_MIN)
|
||||||
O - object of specific type given by class entry (zval*, zend_class_entry)
|
o - object of any type (zval*)
|
||||||
p - valid path (string without null bytes in the middle) and its length (char*, size_t)
|
O - object of specific type given by class entry (zval*, zend_class_entry)
|
||||||
P - valid path (string without null bytes in the middle) as zend_string (zend_string*)
|
p - valid path (string without null bytes in the middle) and its length (char*, size_t)
|
||||||
r - resource (zval*)
|
P - valid path (string without null bytes in the middle) as zend_string (zend_string*)
|
||||||
s - string (with possible null bytes) and its length (char*, size_t)
|
r - resource (zval*)
|
||||||
S - string (with possible null bytes) as zend_string (zend_string*)
|
s - string (with possible null bytes) and its length (char*, size_t)
|
||||||
z - the actual zval (zval*)
|
S - string (with possible null bytes) as zend_string (zend_string*)
|
||||||
* - variable arguments list (0 or more)
|
z - the actual zval (zval*)
|
||||||
+ - variable arguments list (1 or more)
|
* - variable arguments list (0 or more)
|
||||||
|
+ - variable arguments list (1 or more)
|
||||||
|
```
|
||||||
|
|
||||||
The following characters also have a meaning in the specifier string:
|
The following characters also have a meaning in the specifier string:
|
||||||
| - indicates that the remaining parameters are optional, they
|
|
||||||
should be initialized to default values by the extension since they
|
|
||||||
will not be touched by the parsing function if they are not
|
|
||||||
passed to it.
|
|
||||||
/ - use SEPARATE_ZVAL_IF_NOT_REF() on the parameter it follows
|
|
||||||
! - the parameter it follows can be of specified type or NULL. If NULL is
|
|
||||||
passed and the output for such type is a pointer, then the output
|
|
||||||
pointer is set to a native NULL pointer.
|
|
||||||
For 'b', 'l' and 'd', an extra argument of type zend_bool* must be
|
|
||||||
passed after the corresponding bool*, zend_long* or double* arguments,
|
|
||||||
respectively. A non-zero value will be written to the zend_bool if a
|
|
||||||
PHP NULL is passed.
|
|
||||||
|
|
||||||
|
* `|` - indicates that the remaining parameters are optional, they should be
|
||||||
|
initialized to default values by the extension since they will not be touched
|
||||||
|
by the parsing function if they are not passed to it.
|
||||||
|
* `/` - use SEPARATE_ZVAL_IF_NOT_REF() on the parameter it follows
|
||||||
|
* `!` - the parameter it follows can be of specified type or NULL. If NULL is
|
||||||
|
passed and the output for such type is a pointer, then the output pointer is
|
||||||
|
set to a native NULL pointer. For 'b', 'l' and 'd', an extra argument of type
|
||||||
|
zend_bool* must be passed after the corresponding bool*, zend_long* or
|
||||||
|
double* arguments, respectively. A non-zero value will be written to the
|
||||||
|
zend_bool if a PHP NULL is passed.
|
||||||
|
|
||||||
Note on 64bit compatibility
|
## Note on 64bit compatibility
|
||||||
---------------------------
|
|
||||||
Please note that since version 7 PHP uses zend_long as integer type and
|
Please note that since version 7 PHP uses `zend_long` as integer type and
|
||||||
zend_string with size_t as length, so make sure you pass zend_longs to "l"
|
`zend_string` with `size_t` as length, so make sure you pass `zend_long`s to "l"
|
||||||
and size_t to strings length (i.e. for "s" you need to pass char * and size_t),
|
and `size_t` to strings length (i.e. for "s" you need to pass char `*` and
|
||||||
not the other way round!
|
`size_t`), not the other way round!
|
||||||
|
|
||||||
Both mistakes might cause memory corruptions and segfaults:
|
Both mistakes might cause memory corruptions and segfaults:
|
||||||
1)
|
|
||||||
char *str;
|
|
||||||
long str_len; /* XXX THIS IS WRONG!! Use size_t instead. */
|
|
||||||
zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len)
|
|
||||||
|
|
||||||
2)
|
* 1
|
||||||
int num; /* XXX THIS IS WRONG!! Use zend_long instead. */
|
|
||||||
zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num)
|
|
||||||
|
|
||||||
If you're in doubt, use check_parameters.php script to the parameters
|
```c
|
||||||
and their types (it can be found in ./scripts/dev/ directory of PHP sources):
|
char *str;
|
||||||
|
long str_len; /* XXX THIS IS WRONG!! Use size_t instead. */
|
||||||
|
zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len)
|
||||||
|
```
|
||||||
|
|
||||||
# php ./scripts/dev/check_parameters.php /path/to/your/sources/
|
* 2
|
||||||
|
|
||||||
|
```c
|
||||||
|
int num; /* XXX THIS IS WRONG!! Use zend_long instead. */
|
||||||
|
zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num)
|
||||||
|
```
|
||||||
|
|
||||||
Examples
|
If you're in doubt, use check_parameters.php script to the parameters and their
|
||||||
--------
|
types (it can be found in `./scripts/dev/` directory of PHP sources):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php ./scripts/dev/check_parameters.php /path/to/your/sources/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
```c
|
||||||
/* Gets a long, a string and its length, and a zval */
|
/* Gets a long, a string and its length, and a zval */
|
||||||
zend_long l;
|
zend_long l;
|
||||||
char *s;
|
char *s;
|
||||||
|
@ -138,7 +145,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsz",
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Gets an object of class specified by my_ce, and an optional double. */
|
/* Gets an object of class specified by my_ce, and an optional double. */
|
||||||
zval *obj;
|
zval *obj;
|
||||||
double d = 0.5;
|
double d = 0.5;
|
||||||
|
@ -148,7 +154,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|d",
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Gets an object or null, and an array.
|
/* Gets an object or null, and an array.
|
||||||
If null is passed for object, obj will be set to NULL. */
|
If null is passed for object, obj will be set to NULL. */
|
||||||
zval *obj;
|
zval *obj;
|
||||||
|
@ -158,7 +163,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a",
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Gets a separated array which can also be null. */
|
/* Gets a separated array which can also be null. */
|
||||||
zval *arr;
|
zval *arr;
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!",
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!",
|
||||||
|
@ -171,7 +175,7 @@ zend_long l1, l2, l3;
|
||||||
char *s;
|
char *s;
|
||||||
/*
|
/*
|
||||||
* The function expects a pointer to a size_t in this case, not a long
|
* The function expects a pointer to a size_t in this case, not a long
|
||||||
* or any other type. If you specify a type which is larger
|
* or any other type. If you specify a type which is larger
|
||||||
* than a 'size_t', the upper bits might not be initialized
|
* than a 'size_t', the upper bits might not be initialized
|
||||||
* properly, leading to random crashes on platforms like
|
* properly, leading to random crashes on platforms like
|
||||||
* Tru64 or Linux/Alpha.
|
* Tru64 or Linux/Alpha.
|
||||||
|
@ -190,13 +194,11 @@ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Function that accepts only varargs (0 or more) */
|
/* Function that accepts only varargs (0 or more) */
|
||||||
|
|
||||||
int i, num_varargs;
|
int i, num_varargs;
|
||||||
zval *varargs = NULL;
|
zval *varargs = NULL;
|
||||||
|
|
||||||
|
|
||||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &varargs, &num_varargs) == FAILURE) {
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &varargs, &num_varargs) == FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,7 +211,6 @@ if (varargs) {
|
||||||
efree(varargs);
|
efree(varargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Function that accepts a string, followed by varargs (1 or more) */
|
/* Function that accepts a string, followed by varargs (1 or more) */
|
||||||
|
|
||||||
char *str;
|
char *str;
|
||||||
|
@ -243,3 +244,4 @@ for (i = 0; i < num_varargs; i++) {
|
||||||
if (zend_parse_parameters_none() == FAILURE) {
|
if (zend_parse_parameters_none() == FAILURE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -1,84 +1,83 @@
|
||||||
# HOW TO CREATE A SELF-CONTAINED PHP EXTENSION
|
# How to create a self-contained PHP extension
|
||||||
|
|
||||||
A self-contained extension can be distributed independently of
|
A self-contained extension can be distributed independently of the PHP source.
|
||||||
the PHP source. To create such an extension, two things are
|
To create such an extension, two things are required:
|
||||||
required:
|
|
||||||
|
|
||||||
- Configuration file (config.m4)
|
* Configuration file (config.m4)
|
||||||
- Source code for your module
|
* Source code for your module
|
||||||
|
|
||||||
We will describe now how to create these and how to put things
|
We will describe now how to create these and how to put things together.
|
||||||
together.
|
|
||||||
|
|
||||||
## PREPARING YOUR SYSTEM
|
## Prepairing your system
|
||||||
|
|
||||||
While the result will run on any system, a developer's setup needs these
|
While the result will run on any system, a developer's setup needs these tools:
|
||||||
tools:
|
|
||||||
|
|
||||||
GNU autoconf
|
* GNU autoconf
|
||||||
GNU libtool
|
* GNU libtool
|
||||||
GNU m4
|
* GNU m4
|
||||||
|
|
||||||
All of these are available from
|
All of these are available from
|
||||||
|
|
||||||
ftp://ftp.gnu.org/pub/gnu/
|
ftp://ftp.gnu.org/pub/gnu/
|
||||||
|
|
||||||
## CONVERTING AN EXISTING EXTENSION
|
## Converting an existing extension
|
||||||
|
|
||||||
Just to show you how easy it is to create a self-contained
|
Just to show you how easy it is to create a self-contained extension, we will
|
||||||
extension, we will convert an embedded extension into a
|
convert an embedded extension into a self-contained one. Install PHP and execute
|
||||||
self-contained one. Install PHP and execute the following
|
the following commands.
|
||||||
commands.
|
|
||||||
|
|
||||||
$ mkdir /tmp/newext
|
|
||||||
$ cd /tmp/newext
|
|
||||||
|
|
||||||
You now have an empty directory. We will copy the files from
|
|
||||||
the mysql extension:
|
|
||||||
|
|
||||||
$ cp -rp php-4.0.X/ext/mysql/* .
|
|
||||||
|
|
||||||
It is time to finish the module. Run:
|
|
||||||
|
|
||||||
$ phpize
|
|
||||||
|
|
||||||
You can now ship the contents of the directory - the extension
|
|
||||||
can live completely on its own.
|
|
||||||
|
|
||||||
The user instructions boil down to
|
|
||||||
|
|
||||||
$ ./configure \
|
|
||||||
[--with-php-config=/path/to/php-config] \
|
|
||||||
[--with-mysql=MYSQL-DIR]
|
|
||||||
$ make install
|
|
||||||
|
|
||||||
The MySQL module will either use the embedded MySQL client
|
|
||||||
library or the MySQL installation in MYSQL-DIR.
|
|
||||||
|
|
||||||
|
|
||||||
## DEFINING THE NEW EXTENSION
|
|
||||||
|
|
||||||
Our demo extension is called "foobar".
|
|
||||||
|
|
||||||
It consists of two source files "foo.c" and "bar.c"
|
|
||||||
(and any arbitrary amount of header files, but that is not
|
|
||||||
important here).
|
|
||||||
|
|
||||||
The demo extension does not reference any external
|
|
||||||
libraries (that is important, because the user does not
|
|
||||||
need to specify anything).
|
|
||||||
|
|
||||||
|
|
||||||
LTLIBRARY_SOURCES specifies the names of the sources files. You can
|
|
||||||
name an arbitrary number of source files here.
|
|
||||||
|
|
||||||
## CREATING THE M4 CONFIGURATION FILE
|
|
||||||
|
|
||||||
The m4 configuration can perform additional checks. For a
|
|
||||||
self-contained extension, you do not need more than a few
|
|
||||||
macro calls.
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir /tmp/newext
|
||||||
|
cd /tmp/newext
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You now have an empty directory. We will copy the files from the mysqli
|
||||||
|
extension:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp -rp php-src/ext/mysqli/* .
|
||||||
|
```
|
||||||
|
|
||||||
|
It is time to finish the module. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
phpize
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now ship the contents of the directory - the extension can live
|
||||||
|
completely on its own.
|
||||||
|
|
||||||
|
The user instructions boil down to
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./configure \
|
||||||
|
[--with-php-config=/path/to/php-config] \
|
||||||
|
[--with-mysqli=MYSQL-DIR]
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
The MySQL module will either use the embedded MySQL client library or the MySQL
|
||||||
|
installation in MYSQL-DIR.
|
||||||
|
|
||||||
|
## Defining the new extension
|
||||||
|
|
||||||
|
Our demo extension is called "foobar".
|
||||||
|
|
||||||
|
It consists of two source files `foo.c` and `bar.c` (and any arbitrary amount of
|
||||||
|
header files, but that is not important here).
|
||||||
|
|
||||||
|
The demo extension does not reference any external libraries (that is important,
|
||||||
|
because the user does not need to specify anything).
|
||||||
|
|
||||||
|
`LTLIBRARY_SOURCES` specifies the names of the sources files. You can name an
|
||||||
|
arbitrary number of source files here.
|
||||||
|
|
||||||
|
## Creating the M4 configuration file
|
||||||
|
|
||||||
|
The m4 configuration can perform additional checks. For a self-contained
|
||||||
|
extension, you do not need more than a few macro calls.
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_ARG_ENABLE([foobar],
|
PHP_ARG_ENABLE([foobar],
|
||||||
[whether to enable foobar],
|
[whether to enable foobar],
|
||||||
[AS_HELP_STRING([--enable-foobar],
|
[AS_HELP_STRING([--enable-foobar],
|
||||||
|
@ -89,81 +88,86 @@ if test "$PHP_FOOBAR" != "no"; then
|
||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
PHP_ARG_ENABLE will automatically set the correct variables, so
|
`PHP_ARG_ENABLE` will automatically set the correct variables, so that the
|
||||||
that the extension will be enabled by PHP_NEW_EXTENSION in shared mode.
|
extension will be enabled by `PHP_NEW_EXTENSION` in shared mode.
|
||||||
|
|
||||||
The first argument of PHP_NEW_EXTENSION describes the name of the
|
The first argument of `PHP_NEW_EXTENSION` describes the name of the extension.
|
||||||
extension. The second names the source-code files. The third passes
|
The second names the source-code files. The third passes `$ext_shared` which is
|
||||||
$ext_shared which is set by PHP_ARG_ENABLE/WITH to PHP_NEW_EXTENSION.
|
set by `PHP_ARG_ENABLE/WITH` to `PHP_NEW_EXTENSION`.
|
||||||
|
|
||||||
Please use always PHP_ARG_ENABLE or PHP_ARG_WITH. Even if you do not
|
Please use always `PHP_ARG_ENABLE` or `PHP_ARG_WITH`. Even if you do not plan to
|
||||||
plan to distribute your module with PHP, these facilities allow you
|
distribute your module with PHP, these facilities allow you to integrate your
|
||||||
to integrate your module easily into the main PHP module framework.
|
module easily into the main PHP module framework.
|
||||||
|
|
||||||
## CREATING SOURCE FILES
|
## Create source files
|
||||||
|
|
||||||
ext_skel can be of great help when creating the common code for all modules
|
`ext_skel.php` can be of great help when creating the common code for all
|
||||||
in PHP for you and also writing basic function definitions and C code for
|
modules in PHP for you and also writing basic function definitions and C code
|
||||||
handling arguments passed to your functions. See `./ext/ext_skel.php --help`
|
for handling arguments passed to your functions. See `./ext/ext_skel.php --help`
|
||||||
for further information.
|
for further information.
|
||||||
|
|
||||||
As for the rest, you are currently alone here. There are a lot of existing
|
As for the rest, you are currently alone here. There are a lot of existing
|
||||||
modules, use a simple module as a starting point and add your own code.
|
modules, use a simple module as a starting point and add your own code.
|
||||||
|
|
||||||
|
## Creating the self-contained extension
|
||||||
|
|
||||||
## CREATING THE SELF-CONTAINED EXTENSION
|
Put `config.m4` and the source files into one directory. Then, run `phpize`
|
||||||
|
(this is installed during `make install` by PHP).
|
||||||
|
|
||||||
Put config.m4 and the source files into one directory. Then, run phpize
|
For example, if you configured PHP with `--prefix=/php`, you would run
|
||||||
(this is installed during make install by PHP 4.0).
|
|
||||||
|
|
||||||
For example, if you configured PHP with --prefix=/php, you would run
|
```bash
|
||||||
|
/php/bin/phpize
|
||||||
$ /php/bin/phpize
|
|
||||||
|
|
||||||
This will automatically copy the necessary build files and create
|
|
||||||
configure from your config.m4.
|
|
||||||
|
|
||||||
And that's it. You now have a self-contained extension.
|
|
||||||
|
|
||||||
## INSTALLING A SELF-CONTAINED EXTENSION
|
|
||||||
|
|
||||||
An extension can be installed by running:
|
|
||||||
|
|
||||||
$ ./configure \
|
|
||||||
[--with-php-config=/path/to/php-config]
|
|
||||||
$ make install
|
|
||||||
|
|
||||||
## ADDING SHARED MODULE SUPPORT TO A MODULE
|
|
||||||
|
|
||||||
In order to be useful, a self-contained extension must be loadable
|
|
||||||
as a shared module. I will explain now how you can add shared module
|
|
||||||
support to an existing module called foo.
|
|
||||||
|
|
||||||
1. In config.m4, use PHP_ARG_WITH/PHP_ARG_ENABLE. Then you will
|
|
||||||
automatically be able to use --with-foo=shared[,..] or
|
|
||||||
--enable-foo=shared[,..].
|
|
||||||
|
|
||||||
2. In config.m4, use PHP_NEW_EXTENSION(foo,.., $ext_shared) to enable
|
|
||||||
building the extension.
|
|
||||||
|
|
||||||
3. Add the following lines to your C source file:
|
|
||||||
|
|
||||||
```
|
|
||||||
#ifdef COMPILE_DL_FOO
|
|
||||||
ZEND_GET_MODULE(foo)
|
|
||||||
#endif
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## PECL SITE CONFORMITY
|
This will automatically copy the necessary build files and create configure from
|
||||||
|
your `config.m4`.
|
||||||
|
|
||||||
If you plan to release an extension to the PECL website, there are several
|
And that's it. You now have a self-contained extension.
|
||||||
points to be regarded.
|
|
||||||
|
|
||||||
1. Add LICENSE or COPYING to the package.xml
|
## Installing a self-contained extension
|
||||||
|
|
||||||
2. The following should be defined in one of the extension header files
|
An extension can be installed by running:
|
||||||
|
|
||||||
#define PHP_FOO_VERSION "1.2.3"
|
```bash
|
||||||
|
./configure \
|
||||||
|
[--with-php-config=/path/to/php-config]
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
This macros has to be used within your foo_module_entry to indicate the
|
## Adding shared module support to a module
|
||||||
extension version.
|
|
||||||
|
In order to be useful, a self-contained extension must be loadable as a shared
|
||||||
|
module. The following will explain now how you can add shared module support to
|
||||||
|
an existing module called `foo`.
|
||||||
|
|
||||||
|
1. In `config.m4`, use `PHP_ARG_WITH/PHP_ARG_ENABLE`. Then you will
|
||||||
|
automatically be able to use `--with-foo=shared[,..]` or
|
||||||
|
`--enable-foo=shared[,..]`.
|
||||||
|
|
||||||
|
2. In `config.m4`, use `PHP_NEW_EXTENSION(foo,.., $ext_shared)` to enable
|
||||||
|
building the extension.
|
||||||
|
|
||||||
|
3. Add the following lines to your C source file:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#ifdef COMPILE_DL_FOO
|
||||||
|
ZEND_GET_MODULE(foo)
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
## PECL site conformity
|
||||||
|
|
||||||
|
If you plan to release an extension to the PECL website, there are several
|
||||||
|
points to be regarded.
|
||||||
|
|
||||||
|
1. Add `LICENSE` or `COPYING` to the `package.xml`
|
||||||
|
|
||||||
|
2. The following should be defined in one of the extension header files
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define PHP_FOO_VERSION "1.2.3"
|
||||||
|
```
|
||||||
|
|
||||||
|
This macros has to be used within your foo_module_entry to indicate the
|
||||||
|
extension version.
|
||||||
|
|
361
docs/streams.md
361
docs/streams.md
|
@ -1,29 +1,26 @@
|
||||||
An Overview of the PHP Streams abstraction
|
# An overview of the PHP streams abstraction
|
||||||
==========================================
|
|
||||||
|
|
||||||
WARNING: some prototypes in this file are out of date.
|
WARNING: some prototypes in this file are out of date.
|
||||||
The information contained here is being integrated into
|
|
||||||
the PHP manual - stay tuned...
|
|
||||||
|
|
||||||
Please send comments to: Wez Furlong <wez@thebrainroom.com>
|
## Why streams?
|
||||||
|
|
||||||
Why Streams?
|
|
||||||
============
|
|
||||||
You may have noticed a shed-load of issock parameters flying around the PHP
|
You may have noticed a shed-load of issock parameters flying around the PHP
|
||||||
code; we don't want them - they are ugly and cumbersome and force you to
|
code; we don't want them - they are ugly and cumbersome and force you to special
|
||||||
special case sockets and files every time you need to work with a "user-level"
|
case sockets and files every time you need to work with a "user-level" PHP file
|
||||||
PHP file pointer.
|
pointer.
|
||||||
|
|
||||||
Streams take care of that and present the PHP extension coder with an ANSI
|
Streams take care of that and present the PHP extension coder with an ANSI
|
||||||
stdio-alike API that looks much nicer and can be extended to support non file
|
stdio-alike API that looks much nicer and can be extended to support non file
|
||||||
based data sources.
|
based data sources.
|
||||||
|
|
||||||
Using Streams
|
## Using streams
|
||||||
=============
|
|
||||||
Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
|
Streams use a `php_stream*` parameter just as ANSI stdio (fread etc.) use a
|
||||||
FILE* parameter.
|
`FILE*` parameter.
|
||||||
|
|
||||||
The main functions are:
|
The main functions are:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
|
PHPAPI size_t php_stream_read(php_stream * stream, char * buf, size_t count);
|
||||||
PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
|
PHPAPI size_t php_stream_write(php_stream * stream, const char * buf, size_t
|
||||||
count);
|
count);
|
||||||
|
@ -37,210 +34,234 @@ PHPAPI int php_stream_flush(php_stream * stream);
|
||||||
PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
|
PHPAPI int php_stream_seek(php_stream * stream, off_t offset, int whence);
|
||||||
PHPAPI off_t php_stream_tell(php_stream * stream);
|
PHPAPI off_t php_stream_tell(php_stream * stream);
|
||||||
PHPAPI int php_stream_lock(php_stream * stream, int mode);
|
PHPAPI int php_stream_lock(php_stream * stream, int mode);
|
||||||
|
```
|
||||||
|
|
||||||
These (should) behave in the same way as the ANSI stdio functions with similar
|
These (should) behave in the same way as the ANSI stdio functions with similar
|
||||||
names: fread, fwrite, fprintf, feof, fgetc, fgets, fclose, fflush, fseek, ftell, flock.
|
names: fread, fwrite, fprintf, feof, fgetc, fgets, fclose, fflush, fseek, ftell,
|
||||||
|
flock.
|
||||||
|
|
||||||
|
## Opening streams
|
||||||
|
|
||||||
Opening Streams
|
|
||||||
===============
|
|
||||||
In most cases, you should use this API:
|
In most cases, you should use this API:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI php_stream *php_stream_open_wrapper(const char *path, const char *mode,
|
PHPAPI php_stream *php_stream_open_wrapper(const char *path, const char *mode,
|
||||||
int options, char **opened_path);
|
int options, char **opened_path);
|
||||||
|
```
|
||||||
|
|
||||||
Where:
|
Where:
|
||||||
path is the file or resource to open.
|
|
||||||
mode is the stdio compatible mode eg: "wb", "rb" etc.
|
|
||||||
options is a combination of the following values:
|
|
||||||
IGNORE_PATH (default) - don't use include path to search for the file
|
|
||||||
USE_PATH - use include path to search for the file
|
|
||||||
IGNORE_URL - do not use plugin wrappers
|
|
||||||
REPORT_ERRORS - show errors in a standard format if something
|
|
||||||
goes wrong.
|
|
||||||
STREAM_MUST_SEEK - If you really need to be able to seek the stream
|
|
||||||
and don't need to be able to write to the original
|
|
||||||
file/URL, use this option to arrange for the stream
|
|
||||||
to be copied (if needed) into a stream that can
|
|
||||||
be seek()ed.
|
|
||||||
|
|
||||||
opened_path is used to return the path of the actual file opened,
|
* `path` is the file or resource to open.
|
||||||
but if you used STREAM_MUST_SEEK, may not be valid. You are
|
* `mode` is the stdio compatible mode eg: "wb", "rb" etc.
|
||||||
responsible for efree()ing opened_path. opened_path may be (and usually
|
* `options` is a combination of the following values:
|
||||||
is) NULL.
|
* `IGNORE_PATH` (default) - don't use include path to search for the file
|
||||||
|
* `USE_PATH` - use include path to search for the file
|
||||||
|
* `IGNORE_URL` - do not use plugin wrappers
|
||||||
|
* `REPORT_ERRORS` - show errors in a standard format if something goes wrong.
|
||||||
|
* `STREAM_MUST_SEEK` - If you really need to be able to seek the stream and
|
||||||
|
don't need to be able to write to the original file/URL, use this option to
|
||||||
|
arrange for the stream to be copied (if needed) into a stream that can be
|
||||||
|
seek()ed.
|
||||||
|
* `opened_path` is used to return the path of the actual file opened, but if you
|
||||||
|
used `STREAM_MUST_SEEK`, may not be valid. You are responsible for
|
||||||
|
`efree()ing` `opened_path`.
|
||||||
|
* `opened_path` may be (and usually is) `NULL`.
|
||||||
|
|
||||||
If you need to open a specific stream, or convert standard resources into
|
If you need to open a specific stream, or convert standard resources into
|
||||||
streams there are a range of functions to do this defined in php_streams.h.
|
streams there are a range of functions to do this defined in `php_streams.h`. A
|
||||||
A brief list of the most commonly used functions:
|
brief list of the most commonly used functions:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
|
PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode);
|
||||||
Convert a FILE * into a stream.
|
/* Convert a FILE * into a stream. */
|
||||||
|
|
||||||
PHPAPI php_stream *php_stream_fopen_tmpfile(void);
|
PHPAPI php_stream *php_stream_fopen_tmpfile(void);
|
||||||
Open a FILE * with tmpfile() and convert into a stream.
|
/* Open a FILE * with tmpfile() and convert into a stream. */
|
||||||
|
|
||||||
PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
|
PHPAPI php_stream *php_stream_fopen_temporary_file(const char *dir,
|
||||||
const char *pfx, char **opened_path);
|
const char *pfx, char **opened_path);
|
||||||
Generate a temporary file name and open it.
|
/* Generate a temporary file name and open it. */
|
||||||
|
```
|
||||||
|
|
||||||
There are some network enabled relatives in php_network.h:
|
There are some network enabled relatives in `php_network.h`:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
|
PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent);
|
||||||
Convert a socket into a stream.
|
/* Convert a socket into a stream. */
|
||||||
|
|
||||||
PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
|
PHPAPI php_stream *php_stream_sock_open_host(const char *host, unsigned short port,
|
||||||
int socktype, int timeout, int persistent);
|
int socktype, int timeout, int persistent);
|
||||||
Open a connection to a host and return a stream.
|
/* Open a connection to a host and return a stream. */
|
||||||
|
|
||||||
PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
|
PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
|
||||||
struct timeval *timeout);
|
struct timeval *timeout);
|
||||||
Open a UNIX domain socket.
|
/* Open a UNIX domain socket. */
|
||||||
|
```
|
||||||
|
|
||||||
|
## Stream utilities
|
||||||
|
|
||||||
Stream Utilities
|
If you need to copy some data from one stream to another, you will be please to
|
||||||
================
|
know that the streams API provides a standard way to do this:
|
||||||
|
|
||||||
If you need to copy some data from one stream to another, you will be please
|
|
||||||
to know that the streams API provides a standard way to do this:
|
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
|
PHPAPI size_t php_stream_copy_to_stream(php_stream *src,
|
||||||
php_stream *dest, size_t maxlen);
|
php_stream *dest, size_t maxlen);
|
||||||
|
```
|
||||||
|
|
||||||
If you want to copy all remaining data from the src stream, pass
|
If you want to copy all remaining data from the src stream, pass
|
||||||
PHP_STREAM_COPY_ALL as the maxlen parameter, otherwise maxlen indicates the
|
`PHP_STREAM_COPY_ALL` as the maxlen parameter, otherwise maxlen indicates the
|
||||||
number of bytes to copy.
|
number of bytes to copy. This function will try to use mmap where available to
|
||||||
This function will try to use mmap where available to make the copying more
|
make the copying more efficient.
|
||||||
efficient.
|
|
||||||
|
|
||||||
If you want to read the contents of a stream into an allocated memory buffer,
|
If you want to read the contents of a stream into an allocated memory buffer,
|
||||||
you should use:
|
you should use:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
|
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
|
||||||
size_t maxlen, int persistent);
|
size_t maxlen, int persistent);
|
||||||
|
```
|
||||||
|
|
||||||
This function will set buf to the address of the buffer that it allocated,
|
This function will set buf to the address of the buffer that it allocated, which
|
||||||
which will be maxlen bytes in length, or will be the entire length of the
|
will be maxlen bytes in length, or will be the entire length of the data
|
||||||
data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
|
remaining on the stream if you set maxlen to `PHP_STREAM_COPY_ALL`. The buffer
|
||||||
The buffer is allocated using pemalloc(); you need to call pefree() to
|
is allocated using `pemalloc()`. You need to call `pefree()` to release the
|
||||||
release the memory when you are done.
|
memory when you are done. As with `copy_to_stream`, this function will try use
|
||||||
As with copy_to_stream, this function will try use mmap where it can.
|
mmap where it can.
|
||||||
|
|
||||||
If you have an existing stream and need to be able to seek() it, you
|
If you have an existing stream and need to be able to `seek()` it, you can use
|
||||||
can use this function to copy the contents into a new stream that can
|
this function to copy the contents into a new stream that can be `seek()ed`:
|
||||||
be seek()ed:
|
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
|
PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
|
||||||
|
```
|
||||||
|
|
||||||
It returns one of the following values:
|
It returns one of the following values:
|
||||||
#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
|
|
||||||
#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
|
|
||||||
#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
|
|
||||||
#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */
|
|
||||||
|
|
||||||
make_seekable will always set newstream to be the stream that is valid
|
```c
|
||||||
if the function succeeds.
|
#define PHP_STREAM_UNCHANGED 0 /* orig stream was seekable anyway */
|
||||||
When you have finished, remember to close the stream.
|
#define PHP_STREAM_RELEASED 1 /* newstream should be used; origstream is no longer valid */
|
||||||
|
#define PHP_STREAM_FAILED 2 /* an error occurred while attempting conversion */
|
||||||
|
#define PHP_STREAM_CRITICAL 3 /* an error occurred; origstream is in an unknown state; you should close origstream */
|
||||||
|
```
|
||||||
|
|
||||||
NOTE: If you only need to seek forward, there is no need to call this
|
`make_seekable` will always set newstream to be the stream that is valid if the
|
||||||
function, as the php_stream_seek can emulate forward seeking when the
|
function succeeds. When you have finished, remember to close the stream.
|
||||||
whence parameter is SEEK_CUR.
|
|
||||||
|
|
||||||
NOTE: Writing to the stream may not affect the original source, so it
|
NOTE: If you only need to seek forward, there is no need to call this function,
|
||||||
only makes sense to use this for read-only use.
|
as the `php_stream_seek` can emulate forward seeking when the whence parameter
|
||||||
|
is `SEEK_CUR`.
|
||||||
|
|
||||||
NOTE: If the origstream is network based, this function will block
|
NOTE: Writing to the stream may not affect the original source, so it only makes
|
||||||
until the whole contents have been downloaded.
|
sense to use this for read-only use.
|
||||||
|
|
||||||
NOTE: Never call this function with an origstream that is referenced
|
NOTE: If the origstream is network based, this function will block until the
|
||||||
as a resource! It will close the origstream on success, and this
|
whole contents have been downloaded.
|
||||||
can lead to a crash when the resource is later used/released.
|
|
||||||
|
NOTE: Never call this function with an origstream that is referenced as a
|
||||||
|
resource! It will close the origstream on success, and this can lead to a crash
|
||||||
|
when the resource is later used/released.
|
||||||
|
|
||||||
NOTE: If you are opening a stream and need it to be seekable, use the
|
NOTE: If you are opening a stream and need it to be seekable, use the
|
||||||
STREAM_MUST_SEEK option to php_stream_open_wrapper();
|
`STREAM_MUST_SEEK` option to php_stream_open_wrapper();
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI int php_stream_supports_lock(php_stream * stream);
|
PHPAPI int php_stream_supports_lock(php_stream * stream);
|
||||||
|
```
|
||||||
|
|
||||||
This function will return either 1 (success) or 0 (failure) indicating whether or
|
This function will return either 1 (success) or 0 (failure) indicating whether
|
||||||
not a lock can be set on this stream. Typically you can only set locks on stdio streams.
|
or not a lock can be set on this stream. Typically you can only set locks on
|
||||||
|
stdio streams.
|
||||||
|
|
||||||
Casting Streams
|
## Casting streams
|
||||||
===============
|
|
||||||
What if your extension needs to access the FILE* of a user level file pointer?
|
|
||||||
You need to "cast" the stream into a FILE*, and this is how you do it:
|
|
||||||
|
|
||||||
|
What if your extension needs to access the `FILE*` of a user level file pointer?
|
||||||
|
You need to "cast" the stream into a `FILE*`, and this is how you do it:
|
||||||
|
|
||||||
|
```c
|
||||||
FILE * fp;
|
FILE * fp;
|
||||||
php_stream * stream; /* already opened */
|
php_stream * stream; /* already opened */
|
||||||
|
|
||||||
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {
|
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {
|
||||||
RETURN_FALSE;
|
RETURN_FALSE;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
The prototype is:
|
The prototype is:
|
||||||
|
|
||||||
PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
|
```c
|
||||||
show_err);
|
PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int show_err);
|
||||||
|
```
|
||||||
|
|
||||||
The show_err parameter, if non-zero, will cause the function to display an
|
The `show_err` parameter, if non-zero, will cause the function to display an
|
||||||
appropriate error message of type E_WARNING if the cast fails.
|
appropriate error message of type `E_WARNING` if the cast fails.
|
||||||
|
|
||||||
castas can be one of the following values:
|
`castas` can be one of the following values:
|
||||||
|
|
||||||
|
```txt
|
||||||
PHP_STREAM_AS_STDIO - a stdio FILE*
|
PHP_STREAM_AS_STDIO - a stdio FILE*
|
||||||
PHP_STREAM_AS_FD - a generic file descriptor
|
PHP_STREAM_AS_FD - a generic file descriptor
|
||||||
PHP_STREAM_AS_SOCKETD - a socket descriptor
|
PHP_STREAM_AS_SOCKETD - a socket descriptor
|
||||||
|
```
|
||||||
|
|
||||||
If you ask a socket stream for a FILE*, the abstraction will use fdopen to
|
If you ask a socket stream for a `FILE*`, the abstraction will use fdopen to
|
||||||
create it for you. Be warned that doing so may cause buffered data to be lost
|
create it for you. Be warned that doing so may cause buffered data to be lost
|
||||||
if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
|
if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
|
||||||
|
|
||||||
If your system has the fopencookie function, php streams can synthesize a
|
If your system has the fopencookie function, php streams can synthesize a
|
||||||
FILE* on top of any stream, which is useful for SSL sockets, memory based
|
`FILE*` on top of any stream, which is useful for SSL sockets, memory based
|
||||||
streams, data base streams etc. etc.
|
streams, data base streams etc. etc.
|
||||||
|
|
||||||
In situations where this is not desirable, you should query the stream
|
In situations where this is not desirable, you should query the stream to see if
|
||||||
to see if it naturally supports FILE *. You can use this code snippet
|
it naturally supports `FILE *`. You can use this code snippet for this purpose:
|
||||||
for this purpose:
|
|
||||||
|
|
||||||
if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
|
```c
|
||||||
/* can safely cast to FILE* with no adverse side effects */
|
if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
|
||||||
}
|
/* can safely cast to FILE* with no adverse side effects */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
You can use:
|
You can use:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
|
PHPAPI int php_stream_can_cast(php_stream * stream, int castas)
|
||||||
|
```
|
||||||
|
|
||||||
to find out if a stream can be cast, without actually performing the cast, so
|
to find out if a stream can be cast, without actually performing the cast, so to
|
||||||
to check if a stream is a socket you might use:
|
check if a stream is a socket you might use:
|
||||||
|
|
||||||
|
```c
|
||||||
if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
|
if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
|
||||||
/* it can be a socket */
|
/* it can be a socket */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Please note the difference between php_stream_is and php_stream_can_cast;
|
Please note the difference between `php_stream_is` and `php_stream_can_cast`;
|
||||||
stream_is tells you if the stream is a particular type of stream, whereas
|
`stream_is` tells you if the stream is a particular type of stream, whereas
|
||||||
can_cast tells you if the stream can be forced into the form you request.
|
`can_cast` tells you if the stream can be forced into the form you request. The
|
||||||
The former doesn't change anything, while the later *might* change some
|
former doesn't change anything, while the later *might* change some state in the
|
||||||
state in the stream.
|
stream.
|
||||||
|
|
||||||
Stream Internals
|
## Stream internals
|
||||||
================
|
|
||||||
|
|
||||||
There are two main structures associated with a stream - the php_stream
|
There are two main structures associated with a stream - the `php_stream`
|
||||||
itself, which holds some state information (and possibly a buffer) and a
|
itself, which holds some state information (and possibly a buffer) and a
|
||||||
php_stream_ops structure, which holds the "virtual method table" for the
|
`php_stream_ops` structure, which holds the "virtual method table" for the
|
||||||
underlying implementation.
|
underlying implementation.
|
||||||
|
|
||||||
The php_streams ops struct consists of pointers to methods that implement
|
The `php_streams` ops struct consists of pointers to methods that implement
|
||||||
read, write, close, flush, seek, gets and cast operations. Of these, an
|
read, write, close, flush, seek, gets and cast operations. Of these, an
|
||||||
implementation need only implement write, read, close and flush. The gets
|
implementation need only implement write, read, close and flush. The gets method
|
||||||
method is intended to be used for streams if there is an underlying method
|
is intended to be used for streams if there is an underlying method that can
|
||||||
that can efficiently behave as fgets. The ops struct also contains a label
|
efficiently behave as fgets. The ops struct also contains a label for the
|
||||||
for the implementation that will be used when printing error messages - the
|
implementation that will be used when printing error messages - the stdio
|
||||||
stdio implementation has a label of "STDIO" for example.
|
implementation has a label of `STDIO` for example.
|
||||||
|
|
||||||
The idea is that a stream implementation defines a php_stream_ops struct, and
|
The idea is that a stream implementation defines a `php_stream_ops` struct, and
|
||||||
associates it with a php_stream using php_stream_alloc.
|
associates it with a `php_stream` using `php_stream_alloc`.
|
||||||
|
|
||||||
As an example, the php_stream_fopen() function looks like this:
|
As an example, the `php_stream_fopen()` function looks like this:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
|
PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
|
||||||
{
|
{
|
||||||
FILE * fp = fopen(filename, mode);
|
FILE * fp = fopen(filename, mode);
|
||||||
|
@ -255,62 +276,64 @@ PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
|
`php_stream_stdio_ops` is a `php_stream_ops` structure that can be used to
|
||||||
FILE* based streams.
|
handle `FILE*` based streams.
|
||||||
|
|
||||||
A socket based stream would use code similar to that above to create a stream
|
A socket based stream would use code similar to that above to create a stream to
|
||||||
to be passed back to fopen_wrapper (or it's yet to be implemented successor).
|
be passed back to fopen_wrapper (or it's yet to be implemented successor).
|
||||||
|
|
||||||
The prototype for php_stream_alloc is this:
|
The prototype for php_stream_alloc is this:
|
||||||
|
|
||||||
|
```c
|
||||||
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
|
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
|
||||||
size_t bufsize, int persistent, const char * mode)
|
size_t bufsize, int persistent, const char * mode)
|
||||||
|
```
|
||||||
|
|
||||||
ops is a pointer to the implementation,
|
* `ops` is a pointer to the implementation,
|
||||||
abstract holds implementation specific data that is relevant to this instance
|
* `abstract` holds implementation specific data that is relevant to this
|
||||||
of the stream,
|
instance of the stream,
|
||||||
bufsize is the size of the buffer to use - if 0, then buffering at the stream
|
* `bufsize` is the size of the buffer to use - if 0, then buffering at the
|
||||||
level will be disabled (recommended for underlying sources that implement
|
stream
|
||||||
their own buffering - such a FILE*),
|
* `level` will be disabled (recommended for underlying sources that implement
|
||||||
persistent controls how the memory is to be allocated - persistently so that
|
their own buffering - such a `FILE*`)
|
||||||
it lasts across requests, or non-persistently so that it is freed at the end
|
* `persistent` controls how the memory is to be allocated - persistently so that
|
||||||
of a request (it uses pemalloc),
|
it lasts across requests, or non-persistently so that it is freed at the end
|
||||||
mode is the stdio-like mode of operation - php streams places no real meaning
|
of a request (it uses pemalloc),
|
||||||
in the mode parameter, except that it checks for a 'w' in the string when
|
* `mode` is the stdio-like mode of operation - php streams places no real
|
||||||
attempting to write (this may change).
|
meaning in the mode parameter, except that it checks for a `w` in the string
|
||||||
|
when attempting to write (this may change).
|
||||||
|
|
||||||
The mode parameter is passed on to fdopen/fopencookie when the stream is cast
|
The mode parameter is passed on to `fdopen/fopencookie` when the stream is cast
|
||||||
into a FILE*, so it should be compatible with the mode parameter of fopen().
|
into a `FILE*`, so it should be compatible with the mode parameter of `fopen()`.
|
||||||
|
|
||||||
Writing your own stream implementation
|
## Writing your own stream implementation
|
||||||
======================================
|
|
||||||
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
* **RULE #1**: when writing your own streams: make sure you have configured PHP
|
||||||
RULE #1: when writing your own streams: make sure you have configured PHP with
|
with `--enable-debug`.
|
||||||
--enable-debug.
|
Some great great pains have been taken to hook into the Zend memory manager to
|
||||||
I've taken some great pains to hook into the Zend memory manager to help track
|
help track down allocation problems. It will also help you spot incorrect use
|
||||||
down allocation problems. It will also help you spot incorrect use of the
|
of the STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for
|
||||||
STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for function
|
function definitions.
|
||||||
definitions.
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
|
|
||||||
RULE #2: Please use the stdio stream as a reference; it will help you
|
* RULE #2: Please use the stdio stream as a reference; it will help you
|
||||||
understand the semantics of the stream operations, and it will always
|
understand the semantics of the stream operations, and it will always be more
|
||||||
be more up to date than these docs :-)
|
up to date than these docs :-)
|
||||||
|
|
||||||
First, you need to figure out what data you need to associate with the
|
First, you need to figure out what data you need to associate with the
|
||||||
php_stream. For example, you might need a pointer to some memory for memory
|
`php_stream`. For example, you might need a pointer to some memory for memory
|
||||||
based streams, or if you were making a stream to read data from an RDBMS like
|
based streams, or if you were making a stream to read data from an RDBMS like
|
||||||
MySQL, you might want to store the connection and rowset handles.
|
MySQL, you might want to store the connection and rowset handles.
|
||||||
|
|
||||||
The stream has a field called abstract that you can use to hold this data.
|
The stream has a field called abstract that you can use to hold this data. If
|
||||||
If you need to store more than a single field of data, define a structure to
|
you need to store more than a single field of data, define a structure to hold
|
||||||
hold it, allocate it (use pemalloc with the persistent flag set
|
it, allocate it (use pemalloc with the persistent flag set appropriately), and
|
||||||
appropriately), and use the abstract pointer to refer to it.
|
use the abstract pointer to refer to it.
|
||||||
|
|
||||||
For structured state you might have this:
|
For structured state you might have this:
|
||||||
|
|
||||||
|
```c
|
||||||
struct my_state {
|
struct my_state {
|
||||||
MYSQL conn;
|
MYSQL conn;
|
||||||
MYSQL_RES * result;
|
MYSQL_RES * result;
|
||||||
|
@ -327,6 +350,7 @@ state->result = mysql_use_result(&state->conn);
|
||||||
stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
|
stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
|
||||||
|
|
||||||
/* now stream->abstract == state */
|
/* now stream->abstract == state */
|
||||||
|
```
|
||||||
|
|
||||||
Once you have that part figured out, you can write your implementation and
|
Once you have that part figured out, you can write your implementation and
|
||||||
define the your own php_stream_ops struct (we called it my_ops in the above
|
define the your own php_stream_ops struct (we called it my_ops in the above
|
||||||
|
@ -334,6 +358,7 @@ example).
|
||||||
|
|
||||||
For example, for reading from this weird MySQL stream:
|
For example, for reading from this weird MySQL stream:
|
||||||
|
|
||||||
|
```c
|
||||||
static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
|
static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
|
||||||
{
|
{
|
||||||
struct my_state * state = (struct my_state*)stream->abstract;
|
struct my_state * state = (struct my_state*)stream->abstract;
|
||||||
|
@ -354,23 +379,27 @@ static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
|
||||||
such as coping with a buffer size too small to hold the data,
|
such as coping with a buffer size too small to hold the data,
|
||||||
so I won't even go in to how to do that here */
|
so I won't even go in to how to do that here */
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Implement the other operations - remember that write, read, close and flush
|
Implement the other operations - remember that write, read, close and flush are
|
||||||
are all mandatory. The rest are optional. Declare your stream ops struct:
|
all mandatory. The rest are optional. Declare your stream ops struct:
|
||||||
|
|
||||||
|
```c
|
||||||
php_stream_ops my_ops = {
|
php_stream_ops my_ops = {
|
||||||
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
|
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
|
||||||
php_mysqlop_flush, NULL, NULL, NULL,
|
php_mysqlop_flush, NULL, NULL, NULL,
|
||||||
"Strange MySQL example"
|
"Strange MySQL example"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
That's it!
|
That's it!
|
||||||
|
|
||||||
Take a look at the STDIO implementation in streams.c for more information
|
Take a look at the STDIO implementation in streams.c for more information about
|
||||||
about how these operations work.
|
how these operations work.
|
||||||
|
|
||||||
The main thing to remember is that in your close operation you need to release
|
The main thing to remember is that in your close operation you need to release
|
||||||
and free the resources you allocated for the abstract field. In the case of
|
and free the resources you allocated for the abstract field. In the case of the
|
||||||
the example above, you need to use mysql_free_result on the rowset, close the
|
example above, you need to use mysql_free_result on the rowset, close the
|
||||||
connection and then use pefree to dispose of the struct you allocated.
|
connection and then use pefree to dispose of the struct you allocated. You may
|
||||||
You may read the stream->persistent field to determine if your struct was
|
read the stream->persistent field to determine if your struct was allocated in
|
||||||
allocated in persistent mode or not.
|
persistent mode or not.
|
||||||
|
|
|
@ -1,113 +1,121 @@
|
||||||
# PHP Build System V5 Overview
|
# PHP build system V5 overview
|
||||||
|
|
||||||
- supports Makefile.ins during transition phase
|
* supports Makefile.ins during transition phase
|
||||||
- not-really-portable Makefile includes have been eliminated
|
* not-really-portable Makefile includes have been eliminated
|
||||||
- supports separate build directories without VPATH by using
|
* supports separate build directories without VPATH by using explicit rules only
|
||||||
explicit rules only
|
* does not waste disk-space/CPU-time for building temporary libraries =>
|
||||||
- does not waste disk-space/CPU-time for building temporary libraries
|
especially noticeable on slower systems
|
||||||
=> especially noticeable on slower systems
|
* slow recursive make replaced with one global Makefile
|
||||||
- slow recursive make replaced with one global Makefile
|
* eases integration of proper dependencies
|
||||||
- eases integration of proper dependencies
|
* adds PHP_DEFINE(what[, value]) which creates a single include-file per what.
|
||||||
- adds PHP_DEFINE(what[, value]) which creates a single include-file
|
This will allow more fine-grained dependencies.
|
||||||
per what. This will allow more fine-grained dependencies.
|
* abandoning the "one library per directory" concept
|
||||||
- abandoning the "one library per directory" concept
|
* improved integration of the CLI
|
||||||
- improved integration of the CLI
|
* several new targets:
|
||||||
- several new targets
|
* `build-modules`: builds and copies dynamic modules into `modules/`
|
||||||
build-modules: builds and copies dynamic modules into modules/
|
* `install-cli`: installs the CLI only, so that the install-sapi target does
|
||||||
install-cli: installs the CLI only, so that the install-sapi
|
only what its name says
|
||||||
target does only what its name says
|
* finally abandoned automake
|
||||||
- finally abandoned automake
|
* changed some configure-time constructs to run at buildconf-time
|
||||||
- changed some configure-time constructs to run at buildconf-time
|
* upgraded shtool to 1.5.4
|
||||||
- upgraded shtool to 1.5.4
|
* removed `$(moduledir)` (use `EXTENSION_DIR`)
|
||||||
- removed $(moduledir) (use EXTENSION_DIR)
|
|
||||||
|
|
||||||
## The Reason For a New System
|
## The reason for a new system
|
||||||
|
|
||||||
It became more and more apparent that there is a severe need
|
It became more and more apparent that there is a severe need for addressing the
|
||||||
for addressing the portability concerns and improving the chance
|
portability concerns and improving the chance that your build is correct (how
|
||||||
that your build is correct (how often have you been told to
|
often have you been told to `make clean`? When this is done, you won't need to
|
||||||
"make clean"? When this is done, you won't need to anymore).
|
anymore).
|
||||||
|
|
||||||
## If You Build PHP on a Unix System
|
## If you build PHP on a Unix system
|
||||||
|
|
||||||
You, as a user of PHP, will notice no changes. Of course, the build
|
You, as a user of PHP, will notice no changes. Of course, the build system will
|
||||||
system will be faster, look better and work smarter.
|
be faster, look better and work smarter.
|
||||||
|
|
||||||
## If You Are Developing PHP
|
## If you are developing PHP
|
||||||
|
|
||||||
### Extension developers:
|
### Extension developers
|
||||||
|
|
||||||
Makefile.ins are abandoned. The files which are to be compiled
|
Makefile.ins are abandoned. The files which are to be compiled are specified in
|
||||||
are specified in the config.m4 now using the following macro:
|
the `config.m4` now using the following macro:
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_NEW_EXTENSION(foo, foo.c bar.c baz.cpp, $ext_shared)
|
PHP_NEW_EXTENSION(foo, foo.c bar.c baz.cpp, $ext_shared)
|
||||||
|
```
|
||||||
|
|
||||||
E.g. this enables the extension foo which consists of three source-code
|
E.g. this enables the extension foo which consists of three source-code modules,
|
||||||
modules, two in C and one in C++. And, depending on the user's wishes,
|
two in C and one in C++. And, depending on the user's wishes, the extension will
|
||||||
the extension will even be built as a dynamic module.
|
even be built as a dynamic module.
|
||||||
|
|
||||||
The full syntax:
|
The full syntax:
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_NEW_EXTENSION(extname, sources [, shared [,sapi_class[, extra-cflags]]])
|
PHP_NEW_EXTENSION(extname, sources [, shared [,sapi_class[, extra-cflags]]])
|
||||||
|
```
|
||||||
|
|
||||||
Please have a look at acinclude.m4 for the gory details and meanings
|
Please have a look at `acinclude.m4` for the gory details and meanings of the
|
||||||
of the other parameters.
|
other parameters.
|
||||||
|
|
||||||
And that's basically it for the extension side.
|
And that's basically it for the extension side.
|
||||||
|
|
||||||
If you previously built sub-libraries for this module, add
|
If you previously built sub-libraries for this module, add the source-code files
|
||||||
the source-code files here as well. If you need to specify
|
here as well. If you need to specify separate include directories, do it this
|
||||||
separate include directories, do it this way:
|
way:
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_NEW_EXTENSION(foo, foo.c mylib/bar.c mylib/gregor.c,,,-I@ext_srcdir@/lib)
|
PHP_NEW_EXTENSION(foo, foo.c mylib/bar.c mylib/gregor.c,,,-I@ext_srcdir@/lib)
|
||||||
|
```
|
||||||
|
|
||||||
E.g. this builds the three files which are located relative to the
|
E.g. this builds the three files which are located relative to the extension
|
||||||
extension source directory and compiles all three files with the
|
source directory and compiles all three files with the special include directive
|
||||||
special include directive (@ext_srcdir@ is automatically replaced).
|
(`@ext_srcdir@` is automatically replaced).
|
||||||
|
|
||||||
Now, you need to tell the build system that you want to build files
|
Now, you need to tell the build system that you want to build files in a
|
||||||
in a directory called $ext_builddir/lib:
|
directory called `$ext_builddir/lib`:
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_ADD_BUILD_DIR($ext_builddir/lib)
|
PHP_ADD_BUILD_DIR($ext_builddir/lib)
|
||||||
|
```
|
||||||
|
|
||||||
Make sure to call this after PHP_NEW_EXTENSION, because $ext_builddir
|
Make sure to call this after `PHP_NEW_EXTENSION`, because `$ext_builddir` is
|
||||||
is only set by the latter.
|
only set by the latter.
|
||||||
|
|
||||||
If you have a complex extension, you might to need add special
|
If you have a complex extension, you might to need add special Make rules. You
|
||||||
Make rules. You can do this by calling PHP_ADD_MAKEFILE_FRAGMENT
|
can do this by calling `PHP_ADD_MAKEFILE_FRAGMENT` in your `config.m4` after
|
||||||
in your config.m4 after PHP_NEW_EXTENSION.
|
`PHP_NEW_EXTENSION`.
|
||||||
|
|
||||||
This will read a file in the source-dir of your extension called
|
This will read a file in the source-dir of your extension called
|
||||||
Makefile.frag. In this file, $(builddir) and $(srcdir) will be
|
`Makefile.frag`. In this file, `$(builddir)` and `$(srcdir)` will be replaced by
|
||||||
replaced by the values which are correct for your extension
|
the values which are correct for your extension and which are again determined
|
||||||
and which are again determined by the PHP_NEW_EXTENSION macro.
|
by the `PHP_NEW_EXTENSION` macro.
|
||||||
|
|
||||||
Make sure to prefix *all* relative paths correctly with either
|
Make sure to prefix *all* relative paths correctly with either `$(builddir)` or
|
||||||
$(builddir) or $(srcdir). Because the build system does not
|
`$(srcdir)`. Because the build system does not change the working directory
|
||||||
change the working directory anymore, we must use either
|
anymore, we must use either absolute paths or relative ones to the top
|
||||||
absolute paths or relative ones to the top build-directory.
|
build-directory. Correct prefixing ensures that.
|
||||||
Correct prefixing ensures that.
|
|
||||||
|
|
||||||
### SAPI developers:
|
### SAPI developers
|
||||||
|
|
||||||
Instead of using PHP_SAPI=foo/PHP_BUILD_XYZ, you will need to type
|
Instead of using `PHP_SAPI=foo/PHP_BUILD_XYZ`, you will need to type
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_SELECT_SAPI(name, type, sources.c)
|
PHP_SELECT_SAPI(name, type, sources.c)
|
||||||
|
```
|
||||||
|
|
||||||
I.e. specify the source-code files as above and also pass the
|
I.e. specify the source-code files as above and also pass the information
|
||||||
information regarding how PHP is supposed to be built (shared
|
regarding how PHP is supposed to be built (shared module, program, etc).
|
||||||
module, program, etc).
|
|
||||||
|
|
||||||
For example for APXS:
|
For example for APXS:
|
||||||
|
|
||||||
|
```m4
|
||||||
PHP_SELECT_SAPI(apache, shared, sapi_apache.c mod_php.c php_apache.c)
|
PHP_SELECT_SAPI(apache, shared, sapi_apache.c mod_php.c php_apache.c)
|
||||||
|
```
|
||||||
|
|
||||||
## General info
|
## General info
|
||||||
|
|
||||||
The foundation for the new system is the flexible handling of
|
The foundation for the new system is the flexible handling of sources and their
|
||||||
sources and their contexts. With the help of macros you
|
contexts. With the help of macros you can define special flags for each
|
||||||
can define special flags for each source-file, where it is
|
source-file, where it is located, in which target context it can work, etc.
|
||||||
located, in which target context it can work, etc.
|
|
||||||
|
|
||||||
Have a look at the well documented macros
|
Have a look at the well documented macros `PHP_ADD_SOURCES(_X)` in
|
||||||
PHP_ADD_SOURCES(_X) in acinclude.m4.
|
`acinclude.m4`.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
# FFI PHP extension (Foreign Function Interface)
|
# FFI PHP extension (Foreign Function Interface)
|
||||||
|
|
||||||
FFI PHP extension provides a simple way to call native functions, access native variables and create/access data structures defined in C language. The API of the extension is very simple and demonstrated by the following example and its output.
|
FFI PHP extension provides a simple way to call native functions, access native
|
||||||
|
variables and create/access data structures defined in C language. The API of
|
||||||
|
the extension is very simple and demonstrated by the following example and its
|
||||||
|
output.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
<?php
|
<?php
|
||||||
|
@ -22,7 +25,7 @@ $libc = FFI::cdef("
|
||||||
int tz_dsttime;
|
int tz_dsttime;
|
||||||
};
|
};
|
||||||
|
|
||||||
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
||||||
", "libc.so.6");
|
", "libc.so.6");
|
||||||
|
|
||||||
$libc->printf("Hello World from %s!\n", "PHP");
|
$libc->printf("Hello World from %s!\n", "PHP");
|
||||||
|
@ -36,7 +39,7 @@ var_dump($tv->tv_sec, $tv->tv_usec, $tz);
|
||||||
?>
|
?>
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```txt
|
||||||
Hello World from PHP!
|
Hello World from PHP!
|
||||||
string(135) "/usr/lib64/qt-3.3/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/dmitry/.local/bin:/home/dmitry/bin"
|
string(135) "/usr/lib64/qt-3.3/bin:/usr/lib64/ccache:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/dmitry/.local/bin:/home/dmitry/bin"
|
||||||
int(1523617815)
|
int(1523617815)
|
||||||
|
@ -50,13 +53,22 @@ object(FFI\CData:<struct>)#3 (2) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
FFI::cdef() takes two arguments (both are optional). The first one is a collection of C declarations and the second is DSO library. All variables and functions defined by first arguments are bound to corresponding native symbols in DSO library and then may be accessed as FFI object methods and properties. C types of argument, return value and variables are automatically converted to/from PHP types (if possible). Otherwise, they are wrapped in a special CData proxy object and may be accessed by elements.
|
`FFI::cdef()` takes two arguments (both are optional). The first one is a
|
||||||
|
collection of C declarations and the second is DSO library. All variables and
|
||||||
|
functions defined by first arguments are bound to corresponding native symbols
|
||||||
|
in DSO library and then may be accessed as FFI object methods and properties. C
|
||||||
|
types of argument, return value and variables are automatically converted
|
||||||
|
to/from PHP types (if possible). Otherwise, they are wrapped in a special CData
|
||||||
|
proxy object and may be accessed by elements.
|
||||||
|
|
||||||
In some cases (e.g. passing C structure by pointer) we may need to create a real C data structures. This is possible using FFF::new() method. It takes a C type definition and may reuse C types and tags defined by FFI::cdef().
|
In some cases (e.g. passing C structure by pointer) we may need to create a real
|
||||||
|
C data structures. This is possible using `FFF::new()` method. It takes a C type
|
||||||
|
definition and may reuse C types and tags defined by `FFI::cdef()`.
|
||||||
|
|
||||||
It's also possible to use FFI::new() as a static method to create arbitrary C data structures.
|
It's also possible to use `FFI::new()` as a static method to create arbitrary C
|
||||||
|
data structures.
|
||||||
|
|
||||||
``` php
|
```php
|
||||||
<?php
|
<?php
|
||||||
$p = FFI::new("struct {int x,y;} [2]");
|
$p = FFI::new("struct {int x,y;} [2]");
|
||||||
$p[0]->x = 5;
|
$p[0]->x = 5;
|
||||||
|
@ -64,7 +76,7 @@ $p[1]->y = 10;
|
||||||
var_dump($p);
|
var_dump($p);
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```txt
|
||||||
object(FFI\CData:<struct>[2])#1 (2) {
|
object(FFI\CData:<struct>[2])#1 (2) {
|
||||||
[0]=>
|
[0]=>
|
||||||
object(FFI\CData:<struct>)#2 (2) {
|
object(FFI\CData:<struct>)#2 (2) {
|
||||||
|
@ -83,132 +95,169 @@ object(FFI\CData:<struct>[2])#1 (2) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### API Reference
|
## API reference
|
||||||
|
|
||||||
##### function FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI
|
### function FFI::cdef([string $cdef = "" [, string $lib = null]]): FFI
|
||||||
|
|
||||||
##### Call Native Functions
|
#### Call native functions
|
||||||
|
|
||||||
All functions defined in FFI::cdef() may be called as methods of the created FFI object.
|
All functions defined in `FFI::cdef()` may be called as methods of the created
|
||||||
|
FFI object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$libc = FFI::cdef("const char * getenv(const char *);", "libc.so.6");
|
$libc = FFI::cdef("const char * getenv(const char *);", "libc.so.6");
|
||||||
var_dump($libc->getenv("PATH"));
|
var_dump($libc->getenv("PATH"));
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Read/Write Values of Native Variables
|
#### Read/write values of native variables
|
||||||
|
|
||||||
All functions defined in FFI::cdef() may be accessed as properties of the created FFI object.
|
All functions defined in `FFI::cdef()` may be accessed as properties of the
|
||||||
|
created FFI object.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$libc = FFI::cdef("extern int errno;", "libc.so.6");
|
$libc = FFI::cdef("extern int errno;", "libc.so.6");
|
||||||
var_dump($libc->errno);
|
var_dump($libc->errno);
|
||||||
```
|
```
|
||||||
|
|
||||||
##### function FFI::type(string $type): FFI\CType
|
### function FFI::type(string $type): FFI\CType
|
||||||
|
|
||||||
This function creates and returns a **FFI\CType** object, representng type of the given C type declaration string.
|
This function creates and returns a `FFI\CType` object, representng type of
|
||||||
|
the given C type declaration string.
|
||||||
|
|
||||||
FFI::type() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
|
`FFI::type()` may be called statically and use only predefined types, or as a
|
||||||
|
method of previously created FFI object. In last case the first argument may
|
||||||
|
reuse all type and tag names defined in `FFI::cdef()`.
|
||||||
|
|
||||||
|
### function FFI::typeof(FFI\CData $type): FFI\CType
|
||||||
|
|
||||||
##### function FFI::typeof(FFI\CData $type): FFI\CType
|
This function returns a `FFI\CType` object, representing the type of the given
|
||||||
|
`FFI\CData` object.
|
||||||
|
|
||||||
This function returns a **FFI\CType** object, representing the type of the given **FFI\CData** object.
|
### static function FFI::arrayType(FFI\CType $type, array $dims): FFI\CType
|
||||||
|
|
||||||
##### static function FFI::arrayType(FFI\CType $type, array $dims): FFI\CType
|
Constructs a new C array type with elements of $type and dimensions specified by
|
||||||
|
$dims.
|
||||||
|
|
||||||
Constructs a new C array type with elements of $type and dimensions specified by $dims.
|
### function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData
|
||||||
|
|
||||||
##### function FFI::new(mixed $type [, bool $own = true [, bool $persistent = false]]): FFI\CData
|
This function may be used to create a native data structure. The first argument
|
||||||
|
is a C type definition. It may be a `string` or `FFI\CType` object. The
|
||||||
This function may be used to create a native data structure. The first argument is a C type definition. It may be a **string** or **FFI\CType** object. The following example creates two dimensional array of integers.
|
following example creates two dimensional array of integers.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$p = FFI::new("int[2][2]");
|
$p = FFI::new("int[2][2]");
|
||||||
var_dump($p, FFI::sizeof($p));
|
var_dump($p, FFI::sizeof($p));
|
||||||
```
|
```
|
||||||
|
|
||||||
FFI::new() may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
|
`FFI::new()` may be called statically and use only predefined types, or as a
|
||||||
|
method of previously created FFI object. In last case the first argument may
|
||||||
|
reuse all type and tag names defined in `FFI::cdef()`.
|
||||||
|
|
||||||
By default **FFI::new()** creates "owned" native data structures, that live together with corresponding PHP object, reusing PHP reference-counting and GC. However, in some cases it may be necessary to manually control the life time of the data structure. In this case, the PHP ownership on the corresponding data, may be manually changed, using **false** as the second optianal argument. Later, not-owned CData should be manually deallocated using **FFI::free()**.
|
By default `FFI::new()` creates "owned" native data structures, that live
|
||||||
|
together with corresponding PHP object, reusing PHP reference-counting and GC.
|
||||||
|
However, in some cases it may be necessary to manually control the life time of
|
||||||
|
the data structure. In this case, the PHP ownership on the corresponding data,
|
||||||
|
may be manually changed, using `false` as the second optianal argument. Later,
|
||||||
|
not-owned CData should be manually deallocated using `FFI::free()`.
|
||||||
|
|
||||||
Using the optional $persistent argument it's possible to allocate C objects in persistent memory, through malloc(), otherwise memory is allocated in PHP request heap, through emalloc().
|
Using the optional $persistent argument it's possible to allocate C objects in
|
||||||
|
persistent memory, through `malloc()`, otherwise memory is allocated in PHP
|
||||||
|
request heap, through `emalloc()`.
|
||||||
|
|
||||||
##### static function FFI::free(FFI\CData $cdata): void
|
### static function FFI::free(FFI\CData $cdata): void
|
||||||
|
|
||||||
manually removes previously created "not-owned" data structure.
|
Manually removes previously created "not-owned" data structure.
|
||||||
|
|
||||||
##### Read/Write Elements of Native Arrays
|
#### Read/write elements of native arrays
|
||||||
|
|
||||||
Elements of native array may be accessed in the same way as elements of PHP arrays. Of course, native arrays support only integer indexes. It's not possible to check element existence using isset() or empty() and remove element using unset(). Native arrays work fine with "foreach" statement.
|
Elements of native array may be accessed in the same way as elements of PHP
|
||||||
|
arrays. Of course, native arrays support only integer indexes. It's not possible
|
||||||
|
to check element existence using `isset()` or `empty()` and remove element using
|
||||||
|
`unset()`. Native arrays work fine with "foreach" statement.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$p = FFI::new("int[2]");
|
$p = FFI::new("int[2]");
|
||||||
$p[0] = 1;
|
$p[0] = 1;
|
||||||
$p[1] = 2;
|
$p[1] = 2;
|
||||||
foreach ($p as $key => $val) {
|
foreach ($p as $key => $val) {
|
||||||
echo "$key => $val\n";
|
echo "$key => $val\n";
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Read/Write Fields of Native "struct" or "union"
|
#### Read/write fields of native "struct" or "union"
|
||||||
|
|
||||||
Fields of native struct/union may be accessed in the same way as properties of PHP objects. It's not possible to check filed existence using isset() or empty(), remove them using unset(), and iterate using "foreach" statement.
|
Fields of native struct/union may be accessed in the same way as properties of
|
||||||
|
PHP objects. It's not possible to check filed existence using `isset()` or
|
||||||
|
`empty()`, remove them using `unset()`, and iterate using "foreach" statement.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$pp = FFI::new("struct {int x,y;}[2]");
|
$pp = FFI::new("struct {int x,y;}[2]");
|
||||||
foreach($pp as $n => &$p) {
|
foreach($pp as $n => &$p) {
|
||||||
$p->x = $p->y = $n;
|
$p->x = $p->y = $n;
|
||||||
}
|
}
|
||||||
var_dump($pp);
|
var_dump($pp);
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Pointer arithmetic
|
#### Pointer arithmetic
|
||||||
|
|
||||||
CData pointer values may be incremented/decremented by a number. The result is a pointer of the same type moved on given offset.
|
CData pointer values may be incremented/decremented by a number. The result is a
|
||||||
|
pointer of the same type moved on given offset.
|
||||||
|
|
||||||
Two pointers to the same type may be subtracted and return difference (similar to C).
|
Two pointers to the same type may be subtracted and return difference (similar
|
||||||
|
to C).
|
||||||
|
|
||||||
##### static function FFI::sizeof(mixed $cdata_or_ctype): int
|
### static function FFI::sizeof(mixed $cdata_or_ctype): int
|
||||||
|
|
||||||
returns size of C data type of the given **FFI\CData** or **FFI\CType**.
|
Returns size of C data type of the given `FFI\CData` or `FFI\CType`.
|
||||||
|
|
||||||
##### static function FFI::alignof(mixed $cdata_or_ctype): int
|
### static function FFI::alignof(mixed $cdata_or_ctype): int
|
||||||
|
|
||||||
returns size of C data type of the given **FFI\CData** or **FFI\CType**.
|
Returns size of C data type of the given `FFI\CData` or `FFI\CType`.
|
||||||
|
|
||||||
##### static function FFI::memcpy(FFI\CData $dst, mixed $src, int $size): void
|
### static function FFI::memcpy(FFI\CData $dst, mixed $src, int $size): void
|
||||||
|
|
||||||
copies $size bytes from memory area $src to memory area $dst. $src may be any native data structure (**FFI\CData**) or PHP **string**.
|
Copies $size bytes from memory area $src to memory area $dst. $src may be any
|
||||||
|
native data structure (`FFI\CData`) or PHP `string`.
|
||||||
|
|
||||||
##### static function FFI::memcmp(mixed $src1, mixed $src2, int $size): int
|
### static function FFI::memcmp(mixed $src1, mixed $src2, int $size): int
|
||||||
|
|
||||||
compares $size bytes from memory area $src1 and $dst2. $src1 and $src2 may be any native data structures (**FFI\CData**) or PHP **string**s.
|
Compares $size bytes from memory area `$src1` and `$dst2`. `$src1` and `$src2`
|
||||||
|
may be any native data structures (`FFI\CData`) or PHP `string`s.
|
||||||
|
|
||||||
##### static function FFI::memset(FFI\CData $dst, int $c, int $size): void
|
### static function FFI::memset(FFI\CData $dst, int $c, int $size): void
|
||||||
|
|
||||||
fills the $size bytes of the memory area pointed to by $dst with the constant byte $c
|
Fills the $size bytes of the memory area pointed to by `$dst` with the constant
|
||||||
|
byte `$c`.
|
||||||
|
|
||||||
##### static function FFI::string(FFI\CData $src [, int $size]): string
|
### static function FFI::string(FFI\CData $src [, int $size]): string
|
||||||
|
|
||||||
creates a PHP string from $size bytes of memory area pointed by $src. If size is omitted, $src must be zero terminated array of C chars.
|
Creates a PHP string from `$size` bytes of memory area pointed by `$src`. If
|
||||||
|
size is omitted, $src must be zero terminated array of C chars.
|
||||||
|
|
||||||
##### function FFI::cast(mixed $type, FFI\CData $cdata): FFI\CData
|
### function FFI::cast(mixed $type, FFI\CData $cdata): FFI\CData
|
||||||
|
|
||||||
Casts given $cdata to another C type, specified by C declaration **string** or **FFI\CType** object.
|
Casts given $cdata to another C type, specified by C declaration `string` or
|
||||||
|
`FFI\CType` object.
|
||||||
|
|
||||||
This function may be called statically and use only predefined types, or as a method of previously created FFI object. In last case the first argument may reuse all type and tag names defined in FFI::cdef().
|
This function may be called statically and use only predefined types, or as a
|
||||||
|
method of previously created FFI object. In last case the first argument may
|
||||||
|
reuse all type and tag names defined in `FFI::cdef()`.
|
||||||
|
|
||||||
##### static function addr(FFI\CData $cdata): FFI\CData;
|
### static function addr(FFI\CData $cdata): FFI\CData
|
||||||
|
|
||||||
Returns C pointer to the given C data structure. The pointer is not "owned" and won't be free. Anyway, this is a potentially unsafe operation, because the life-time of the returned pointer may be longer than life-time of the source object, and this may cause dangling pointer dereference (like in regular C).
|
Returns C pointer to the given C data structure. The pointer is not "owned" and
|
||||||
|
won't be free. Anyway, this is a potentially unsafe operation, because the
|
||||||
|
life-time of the returned pointer may be longer than life-time of the source
|
||||||
|
object, and this may cause dangling pointer dereference (like in regular C).
|
||||||
|
|
||||||
##### static function load(string $filename): FFI;
|
### static function load(string $filename): FFI
|
||||||
|
|
||||||
Instead of embedding of a long C definition into PHP string, and creating FFI through FFI::cdef(), it's possible to separate it into a C header file. Note, that C preprocessor directives (e.g. #define or #ifdef) are not supported. And only a couple of special macros may be used especially for FFI.
|
Instead of embedding of a long C definition into PHP string, and creating FFI
|
||||||
|
through `FFI::cdef()`, it's possible to separate it into a C header file. Note,
|
||||||
|
that C preprocessor directives (e.g. `#define` or `#ifdef`) are not supported.
|
||||||
|
And only a couple of special macros may be used especially for FFI.
|
||||||
|
|
||||||
``` C
|
```c
|
||||||
#define FFI_LIB "libc.so.6"
|
#define FFI_LIB "libc.so.6"
|
||||||
|
|
||||||
int printf(const char *format, ...);
|
int printf(const char *format, ...);
|
||||||
|
@ -216,41 +265,54 @@ int printf(const char *format, ...);
|
||||||
|
|
||||||
Here, FFI_LIB specifies, that the given library should be loaded.
|
Here, FFI_LIB specifies, that the given library should be loaded.
|
||||||
|
|
||||||
``` php
|
```php
|
||||||
$ffi = FFI::load(__DIR__ . "/printf.h");
|
$ffi = FFI::load(__DIR__ . "/printf.h");
|
||||||
$ffi->printf("Hello world!\n");
|
$ffi->printf("Hello world!\n");
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
##### static function scope(string $name): FFI;
|
### static function scope(string $name): FFI
|
||||||
|
|
||||||
FFI definition parsing and shared library loading may take significant time. It's not useful to do it on each HTTP request in WEB environment. However, it's possible to pre-load FFI definitions and libraries at php startup, and instantiate FFI objects when necessary. Header files may be extended with **FFI_SCOPE** define (default pre-loading scope is "C"). This name is going to be used as **FFI::scope()** argument. It's possible to pre-load few files into a single scope.
|
FFI definition parsing and shared library loading may take significant time.
|
||||||
|
It's not useful to do it on each HTTP request in WEB environment. However, it's
|
||||||
|
possible to pre-load FFI definitions and libraries at php startup, and
|
||||||
|
instantiate FFI objects when necessary. Header files may be extended with
|
||||||
|
`FFI_SCOPE` define (default pre-loading scope is "C"). This name is going to
|
||||||
|
be used as `FFI::scope()` argument. It's possible to pre-load few files into a
|
||||||
|
single scope.
|
||||||
|
|
||||||
``` C
|
```c
|
||||||
#define FFI_LIB "libc.so.6"
|
#define FFI_LIB "libc.so.6"
|
||||||
#define FFI_SCOPE "libc"
|
#define FFI_SCOPE "libc"
|
||||||
|
|
||||||
int printf(const char *format, ...);
|
int printf(const char *format, ...);
|
||||||
```
|
```
|
||||||
|
|
||||||
These files are loaded through the same **FFI::load()** load function, executed from file loaded by **opcache.preload** php.ini directive.
|
These files are loaded through the same `FFI::load()` load function, executed
|
||||||
|
from file loaded by `opcache.preload` php.ini directive.
|
||||||
|
|
||||||
``` ini
|
```ini
|
||||||
ffi.preload=/etc/php/ffi/printf.h
|
ffi.preload=/etc/php/ffi/printf.h
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, **FFI::scope()** instantiate an **FFI** object, that implements all C definition from the given scope.
|
Finally, `FFI::scope()` instantiates an `FFI` object, that implements all C
|
||||||
|
definitions from the given scope.
|
||||||
|
|
||||||
``` php
|
```php
|
||||||
$ffi = FFI::scope("libc");
|
$ffi = FFI::scope("libc");
|
||||||
$ffi->printf("Hello world!\n");
|
$ffi->printf("Hello world!\n");
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Owned and Not-Owned CData
|
## Owned and not-owned CData
|
||||||
|
|
||||||
FFI extension uses two kind of native C data structures. "Owned" pointers are created using **FFI::new([, true])**, **clone**ed. Owned data is deallocated together with last PHP variable, that reference it. This mechanism reuses PHP reference-counting and garbage-collector.
|
FFI extension uses two kind of native C data structures. "Owned" pointers are
|
||||||
|
created using `FFI::new([, true])`, `clone`d. Owned data is deallocated
|
||||||
|
together with last PHP variable, that reference it. This mechanism reuses PHP
|
||||||
|
reference-counting and garbage-collector.
|
||||||
|
|
||||||
Elements of C arrays and structures, as well as most data structures returned by C functions are "not-owned". They work just as regular C pointers. They may leak memory, if not freed manually using **FFI::free()**, or may become dangling pointers and lead to PHP crashes.
|
Elements of C arrays and structures, as well as most data structures returned by
|
||||||
|
C functions are "not-owned". They work just as regular C pointers. They may leak
|
||||||
|
memory, if not freed manually using `FFI::free()`, or may become dangling
|
||||||
|
pointers and lead to PHP crashes.
|
||||||
|
|
||||||
The following example demonstrates the problem.
|
The following example demonstrates the problem.
|
||||||
|
|
||||||
|
@ -261,7 +323,8 @@ unset($p1); // $p1 is deallocated ($p2 became dangling pointer)
|
||||||
var_dump($p2); // crash because dereferencing of dangling pointer
|
var_dump($p2); // crash because dereferencing of dangling pointer
|
||||||
```
|
```
|
||||||
|
|
||||||
It's possible to change ownership, to avoid this crash, but this would require manual memory management and may lead to memory leaks
|
It's possible to change ownership, to avoid this crash, but this would require
|
||||||
|
manual memory management and may lead to memory leaks.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$p1 = FFI::new("int[2][2]", false); // $p1 is not-owned pointer
|
$p1 = FFI::new("int[2][2]", false); // $p1 is not-owned pointer
|
||||||
|
@ -270,36 +333,50 @@ unset($p1); // $p1 CData is keep alive (memory leak)
|
||||||
var_dump($p2); // works fine, except of memory leak
|
var_dump($p2); // works fine, except of memory leak
|
||||||
```
|
```
|
||||||
|
|
||||||
##### PHP Callbacks
|
## PHP callbacks
|
||||||
|
|
||||||
It's possible to assign PHP function to native function variable (or pass it as a function argument). This seems to work, but this functionality is not supported on all libffi platforms, it is not efficient and leaks resources by the end of request.
|
It's possible to assign PHP function to native function variable (or pass it as
|
||||||
|
a function argument). This seems to work, but this functionality is not
|
||||||
|
supported on all libffi platforms, it is not efficient and leaks resources by
|
||||||
|
the end of request.
|
||||||
|
|
||||||
##### FFI API restriction
|
## FFI API restriction
|
||||||
|
|
||||||
With FFI users may do almost anything, like in C, and therefor may crash PHP in thousand ways. It's possible to completely disable or enable all FFI functions using ffi.enable=0/1 configuration directives, or limit FFI usage to preloaded scripts using ffi.enable=preload (this is the default setting). In case FFI is not completely disabled, it's also enabled for CLI scripts. Finally, the restriction affects only FFI functions their selves, but not the overloaded method of created FFI or CData objects.
|
With FFI users may do almost anything, like in C, and therefor may crash PHP in
|
||||||
|
thousand ways. It's possible to completely disable or enable all FFI functions
|
||||||
|
using `ffi.enable=0/1` configuration directives, or limit FFI usage to preloaded
|
||||||
|
scripts using `ffi.enable=preload` (this is the default setting). In case FFI is
|
||||||
|
not completely disabled, it's also enabled for CLI scripts. Finally, the
|
||||||
|
restriction affects only FFI functions their selves, but not the overloaded
|
||||||
|
method of created FFI or CData objects.
|
||||||
|
|
||||||
### Status
|
## Status
|
||||||
|
|
||||||
In current state, access to FFI data structures is significantly (about 2 times) slower, than access to PHP arrays and objects. It make no sense to use them for speed, but may make sense to reduce memory consumption.
|
In current state, access to FFI data structures is significantly (about 2 times)
|
||||||
|
slower, than access to PHP arrays and objects. It makes no sense to use them for
|
||||||
|
speed, but may make sense to reduce memory consumption.
|
||||||
|
|
||||||
FFI functionality may be included into PHP-8 core, to provide better interpretation performance and integrate with JIT, providing almost C performance (similar to LuaJIT)
|
FFI functionality may be included into PHP-8 core, to provide better
|
||||||
|
interpretation performance and integrate with JIT, providing almost C
|
||||||
|
performance (similar to LuaJIT).
|
||||||
|
|
||||||
### Requirement
|
## Requirement
|
||||||
|
|
||||||
- [libffi-3.*](http://sourceware.org/libffi/)
|
* [libffi-3.*](https://sourceware.org/libffi/)
|
||||||
|
|
||||||
### Install
|
## Installation
|
||||||
|
|
||||||
``` bash
|
```bash
|
||||||
./configure ... --with-ffi
|
./configure ... --with-ffi
|
||||||
make
|
make
|
||||||
sudo make install
|
sudo make install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Real Usage
|
## Real usage
|
||||||
|
|
||||||
FFI extension was used to implement [PHP TensorFlow binding](https://github.com/dstogov/php-tensorflow)
|
FFI extension was used to implement
|
||||||
|
[PHP TensorFlow binding](https://github.com/dstogov/php-tensorflow).
|
||||||
|
|
||||||
### FFI Parser
|
## FFI parser
|
||||||
|
|
||||||
FFI C parser is generated using [LLK](https://github.com/dstogov/llk).
|
FFI C parser is generated using [LLK](https://github.com/dstogov/llk).
|
||||||
|
|
|
@ -1,221 +1,204 @@
|
||||||
Introduction
|
# Introduction
|
||||||
============
|
|
||||||
|
|
||||||
LiteSpeed SAPI module is a dedicated interface for PHP integration with
|
LiteSpeed SAPI module is a dedicated interface for PHP integration with
|
||||||
LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the
|
LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the FastCGI
|
||||||
FastCGI SAPI with there major enhancements: better performance, dynamic
|
SAPI with there major enhancements: better performance, dynamic spawning and PHP
|
||||||
spawning and PHP configuration modification through web server
|
configuration modification through web server configuration and `.htaccess`
|
||||||
configuration and .htaccess files.
|
files.
|
||||||
|
|
||||||
Our simple benchmark test ("hello world") shows that PHP with
|
A simple benchmark test ("hello world") shows that PHP with LiteSpeed SAPI has
|
||||||
LiteSpeed SAPI has 30% better performance over PHP with FastCGI SAPI,
|
30% better performance over PHP with FastCGI SAPI, which is nearly twice the
|
||||||
which is nearly twice the performance that Apache mod_php can deliver.
|
performance that Apache mod_php can deliver.
|
||||||
|
|
||||||
A major drawback of FastCGI PHP comparing to Apache mod_php is lacking
|
A major drawback of FastCGI PHP comparing to Apache mod_php is lacking the
|
||||||
the flexibilities in PHP configurations. PHP configurations cannot be
|
flexibilities in PHP configurations. PHP configurations cannot be changed at
|
||||||
changed at runtime via configuration files like .htaccess files or web
|
runtime via configuration files like `.htaccess` files or web server's virtual
|
||||||
server's virtual host configuration. In shared hosting environment,
|
host configuration. In shared hosting environment, each hosting account will has
|
||||||
each hosting account will has its own "open_basedir" overridden in
|
its own `open_basedir` overridden in server configuration to enhance server
|
||||||
server configuration to enhance server security when mod_php is used.
|
security when mod_php is used. Usually, FastCGI PHP is not an option in shared
|
||||||
usually, FastCGI PHP is not an option in shared hosting environment
|
hosting environment due to lacking of this flexibility. LiteSpeed SAPI is
|
||||||
due to lacking of this flexibility. LiteSpeed SAPI is carefully designed
|
carefully designed to address this issue. PHP configurations can be modified the
|
||||||
to address this issue. PHP configurations can be modified the same way
|
same way as that in mod_php with the same configuration directives.
|
||||||
as that in mod_php with the same configuration directives.
|
|
||||||
|
|
||||||
PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for
|
PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for PHP scripting
|
||||||
PHP scripting with LiteSpeed web server.
|
with LiteSpeed web server.
|
||||||
|
|
||||||
|
## Building PHP with LiteSpeed SAPI
|
||||||
|
|
||||||
Building PHP with LiteSpeed SAPI
|
You need to add `--with-litespeed` to the configure command to build PHP with
|
||||||
================================
|
LiteSpeed SAPI, all other SAPI related configure options should be removed.
|
||||||
|
|
||||||
You need to add "--with-litespeed" to the configure command to build
|
|
||||||
PHP with LiteSpeed SAPI, all other SAPI related configure options
|
|
||||||
should be removed.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
./configure --with-litespeed
|
|
||||||
make
|
|
||||||
|
|
||||||
You should find an executable called 'php' under sapi/litespeed/
|
```bash
|
||||||
directory after the compilation succeeds. Copy it to
|
./configure --with-litespeed
|
||||||
'lsws/fcgi-bin/lsphp' or wherever you prefer, if LiteSpeed web server
|
make
|
||||||
has been configured to run PHP with LiteSpeed SAPI already, you just
|
```
|
||||||
need to overwrite the old executable with this one and you are all
|
|
||||||
set.
|
|
||||||
|
|
||||||
Start PHP from command line
|
You should find an executable called `lsphp` under `sapi/litespeed/` directory
|
||||||
===========================
|
after the compilation succeeds. Copy it to `lsws/fcgi-bin/lsphp` or wherever you
|
||||||
|
prefer, if LiteSpeed web server has been configured to run PHP with LiteSpeed
|
||||||
|
SAPI already, you just need to overwrite the old executable with this one and
|
||||||
|
you are all set.
|
||||||
|
|
||||||
Usually, lsphp is managed by LiteSpeed web server in a single server
|
## Start PHP from command line
|
||||||
installation. lsphp can be used in clustered environment with one
|
|
||||||
LiteSpeed web server at the front, load balancing lsphp processes
|
|
||||||
running on multiple backend servers. In such environment, lsphp can be
|
|
||||||
start manually from command with option "-b <socket_address>", socket
|
|
||||||
address can be IPv4, IPv6 or Unix Domain Socket address.
|
|
||||||
for example:
|
|
||||||
|
|
||||||
./lsphp -b [::]:3000
|
Usually, `lsphp` is managed by LiteSpeed web server in a single server
|
||||||
|
installation. lsphp can be used in clustered environment with one LiteSpeed web
|
||||||
|
server at the front, load balancing lsphp processes running on multiple backend
|
||||||
|
servers. In such environment, lsphp can be start manually from command with
|
||||||
|
option `-b <socket_address>`, socket address can be IPv4, IPv6 or Unix Domain
|
||||||
|
Socket address.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./lsphp -b [::]:3000
|
||||||
|
```
|
||||||
|
|
||||||
have lsphp bind to port 3000 on all IPv4 and IPv6 address,
|
have lsphp bind to port 3000 on all IPv4 and IPv6 address,
|
||||||
|
|
||||||
./lsphp -b *:3000
|
```bash
|
||||||
|
./lsphp -b *:3000
|
||||||
|
```
|
||||||
|
|
||||||
have lsphp bind to port 300 on all IPv4 address.
|
have lsphp bind to port 300 on all IPv4 address,
|
||||||
|
|
||||||
./lsphp -b 192.168.0.2:3000
|
```bash
|
||||||
|
./lsphp -b 192.168.0.2:3000
|
||||||
|
```
|
||||||
|
|
||||||
have lsphp bind to address 192.168.0.2:3000.
|
have lsphp bind to address 192.168.0.2:3000,
|
||||||
|
|
||||||
./lsphp -b /tmp/lsphp_manual.sock
|
```bash
|
||||||
|
./lsphp -b /tmp/lsphp_manual.sock
|
||||||
|
```
|
||||||
|
|
||||||
have lsphp accept request on Unix domain socket "/tmp/lsphp_manual.sock"
|
have lsphp accept request on Unix domain socket `/tmp/lsphp_manual.sock`.
|
||||||
|
|
||||||
|
## Using LiteSpeed PHP with LiteSpeed Web Server
|
||||||
|
|
||||||
Using LiteSpeed PHP with LiteSpeed Web Server
|
Detailed information about how to configure LiteSpeed web server with PHP
|
||||||
=============================================
|
support is available from
|
||||||
|
[LiteSpeed website](https://www.litespeedtech.com/docs/webserver).
|
||||||
|
|
||||||
Detailed information about how to configure LiteSpeed web server with
|
Usually, PHP support has been configured out of box, you don't need to change it
|
||||||
PHP support is available from our website, at:
|
unless you want to change PHP interface from FastCGI to LiteSpeed SAPI or vice
|
||||||
|
versa.
|
||||||
https://www.litespeedtech.com/docs/webserver
|
|
||||||
|
|
||||||
Usually, PHP support has been configured out of box, you don't need to
|
|
||||||
change it unless you want to change PHP interface from FastCGI to
|
|
||||||
LiteSpeed SAPI or vice versa.
|
|
||||||
|
|
||||||
Brief instructions are as follow:
|
Brief instructions are as follow:
|
||||||
|
|
||||||
1) Login to web administration interface, go to 'Server'->'Ext App' tab,
|
1. Login to web administration interface, go to 'Server'->'Ext App' tab, add an
|
||||||
add an external application of type "LSAPI app", "Command" should be
|
external application of type "LSAPI app", "Command" should be set to a shell
|
||||||
set to a shell command that executes the PHP binary you just built.
|
command that executes the PHP binary you just built. "Instances" should be
|
||||||
"Instances" should be set to "1". Add "LSAPI_CHILDREN" environment
|
set to "1". Add "LSAPI_CHILDREN" environment variable to match the value of
|
||||||
variable to match the value of "Max Connections". More tunable
|
"Max Connections". More tunable environment variable described below can be
|
||||||
environment variable described below can be added.
|
added.
|
||||||
|
|
||||||
2) Go to 'Server'->'Script Handler' tab, add a script handler
|
2. Go to 'Server'->'Script Handler' tab, add a script handler configuration: set
|
||||||
configuration: set 'suffix' to 'php', 'Handler Type' to 'LiteSpeed
|
'suffix' to 'php', 'Handler Type' to 'LiteSpeed API', 'Handler Name' should
|
||||||
API', 'Handler Name' should be the name of external application
|
be the name of external application just defined.
|
||||||
just defined.
|
|
||||||
|
|
||||||
|
3. Click 'Apply Changes' link on the top left of the page, then click
|
||||||
3) Click 'Apply Changes' link on the top left of the page, then click
|
|
||||||
'graceful restart'. Now PHP is running with LiteSpeed SAPI.
|
'graceful restart'. Now PHP is running with LiteSpeed SAPI.
|
||||||
|
|
||||||
Tunings
|
## Tunings
|
||||||
-------
|
|
||||||
|
|
||||||
There are a few environment variables that can be tweaked to control the
|
There are a few environment variables that can be tweaked to control the
|
||||||
behavior of LSAPI application.
|
behavior of LSAPI application.
|
||||||
|
|
||||||
* LSAPI_CHILDREN or PHP_LSAPI_CHILDREN (default: 0)
|
* `LSAPI_CHILDREN` or `PHP_LSAPI_CHILDREN` (default: 0)
|
||||||
|
|
||||||
There are two ways to let PHP handle multiple requests concurrently,
|
There are two ways to let PHP handle multiple requests concurrently, Server
|
||||||
Server Managed Mode and Self Managed Mode. In Server Managed Mode,
|
Managed Mode and Self Managed Mode. In Server Managed Mode, LiteSpeed web
|
||||||
LiteSpeed web server dynamically spawn/stop PHP processes, in this mode
|
server dynamically spawn/stop PHP processes, in this mode "Instances" should
|
||||||
"Instances" should match "Max Connections" configuration for PHP
|
match "Max Connections" configuration for PHP external application. To start
|
||||||
external application. To start PHP in Self Managed Mode, "Instances"
|
PHP in Self Managed Mode, "Instances" should be set to "1", while
|
||||||
should be set to "1", while "LSAPI_CHILDREN" environment variable should
|
`LSAPI_CHILDREN` environment variable should be set to match the value of "Max
|
||||||
be set to match the value of "Max Connections" and >1. Web Server will
|
Connections" and greater than 1. Web Server will start one PHP process, this
|
||||||
start one PHP process, this process will start/stop children PHP processes
|
process will start/stop children PHP processes dynamically based on on demand.
|
||||||
dynamically based on on demand. If "LSAPI_CHILDREN" <=1, PHP will be
|
If `LSAPI_CHILDREN` less or equal to 1, PHP will be started in server managed
|
||||||
started in server managed mode.
|
mode.
|
||||||
|
|
||||||
Self Managed Mode is preferred because all PHP processes can share one
|
Self Managed Mode is preferred because all PHP processes can share one shared
|
||||||
shared memory block for the opcode cache.
|
memory block for the opcode cache.
|
||||||
|
|
||||||
Usually, there is no need to set value of LSAPI_CHILDREN over 100 in
|
Usually, there is no need to set value of `LSAPI_CHILDREN` over 100 in most
|
||||||
most server environment.
|
server environments.
|
||||||
|
|
||||||
|
* `LSAPI_AVOID_FORK` (default: 0)
|
||||||
|
|
||||||
* LSAPI_AVOID_FORK (default: 0)
|
`LSAPI_AVOID_FORK` specifies the policy of the internal process manager in
|
||||||
|
"Self Managed Mode". When set to 0, the internal process manager will stop and
|
||||||
|
start children process on demand to save system resource. This is preferred in
|
||||||
|
a shared hosting environment. When set to 1, the internal process manager will
|
||||||
|
try to avoid freqently stopping and starting children process. This might be
|
||||||
|
preferred in a dedicate hosting environment.
|
||||||
|
|
||||||
LSAPI_AVOID_FORK specifies the policy of the internal process manager in
|
* `LSAPI_EXTRA_CHILDREN` (default: 1/3 of `LSAPI_CHILDREN` or 0)
|
||||||
"Self Managed Mode". When set to 0, the internal process manager will stop
|
|
||||||
and start children process on demand to save system resource. This is
|
|
||||||
preferred in a shared hosting environment. When set to 1, the internal
|
|
||||||
process manager will try to avoid freqently stopping and starting children
|
|
||||||
process. This might be preferred in a dedicate hosting environment.
|
|
||||||
|
|
||||||
|
`LSAPI_EXTRA_CHILDREN` controls the maximum number of extra children processes
|
||||||
|
can be started when some or all existing children processes are in
|
||||||
|
malfunctioning state. Total number of children processes will be reduced to
|
||||||
|
`LSAPI_CHILDREN` level as soon as service is back to normal. When
|
||||||
|
`LSAPI_AVOID_FORK` is set to 0, the default value is 1/3 of `LSAPI_CHILDREN`,
|
||||||
|
When `LSAPI_AVOID_FORK` is set to 1, the default value is 0.
|
||||||
|
|
||||||
* LSAPI_EXTRA_CHILDREN (default: 1/3 of LSAPI_CHILDREN or 0)
|
* `LSAPI_MAX_REQS` or `PHP_LSAPI_MAX_REQUESTS` (default value: 10000)
|
||||||
|
|
||||||
LSAPI_EXTRA_CHILDREN controls the maximum number of extra children processes
|
This controls how many requests each child process will handle before it exits
|
||||||
can be started when some or all existing children processes are in
|
automatically. Several PHP functions have been identified having memory leaks.
|
||||||
malfunctioning state. Total number of children processes will be reduced to
|
This parameter can help reducing memory usage of leaky PHP functions.
|
||||||
LSAPI_CHILDREN level as soon as service is back to normal.
|
|
||||||
When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
|
|
||||||
LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value is 0.
|
|
||||||
|
|
||||||
|
* `LSAPI_MAX_IDLE` (default value: 300 seconds)
|
||||||
|
|
||||||
* LSAPI_MAX_REQS or PHP_LSAPI_MAX_REQUESTS (default value: 10000)
|
In Self Managed Mode, LSAPI_MAX_IDLE controls how long a idle child process
|
||||||
|
will wait for a new request before it exits. This option help releasing system
|
||||||
|
resources taken by idle processes.
|
||||||
|
|
||||||
This controls how many requests each child process will handle before
|
* `LSAPI_MAX_IDLE_CHILDREN` (default value: 1/3 of `LSAPI_CHILDREN` or
|
||||||
it exits automatically. Several PHP functions have been identified
|
`LSAPI_CHILDREN`)
|
||||||
having memory leaks. This parameter can help reducing memory usage
|
|
||||||
of leaky PHP functions.
|
|
||||||
|
|
||||||
|
In Self Managed Mode, `LSAI_MAX_IDLE_CHILDREN` controls how many idle children
|
||||||
|
processes are allowed. Excessive idle children processes will be killed by the
|
||||||
|
parent process immediately. When `LSAPI_AVOID_FORK` is set to 0, the default
|
||||||
|
value is 1/3 of `LSAPI_CHIDLREN`, When `LSAPI_AVOID_FORK` is set to 1, the
|
||||||
|
default value is `LSAPI_CHILDREN`.
|
||||||
|
|
||||||
* LSAPI_MAX_IDLE (default value: 300 seconds)
|
* `LSAPI_MAX_PROCESS_TIME` (default value: 300 seconds)
|
||||||
|
|
||||||
In Self Managed Mode, LSAPI_MAX_IDLE controls how long a idle child
|
In Self Managed Mode, `LSAPI_MAX_PROCESS_TIME` controls the maximum processing
|
||||||
process will wait for a new request before it exits. This option help
|
time allowed when processing a request. If a child process can not finish
|
||||||
releasing system resources taken by idle processes.
|
processing of a request in the given time period, it will be killed by the
|
||||||
|
parent process. This option can help getting rid of dead or runaway child
|
||||||
|
process.
|
||||||
|
|
||||||
|
* `LSAPI_PGRP_MAX_IDLE` (default value: FOREVER)
|
||||||
|
|
||||||
* LSAPI_MAX_IDLE_CHILDREN
|
In Self Managed Mode, `LSAPI_PGRP_MAX_IDLE` controls how long the parent
|
||||||
(default value: 1/3 of LSAPI_CHILDREN or LSAPI_CHILDREN)
|
process will wait before exiting when there is no child process. This option
|
||||||
|
helps releasing system resources taken by an idle parent process.
|
||||||
|
|
||||||
In Self Managed Mode, LSAI_MAX_IDLE_CHILDREN controls how many idle
|
* `LSAPI_PPID_NO_CHECK`
|
||||||
children processes are allowed. Excessive idle children processes
|
|
||||||
will be killed by the parent process immediately.
|
|
||||||
When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
|
|
||||||
LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value
|
|
||||||
is LSAPI_CHILDREN.
|
|
||||||
|
|
||||||
|
By default a LSAPI application check the existence of its parent process and
|
||||||
|
exits automatically if the parent process died. This is to reduce orphan
|
||||||
|
process when web server is restarted. However, it is desirable to disable this
|
||||||
|
feature, such as when a LSAPI process was started manually from command line.
|
||||||
|
`LSAPI_PPID_NO_CHECK` should be set when you want to disable the checking of
|
||||||
|
existence of parent process. When PHP is started by `-b` option, it is
|
||||||
|
disabled automatically.
|
||||||
|
|
||||||
* LSAPI_MAX_PROCESS_TIME (default value: 300 seconds)
|
## Compatibility with Apache mod_php
|
||||||
|
|
||||||
In Self Managed Mode, LSAPI_MAX_PROCESS_TIME controls the maximum
|
|
||||||
processing time allowed when processing a request. If a child process
|
|
||||||
can not finish processing of a request in the given time period, it
|
|
||||||
will be killed by the parent process. This option can help getting rid
|
|
||||||
of dead or runaway child process.
|
|
||||||
|
|
||||||
|
|
||||||
* LSAPI_PGRP_MAX_IDLE (default value: FOREVER )
|
|
||||||
|
|
||||||
In Self Managed Mode, LSAPI_PGRP_MAX_IDLE controls how long the parent
|
|
||||||
process will wait before exiting when there is no child process.
|
|
||||||
This option help releasing system resources taken by an idle parent
|
|
||||||
process.
|
|
||||||
|
|
||||||
|
|
||||||
* LSAPI_PPID_NO_CHECK
|
|
||||||
|
|
||||||
By default a LSAPI application check the existence of its parent process
|
|
||||||
and exits automatically if the parent process died. This is to reduce
|
|
||||||
orphan process when web server is restarted. However, it is desirable
|
|
||||||
to disable this feature, such as when a LSAPI process was started
|
|
||||||
manually from command line. LSAPI_PPID_NO_CHECK should be set when
|
|
||||||
you want to disable the checking of existence of parent process.
|
|
||||||
When PHP started by "-b" option, it is disabled automatically.
|
|
||||||
|
|
||||||
|
|
||||||
Compatibility with Apache mod_php
|
|
||||||
=================================
|
|
||||||
|
|
||||||
LSAPI PHP supports PHP configuration overridden via web server configuration
|
LSAPI PHP supports PHP configuration overridden via web server configuration
|
||||||
as well as .htaccess.
|
as well as `.htaccess`.
|
||||||
Since 4.0 release "apache_response_headers" function is supported.
|
|
||||||
|
|
||||||
|
Since 4.0 release `apache_response_headers` function is supported.
|
||||||
|
|
||||||
|
## Contact
|
||||||
|
|
||||||
Contact
|
For support questions, please post to the free support
|
||||||
=======
|
[forum](https://www.litespeedtech.com/support/forum/):
|
||||||
|
|
||||||
For support questions, please post to our free support forum, at:
|
|
||||||
|
|
||||||
https://www.litespeedtech.com/support/forum/
|
|
||||||
|
|
||||||
For bug report, please send bug report to bug [at] litespeedtech.com.
|
For bug report, please send bug report to bug [at] litespeedtech.com.
|
||||||
|
|
|
@ -1,81 +1,78 @@
|
||||||
The interactive PHP debugger
|
# The interactive PHP debugger
|
||||||
============================
|
|
||||||
|
|
||||||
Implemented as a SAPI module, phpdbg can exert complete control over the environment without impacting the functionality or performance of your code.
|
Implemented as a SAPI module, phpdbg can exert complete control over the
|
||||||
|
environment without impacting the functionality or performance of your code.
|
||||||
|
|
||||||
phpdbg aims to be a lightweight, powerful, easy to use debugging platform for PHP 5.4+
|
phpdbg aims to be a lightweight, powerful, easy to use debugging platform for
|
||||||
|
PHP 5.4+.
|
||||||
|
|
||||||
Features
|
## Features
|
||||||
========
|
|
||||||
|
|
||||||
- Stepthrough Debugging
|
* Stepthrough Debugging
|
||||||
- Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
|
* Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
|
||||||
- Easy Access to PHP with built-in eval()
|
* Easy Access to PHP with built-in eval()
|
||||||
- Easy Access to Currently Executing Code
|
* Easy Access to Currently Executing Code
|
||||||
- Userland API
|
* Userland API
|
||||||
- SAPI Agnostic - Easily Integrated
|
* SAPI Agnostic - Easily Integrated
|
||||||
- PHP Configuration File Support
|
* PHP Configuration File Support
|
||||||
- JIT Super Globals - Set Your Own!!
|
* JIT Super Globals - Set Your Own!!
|
||||||
- Optional readline Support - Comfortable Terminal Operation
|
* Optional readline Support - Comfortable Terminal Operation
|
||||||
- Remote Debugging Support - Bundled Java GUI
|
* Remote Debugging Support - Bundled Java GUI
|
||||||
- Easy Operation - See Help :)
|
* Easy Operation - See Help
|
||||||
|
|
||||||
Planned
|
## Planned
|
||||||
=======
|
|
||||||
|
|
||||||
- Improve Everything :)
|
* Improve Everything :)
|
||||||
|
|
||||||
Installation
|
## Installation
|
||||||
============
|
|
||||||
|
|
||||||
To install **phpdbg**, you must compile the source against your PHP installation sources, and enable the SAPI with the configure command.
|
To install **phpdbg**, you must compile the source against your PHP installation
|
||||||
|
sources, and enable the SAPI with the configure command. It is enabled by
|
||||||
|
default:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cd /usr/src/php-src/sapi
|
cd /path/to/php-src
|
||||||
git clone https://github.com/krakjoe/phpdbg
|
|
||||||
cd ../
|
|
||||||
./buildconf --force
|
./buildconf --force
|
||||||
./configure --enable-phpdbg
|
./configure
|
||||||
make -j8
|
make -j8
|
||||||
make install-phpdbg
|
./sapi/phpdbg/phpdbg --version
|
||||||
```
|
```
|
||||||
|
|
||||||
Where the source directory has been used previously to build PHP, there exists a file named *config.nice* which can be used to invoke configure with the same
|
Where the source directory has been used previously to build PHP, there exists a
|
||||||
parameters as were used by the last execution of *configure*.
|
file named `config.nice` which can be used to invoke configure with the same
|
||||||
|
parameters as were used by the last execution of `configure`.
|
||||||
|
|
||||||
**Note:** PHP must be configured with the switch --with-readline for phpdbg to support history, autocompletion, tab-listing etc.
|
**Note:** PHP must be configured with the switch `--with-readline` for phpdbg to
|
||||||
|
support history, autocompletion, tab-listing etc.
|
||||||
|
|
||||||
Command Line Options
|
## Command line options
|
||||||
====================
|
|
||||||
|
|
||||||
The following switches are implemented (just like cli SAPI):
|
The following switches are implemented (just like cli SAPI):
|
||||||
|
|
||||||
- -n ignore php ini
|
* `-n` ignore php ini
|
||||||
- -c search for php ini in path
|
* `-c` search for php ini in path
|
||||||
- -z load zend extension
|
* `-z` load zend extension
|
||||||
- -d define php ini entry
|
* `-d` define php ini entry
|
||||||
|
|
||||||
The following switches change the default behaviour of phpdbg:
|
The following switches change the default behaviour of phpdbg:
|
||||||
|
|
||||||
- -v disables quietness
|
* `-v` disables quietness
|
||||||
- -s enabled stepping
|
* `-s` enabled stepping
|
||||||
- -e sets execution context
|
* `-e` sets execution context
|
||||||
- -b boring - disables use of colour on the console
|
* `-b` boring - disables use of colour on the console
|
||||||
- -I ignore .phpdbginit (default init file)
|
* `-I` ignore .phpdbginit (default init file)
|
||||||
- -i override .phpgdbinit location (implies -I)
|
* `-i` override .phpgdbinit location (implies -I)
|
||||||
- -O set oplog output file
|
* `-O` set oplog output file
|
||||||
- -q do not print banner on startup
|
* `-q` do not print banner on startup
|
||||||
- -r jump straight to run
|
* `-r` jump straight to run
|
||||||
- -E enable step through eval()
|
* `-E` enable step through eval()
|
||||||
- -l listen ports for remote mode
|
* `-l` listen ports for remote mode
|
||||||
- -a listen address for remote mode
|
* `-a` listen address for remote mode
|
||||||
- -S override SAPI name
|
* `-S` override SAPI name
|
||||||
|
|
||||||
**Note:** Passing -rr will cause phpdbg to quit after execution, rather than returning to the console.
|
**Note:** Passing `-rr` will cause phpdbg to quit after execution, rather than
|
||||||
|
returning to the console.
|
||||||
|
|
||||||
Getting Started
|
## Getting started
|
||||||
===============
|
|
||||||
|
|
||||||
See the website for tutorials/documentation
|
See the [website](https://phpdbg.room11.org) for tutorials/documentation.
|
||||||
|
|
||||||
https://phpdbg.room11.org
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue