Merge branch 'PHP-7.4'

* PHP-7.4:
  [ci skip] Fix CS in Markdown files
This commit is contained in:
Peter Kokot 2019-04-21 15:39:22 +02:00
commit cbca8a8df4
11 changed files with 1004 additions and 904 deletions

View file

@ -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

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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;
} }
```

View file

@ -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.

View file

@ -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.

View file

@ -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`.

View file

@ -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).

View file

@ -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.

View file

@ -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