[ci skip] Fix CS in Markdown files

Checked and quickfixed with Markdown linter
- 80 columns line width (~)
- code highlighting
- ...

Some most obvious outdated content updated a bit more.
This commit is contained in:
Peter Kokot 2019-04-21 15:33:20 +02:00
parent 80f3c69ae9
commit 1c94aac89e
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.*
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
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
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
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
@ -72,7 +78,8 @@ After a successful build (and test), PHP may be installed with:
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

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,
and can be quite difficult to prevent. Whenever you accept user data
and somehow display this data back to users, you are likely vulnerable
to XSS hacks.
XSS (Cross Site Scripting) hacks are becoming more and more prevalent, and can
be quite difficult to prevent. Whenever you accept user data and somehow display
this data back to users, you are likely vulnerable to XSS hacks.
The Input Filter support in PHP 5 is aimed at providing the framework
through which a company-wide or site-wide security policy can be
enforced. It is implemented as a SAPI hook and is called from the
treat_data and post handler functions. To implement your own security
policy you will need to write a standard PHP extension. There is also
a powerful standard implementation in ext/filter that should suit most
peoples' needs. However, if you want to implement your own security
policy, read on.
The Input Filter support in PHP is aimed at providing the framework through
which a company-wide or site-wide security policy can be enforced. It is
implemented as a SAPI hook and is called from the `treat_data` and post handler
functions. To implement your own security policy you will need to write a
standard PHP extension. There is also a powerful standard implementation in
`ext/filter` that should suit most peoples' needs. However, if you want to
implement your own security policy, read on.
A simple implementation might look like the following. This stores the
original raw user data and adds a my_get_raw() function while the normal
$_POST, $_GET and $_COOKIE arrays are only populated with stripped
data. In this simple example all I am doing is calling strip_tags() on
the data.
A simple implementation might look like the following. This stores the original
raw user data and adds a `my_get_raw()` function while the normal `$_POST`,
`$_GET` and `$_COOKIE` arrays are only populated with stripped data. In this
simple example all I am doing is calling `strip_tags()` on the data.
```
```c
ZEND_BEGIN_MODULE_GLOBALS(my_input_filter)
zval *post_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
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.
Having said that, here are the organizational rules:
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.
3. Make sure you pick the right mailinglist for your posting. Please review
the descriptions on the mailinglist overview page
(http://www.php.net/mailing-lists.php). When in doubt ask a friend or
someone you trust on IRC.
the descriptions on the
[mailinglist overview page](https://www.php.net/mailing-lists.php). When
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
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.
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,

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,
and there's an API call for every output handler op.
Everything now resides beneath the php_output namespace, and there's an API call
for every output handler op.
Checking output control layers status:
// Using OG()
php_output_get_status();
Checking output control layers status:
// Using OG()
php_output_get_status();
Starting the default output handler:
// php_start_ob_buffer(NULL, 0, 1);
php_output_start_default();
Starting the default output handler:
// php_start_ob_buffer(NULL, 0, 1);
php_output_start_default();
Starting an user handler by zval:
// php_start_ob_buffer(zhandler, chunk_size, erase);
php_output_start_user(zhandler, chunk_size, flags);
Starting an user handler by zval:
// php_start_ob_buffer(zhandler, chunk_size, erase);
php_output_start_user(zhandler, chunk_size, flags);
Starting an internal handler without context:
// 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);
Starting an internal handler without context:
// 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);
Starting an internal handler with context:
// not possible with old API
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);
php_output_handler_set_context(h, my_context, my_context_dtor);
php_output_handler_start(h);
Starting an internal handler with context:
// not possible with old API
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);
php_output_handler_set_context(h, my_context, my_context_dtor);
php_output_handler_start(h);
Testing whether a certain output handler has already been started:
// php_ob_handler_used("output handler name");
php_output_handler_started(handler_name, handler_name_len);
Testing whether a certain output handler has already been started:
// php_ob_handler_used("output handler name");
php_output_handler_started(handler_name, handler_name_len);
Flushing one output buffer:
// php_end_ob_buffer(1, 1);
php_output_flush();
Flushing one output buffer:
// php_end_ob_buffer(1, 1);
php_output_flush();
Flushing all output buffers:
// not possible with old API
php_output_flush_all();
Flushing all output buffers:
// not possible with old API
php_output_flush_all();
Cleaning one output buffer:
// php_ob_end_buffer(0, 1);
php_output_clean();
Cleaning one output buffer:
// php_ob_end_buffer(0, 1);
php_output_clean();
Cleaning all output buffers:
// not possible with old API
php_output_clean_all();
Cleaning all output buffers:
// not possible with old API
php_output_clean_all();
Discarding one output buffer:
// php_ob_end_buffer(0, 0);
php_output_discard();
Discarding one output buffer:
// php_ob_end_buffer(0, 0);
php_output_discard();
Discarding all output buffers:
// php_ob_end_buffers(0);
php_output_discard_all();
Discarding all output buffers:
// php_ob_end_buffers(0);
php_output_discard_all();
Stopping (and dropping) one output buffer:
// php_ob_end_buffer(1, 0)
php_output_end();
Stopping (and dropping) one output buffer:
// php_ob_end_buffer(1, 0)
php_output_end();
Stopping (and dropping) all output buffers:
// php_ob_end_buffers(1, 0);
php_output_end_all();
Stopping (and dropping) all output buffers:
// php_ob_end_buffers(1, 0);
php_output_end_all();
Retrieving output buffers contents:
// php_ob_get_buffer(zstring);
php_output_get_contents(zstring);
Retrieving output buffers contents:
// php_ob_get_buffer(zstring);
php_output_get_contents(zstring);
Retrieving output buffers length:
// php_ob_get_length(zlength);
php_output_get_length(zlength);
Retrieving output buffers length:
// php_ob_get_length(zlength);
php_output_get_length(zlength);
Retrieving output buffering level:
// OG(nesting_level);
php_output_get_level();
Retrieving output buffering level:
// OG(nesting_level);
php_output_get_level();
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_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len);
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_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:
// 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);
Registering a conflict checking function, which will be checked prior starting the handler:
// 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);
Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
// 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);
Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
// 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);
Facilitating a context from within an output handler callable with ob_start():
// not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
Facilitating a context from within an output handler callable with ob_start():
// not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
Disabling of the output handler by itself:
//not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
Disabling of the output handler by itself:
//not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
Marking an output handler immutable by itself because of irreversibility of its operation:
// not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
Marking an output handler immutable by itself because of irreversibility of its operation:
// not possible with old API
php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
Restarting the output handler because of a CLEAN operation:
// not possible with old API
if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
Restarting the output handler because of a CLEAN operation:
// not possible with old API
if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
Recognizing by the output handler itself if it gets discarded:
// not possible with old API
if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
Recognizing by the output handler itself if it gets discarded:
// not possible with old API
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
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:
PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
pass a void*** pointer as second arg to receive the address of a pointer
pointer to the opaque field of the output handler context
PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
pass a int* pointer as second arg to receive the flags set for the output handler
PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
pass a int* pointer as second arg to receive the level of this output handler
(starts with 0)
PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
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
PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
pass a void*** pointer as second arg to receive the address of a pointer
pointer to the opaque field of the output handler context
PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
pass a int* pointer as second arg to receive the flags set for the output handler
PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
pass a int* pointer as second arg to receive the level of this output handler
(starts with 0)
PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
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 behaviour be adjusted?
END
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
behaviour be adjusted?

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
with the zend_parse_parameters() function described below.
## Parameter parsing functions
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
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
----------
```c
/* Implemented. */
int zend_parse_parameters(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
passed to the extension function, the type specifier string, and the
list of pointers to variables to store the results in. The _ex() version
also takes 'flags' argument -- current only ZEND_PARSE_PARAMS_QUIET can
be used as 'flags' to specify that the function should operate quietly
and not output any error messages.
The `zend_parse_parameters()` function takes the number of parameters passed to
the extension function, the type specifier string, and the list of pointers to
variables to store the results in. The _ex() version also takes 'flags' argument
-- current only `ZEND_PARSE_PARAMS_QUIET` can be used as 'flags' to specify that
the function should operate quietly 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
resources cannot be auto-converted.
The auto-conversions are performed as necessary. Arrays, objects, and resources
cannot be auto-converted.
PHP 5.3 includes a new function (actually implemented as macro):
```c
int zend_parse_parameters_none();
```
This returns SUCCESS if no argument has been passed to the function,
FAILURE otherwise.
This returns `SUCCESS` if no argument has been passed to the function, `FAILURE`
otherwise.
PHP 5.5 includes a new function:
```c
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
(passed with double indirection). The passed zval may be changed in place as
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 parameter is non optional and untouched if optional and the
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.
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
parameter is non optional and untouched if optional and the 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*)
A - array or object (zval*)
b - boolean (zend_bool)
C - class (zend_class_entry*)
d - double (double)
f - function or array containing php method call info (returned as
zend_fcall_info and zend_fcall_info_cache)
h - array (returned as HashTable*)
H - array or HASH_OF(object) (returned as HashTable*)
l - long (zend_long)
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (zend_long, ZEND_LONG_MAX/ZEND_LONG_MIN)
o - object of any type (zval*)
O - object of specific type given by class entry (zval*, zend_class_entry)
p - valid path (string without null bytes in the middle) and its length (char*, size_t)
P - valid path (string without null bytes in the middle) as zend_string (zend_string*)
r - resource (zval*)
s - string (with possible null bytes) and its length (char*, size_t)
S - string (with possible null bytes) as zend_string (zend_string*)
z - the actual zval (zval*)
* - variable arguments list (0 or more)
+ - variable arguments list (1 or more)
```txt
a - array (zval*)
A - array or object (zval*)
b - boolean (zend_bool)
C - class (zend_class_entry*)
d - double (double)
f - function or array containing php method call info (returned as
zend_fcall_info and zend_fcall_info_cache)
h - array (returned as HashTable*)
H - array or HASH_OF(object) (returned as HashTable*)
l - long (zend_long)
L - long, limits out-of-range numbers to LONG_MAX/LONG_MIN (zend_long, ZEND_LONG_MAX/ZEND_LONG_MIN)
o - object of any type (zval*)
O - object of specific type given by class entry (zval*, zend_class_entry)
p - valid path (string without null bytes in the middle) and its length (char*, size_t)
P - valid path (string without null bytes in the middle) as zend_string (zend_string*)
r - resource (zval*)
s - string (with possible null bytes) and its length (char*, size_t)
S - string (with possible null bytes) as zend_string (zend_string*)
z - the actual zval (zval*)
* - variable arguments list (0 or more)
+ - variable arguments list (1 or more)
```
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.
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.
Note on 64bit compatibility
---------------------------
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"
and size_t to strings length (i.e. for "s" you need to pass char * and size_t),
not the other way round!
## Note on 64bit compatibility
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_long`s to "l"
and `size_t` to strings length (i.e. for "s" you need to pass char `*` and
`size_t`), not the other way round!
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)
int num; /* XXX THIS IS WRONG!! Use zend_long instead. */
zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num)
* 1
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):
```c
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 */
zend_long l;
char *s;
@ -138,7 +145,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsz",
return;
}
/* Gets an object of class specified by my_ce, and an optional double. */
zval *obj;
double d = 0.5;
@ -148,7 +154,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|d",
return;
}
/* Gets an object or null, and an array.
If null is passed for object, obj will be set to NULL. */
zval *obj;
@ -158,7 +163,6 @@ if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a",
return;
}
/* Gets a separated array which can also be null. */
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!",
@ -171,7 +175,7 @@ zend_long l1, l2, l3;
char *s;
/*
* 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
* properly, leading to random crashes on platforms like
* Tru64 or Linux/Alpha.
@ -190,13 +194,11 @@ if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(),
return;
}
/* Function that accepts only varargs (0 or more) */
int i, num_varargs;
zval *varargs = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &varargs, &num_varargs) == FAILURE) {
return;
}
@ -209,7 +211,6 @@ if (varargs) {
efree(varargs);
}
/* Function that accepts a string, followed by varargs (1 or more) */
char *str;
@ -243,3 +244,4 @@ for (i = 0; i < num_varargs; i++) {
if (zend_parse_parameters_none() == FAILURE) {
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
the PHP source. To create such an extension, two things are
required:
A self-contained extension can be distributed independently of the PHP source.
To create such an extension, two things are required:
- Configuration file (config.m4)
- Source code for your module
* Configuration file (config.m4)
* Source code for your module
We will describe now how to create these and how to put things
together.
We will describe now how to create these and how to put things together.
## PREPARING YOUR SYSTEM
## Prepairing your system
While the result will run on any system, a developer's setup needs these
tools:
While the result will run on any system, a developer's setup needs these tools:
GNU autoconf
GNU libtool
GNU m4
* GNU autoconf
* GNU libtool
* GNU m4
All of these are available from
All of these are available from
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
extension, we will convert an embedded extension into a
self-contained one. Install PHP and execute the following
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.
Just to show you how easy it is to create a self-contained extension, we will
convert an embedded extension into a self-contained one. Install PHP and execute
the following commands.
```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],
[whether to enable foobar],
[AS_HELP_STRING([--enable-foobar],
@ -89,81 +88,86 @@ if test "$PHP_FOOBAR" != "no"; then
fi
```
PHP_ARG_ENABLE will automatically set the correct variables, so
that the extension will be enabled by PHP_NEW_EXTENSION in shared mode.
`PHP_ARG_ENABLE` will automatically set the correct variables, so that the
extension will be enabled by `PHP_NEW_EXTENSION` in shared mode.
The first argument of PHP_NEW_EXTENSION describes the name of the
extension. The second names the source-code files. The third passes
$ext_shared which is set by PHP_ARG_ENABLE/WITH to PHP_NEW_EXTENSION.
The first argument of `PHP_NEW_EXTENSION` describes the name of the extension.
The second names the source-code files. The third passes `$ext_shared` which is
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
plan to distribute your module with PHP, these facilities allow you
to integrate your module easily into the main PHP module framework.
Please use always `PHP_ARG_ENABLE` or `PHP_ARG_WITH`. Even if you do not plan to
distribute your module with PHP, these facilities allow you to integrate your
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
in PHP for you and also writing basic function definitions and C code for
handling arguments passed to your functions. See `./ext/ext_skel.php --help`
for further information.
`ext_skel.php` can be of great help when creating the common code for all
modules in PHP for you and also writing basic function definitions and C code
for handling arguments passed to your functions. See `./ext/ext_skel.php --help`
for further information.
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.
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.
## 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
(this is installed during make install by PHP 4.0).
For example, if you configured PHP with `--prefix=/php`, you would run
For example, if you configured PHP with --prefix=/php, you would run
$ /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
```bash
/php/bin/phpize
```
## 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
points to be regarded.
And that's it. You now have a self-contained extension.
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
extension version.
## Adding shared module support to a module
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.
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
code; we don't want them - they are ugly and cumbersome and force you to
special case sockets and files every time you need to work with a "user-level"
PHP file pointer.
code; we don't want them - they are ugly and cumbersome and force you to special
case sockets and files every time you need to work with a "user-level" PHP file
pointer.
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
based data sources.
Using Streams
=============
Streams use a php_stream* parameter just as ANSI stdio (fread etc.) use a
FILE* parameter.
## Using streams
Streams use a `php_stream*` parameter just as ANSI stdio (fread etc.) use a
`FILE*` parameter.
The main functions are:
```c
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
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 off_t php_stream_tell(php_stream * stream);
PHPAPI int php_stream_lock(php_stream * stream, int mode);
```
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:
```c
PHPAPI php_stream *php_stream_open_wrapper(const char *path, const char *mode,
int options, char **opened_path);
```
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,
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.
* `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, 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
streams there are a range of functions to do this defined in php_streams.h.
A brief list of the most commonly used functions:
streams there are a range of functions to do this defined in `php_streams.h`. A
brief list of the most commonly used functions:
```c
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);
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,
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);
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,
int socktype, int timeout, int persistent);
Open a connection to a host and return a stream.
int socktype, int timeout, int persistent);
/* Open a connection to a host and return a stream. */
PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent,
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,
php_stream *dest, size_t maxlen);
```
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
number of bytes to copy.
This function will try to use mmap where available to make the copying more
efficient.
`PHP_STREAM_COPY_ALL` as the maxlen parameter, otherwise maxlen indicates the
number of bytes to copy. This function will try to use mmap where available to
make the copying more efficient.
If you want to read the contents of a stream into an allocated memory buffer,
you should use:
```c
PHPAPI size_t php_stream_copy_to_mem(php_stream *src, char **buf,
size_t maxlen, int persistent);
```
This function will set buf to the address of the buffer that it allocated,
which will be maxlen bytes in length, or will be the entire length of the
data remaining on the stream if you set maxlen to PHP_STREAM_COPY_ALL.
The buffer is allocated using pemalloc(); you need to call pefree() to
release the memory when you are done.
As with copy_to_stream, this function will try use mmap where it can.
This function will set buf to the address of the buffer that it allocated, which
will be maxlen bytes in length, or will be the entire length of the data
remaining on the stream if you set maxlen to `PHP_STREAM_COPY_ALL`. The buffer
is allocated using `pemalloc()`. You need to call `pefree()` to release the
memory when you are done. As with `copy_to_stream`, this function will try use
mmap where it can.
If you have an existing stream and need to be able to seek() it, you
can use this function to copy the contents into a new stream that can
be seek()ed:
If you have an existing stream and need to be able to `seek()` it, you can use
this function to copy the contents into a new stream that can be `seek()ed`:
```c
PHPAPI int php_stream_make_seekable(php_stream *origstream, php_stream **newstream);
```
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
if the function succeeds.
When you have finished, remember to close the stream.
```c
#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 */
```
NOTE: If you only need to seek forward, there is no need to call this
function, as the php_stream_seek can emulate forward seeking when the
whence parameter is SEEK_CUR.
`make_seekable` will always set newstream to be the stream that is valid if the
function succeeds. When you have finished, remember to close the stream.
NOTE: Writing to the stream may not affect the original source, so it
only makes sense to use this for read-only use.
NOTE: If you only need to seek forward, there is no need to call this function,
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
until the whole contents have been downloaded.
NOTE: Writing to the stream may not affect the original source, so it only makes
sense to use this for read-only use.
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 the origstream is network based, this function will block until the
whole contents have been downloaded.
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
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);
```
This function will return either 1 (success) or 0 (failure) indicating whether or
not a lock can be set on this stream. Typically you can only set locks on stdio streams.
This function will return either 1 (success) or 0 (failure) indicating whether
or not a lock can be set on this stream. Typically you can only set locks on
stdio 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:
## 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:
```c
FILE * fp;
php_stream * stream; /* already opened */
if (php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void*)&fp, REPORT_ERRORS) == FAILURE) {
RETURN_FALSE;
}
```
The prototype is:
PHPAPI int php_stream_cast(php_stream * stream, int castas, void ** ret, int
show_err);
```c
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
appropriate error message of type E_WARNING if the cast fails.
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.
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_FD - a generic file descriptor
PHP_STREAM_AS_SOCKETD - a socket descriptor
```
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
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
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
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.
In situations where this is not desirable, you should query the stream
to see if it naturally supports FILE *. You can use this code snippet
for this purpose:
In situations where this is not desirable, you should query the stream to see if
it naturally supports `FILE *`. You can use this code snippet for this purpose:
if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
/* can safely cast to FILE* with no adverse side effects */
}
```c
if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) {
/* can safely cast to FILE* with no adverse side effects */
}
```
You can use:
```c
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 check if a stream is a socket you might use:
to find out if a stream can be cast, without actually performing the cast, so to
check if a stream is a socket you might use:
```c
if (php_stream_can_cast(stream, PHP_STREAM_AS_SOCKETD) == SUCCESS) {
/* it can be a socket */
}
```
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
can_cast tells you if the stream can be forced into the form you request.
The former doesn't change anything, while the later *might* change some
state in the stream.
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
`can_cast` tells you if the stream can be forced into the form you request. The
former doesn't change anything, while the later *might* change some state in the
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
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.
The php_streams ops struct consists of pointers to methods that implement
read, write, close, flush, seek, gets and cast operations. Of these, an
implementation need only implement write, read, close and flush. The gets
method is intended to be used for streams if there is an underlying method
that can efficiently behave as fgets. The ops struct also contains a label
for the implementation that will be used when printing error messages - the
stdio implementation has a label of "STDIO" for example.
The `php_streams` ops struct consists of pointers to methods that implement
read, write, close, flush, seek, gets and cast operations. Of these, an
implementation need only implement write, read, close and flush. The gets method
is intended to be used for streams if there is an underlying method that can
efficiently behave as fgets. The ops struct also contains a label for the
implementation that will be used when printing error messages - the stdio
implementation has a label of `STDIO` for example.
The idea is that a stream implementation defines a php_stream_ops struct, and
associates it with a php_stream using php_stream_alloc.
The idea is that a stream implementation defines a `php_stream_ops` struct, and
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)
{
FILE * fp = fopen(filename, mode);
@ -255,62 +276,64 @@ PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode)
}
return NULL;
}
```
php_stream_stdio_ops is a php_stream_ops structure that can be used to handle
FILE* based streams.
`php_stream_stdio_ops` is a `php_stream_ops` structure that can be used to
handle `FILE*` based streams.
A socket based stream would use code similar to that above to create a stream
to be passed back to fopen_wrapper (or it's yet to be implemented successor).
A socket based stream would use code similar to that above to create a stream to
be passed back to fopen_wrapper (or it's yet to be implemented successor).
The prototype for php_stream_alloc is this:
```c
PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
size_t bufsize, int persistent, const char * mode)
```
ops is a pointer to the implementation,
abstract holds implementation specific data that is relevant to this instance
of the stream,
bufsize is the size of the buffer to use - if 0, then buffering at the stream
level will be disabled (recommended for underlying sources that implement
their own buffering - such a FILE*),
persistent controls how the memory is to be allocated - persistently so that
it lasts across requests, or non-persistently so that it is freed at the end
of a request (it uses pemalloc),
mode is the stdio-like mode of operation - php streams places no real meaning
in the mode parameter, except that it checks for a 'w' in the string when
attempting to write (this may change).
* `ops` is a pointer to the implementation,
* `abstract` holds implementation specific data that is relevant to this
instance of the stream,
* `bufsize` is the size of the buffer to use - if 0, then buffering at the
stream
* `level` will be disabled (recommended for underlying sources that implement
their own buffering - such a `FILE*`)
* `persistent` controls how the memory is to be allocated - persistently so that
it lasts across requests, or non-persistently so that it is freed at the end
of a request (it uses pemalloc),
* `mode` is the stdio-like mode of operation - php streams places no real
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
into a FILE*, so it should be compatible with the mode parameter of fopen().
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()`.
Writing your own stream implementation
======================================
## Writing your own stream implementation
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
RULE #1: when writing your own streams: make sure you have configured PHP with
--enable-debug.
I've taken some great pains to hook into the Zend memory manager to help track
down allocation problems. It will also help you spot incorrect use of the
STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for function
definitions.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* **RULE #1**: when writing your own streams: make sure you have configured PHP
with `--enable-debug`.
Some great great pains have been taken to hook into the Zend memory manager to
help track down allocation problems. It will also help you spot incorrect use
of the STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for
function definitions.
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
be more up to date than these docs :-)
* 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 be more
up to date than these docs :-)
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
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.
If you need to store more than a single field of data, define a structure to
hold it, allocate it (use pemalloc with the persistent flag set
appropriately), and use the abstract pointer to refer to it.
The stream has a field called abstract that you can use to hold this data. If
you need to store more than a single field of data, define a structure to hold
it, allocate it (use pemalloc with the persistent flag set appropriately), and
use the abstract pointer to refer to it.
For structured state you might have this:
```c
struct my_state {
MYSQL conn;
MYSQL_RES * result;
@ -327,6 +350,7 @@ state->result = mysql_use_result(&state->conn);
stream = php_stream_alloc(&my_ops, state, 0, persistent, "r");
/* now stream->abstract == state */
```
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
@ -334,6 +358,7 @@ example).
For example, for reading from this weird MySQL stream:
```c
static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count)
{
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,
so I won't even go in to how to do that here */
}
```
Implement the other operations - remember that write, read, close and flush
are all mandatory. The rest are optional. Declare your stream ops struct:
Implement the other operations - remember that write, read, close and flush are
all mandatory. The rest are optional. Declare your stream ops struct:
```c
php_stream_ops my_ops = {
php_mysqlop_write, php_mysqlop_read, php_mysqlop_close,
php_mysqlop_flush, NULL, NULL, NULL,
"Strange MySQL example"
}
```
That's it!
Take a look at the STDIO implementation in streams.c for more information
about how these operations work.
Take a look at the STDIO implementation in streams.c for more information about
how these operations work.
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
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.
You may read the stream->persistent field to determine if your struct was
allocated in persistent mode or not.
and free the resources you allocated for the abstract field. In the case of 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. You may
read the stream->persistent field to determine if your struct was allocated in
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
- not-really-portable Makefile includes have been eliminated
- supports separate build directories without VPATH by using
explicit rules only
- does not waste disk-space/CPU-time for building temporary libraries
=> especially noticeable on slower systems
- slow recursive make replaced with one global Makefile
- eases integration of proper dependencies
- adds PHP_DEFINE(what[, value]) which creates a single include-file
per what. This will allow more fine-grained dependencies.
- abandoning the "one library per directory" concept
- improved integration of the CLI
- several new targets
build-modules: builds and copies dynamic modules into modules/
install-cli: installs the CLI only, so that the install-sapi
target does only what its name says
- finally abandoned automake
- changed some configure-time constructs to run at buildconf-time
- upgraded shtool to 1.5.4
- removed $(moduledir) (use EXTENSION_DIR)
* supports Makefile.ins during transition phase
* not-really-portable Makefile includes have been eliminated
* supports separate build directories without VPATH by using explicit rules only
* does not waste disk-space/CPU-time for building temporary libraries =>
especially noticeable on slower systems
* slow recursive make replaced with one global Makefile
* eases integration of proper dependencies
* adds PHP_DEFINE(what[, value]) which creates a single include-file per what.
This will allow more fine-grained dependencies.
* abandoning the "one library per directory" concept
* improved integration of the CLI
* several new targets:
* `build-modules`: builds and copies dynamic modules into `modules/`
* `install-cli`: installs the CLI only, so that the install-sapi target does
only what its name says
* finally abandoned automake
* changed some configure-time constructs to run at buildconf-time
* upgraded shtool to 1.5.4
* 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
for addressing the portability concerns and improving the chance
that your build is correct (how often have you been told to
"make clean"? When this is done, you won't need to anymore).
It became more and more apparent that there is a severe need for addressing the
portability concerns and improving the chance that your build is correct (how
often have you been told to `make clean`? When this is done, you won't need to
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
system will be faster, look better and work smarter.
You, as a user of PHP, will notice no changes. Of course, the build system will
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
are specified in the config.m4 now using the following macro:
Makefile.ins are abandoned. The files which are to be compiled are specified in
the `config.m4` now using the following macro:
```m4
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
modules, two in C and one in C++. And, depending on the user's wishes,
the extension will even be built as a dynamic module.
E.g. this enables the extension foo which consists of three source-code modules,
two in C and one in C++. And, depending on the user's wishes, the extension will
even be built as a dynamic module.
The full syntax:
```m4
PHP_NEW_EXTENSION(extname, sources [, shared [,sapi_class[, extra-cflags]]])
```
Please have a look at acinclude.m4 for the gory details and meanings
of the other parameters.
Please have a look at `acinclude.m4` for the gory details and meanings of the
other parameters.
And that's basically it for the extension side.
If you previously built sub-libraries for this module, add
the source-code files here as well. If you need to specify
separate include directories, do it this way:
If you previously built sub-libraries for this module, add the source-code files
here as well. If you need to specify separate include directories, do it this
way:
```m4
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
extension source directory and compiles all three files with the
special include directive (@ext_srcdir@ is automatically replaced).
E.g. this builds the three files which are located relative to the extension
source directory and compiles all three files with the special include directive
(`@ext_srcdir@` is automatically replaced).
Now, you need to tell the build system that you want to build files
in a directory called $ext_builddir/lib:
Now, you need to tell the build system that you want to build files in a
directory called `$ext_builddir/lib`:
```m4
PHP_ADD_BUILD_DIR($ext_builddir/lib)
```
Make sure to call this after PHP_NEW_EXTENSION, because $ext_builddir
is only set by the latter.
Make sure to call this after `PHP_NEW_EXTENSION`, because `$ext_builddir` is
only set by the latter.
If you have a complex extension, you might to need add special
Make rules. You can do this by calling PHP_ADD_MAKEFILE_FRAGMENT
in your config.m4 after PHP_NEW_EXTENSION.
If you have a complex extension, you might to need add special Make rules. You
can do this by calling `PHP_ADD_MAKEFILE_FRAGMENT` in your `config.m4` after
`PHP_NEW_EXTENSION`.
This will read a file in the source-dir of your extension called
Makefile.frag. In this file, $(builddir) and $(srcdir) will be
replaced by the values which are correct for your extension
and which are again determined by the PHP_NEW_EXTENSION macro.
`Makefile.frag`. In this file, `$(builddir)` and `$(srcdir)` will be replaced by
the values which are correct for your extension and which are again determined
by the `PHP_NEW_EXTENSION` macro.
Make sure to prefix *all* relative paths correctly with either
$(builddir) or $(srcdir). Because the build system does not
change the working directory anymore, we must use either
absolute paths or relative ones to the top build-directory.
Correct prefixing ensures that.
Make sure to prefix *all* relative paths correctly with either `$(builddir)` or
`$(srcdir)`. Because the build system does not change the working directory
anymore, we must use either absolute paths or relative ones to the top
build-directory. 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)
```
I.e. specify the source-code files as above and also pass the
information regarding how PHP is supposed to be built (shared
module, program, etc).
I.e. specify the source-code files as above and also pass the information
regarding how PHP is supposed to be built (shared module, program, etc).
For example for APXS:
```m4
PHP_SELECT_SAPI(apache, shared, sapi_apache.c mod_php7.c php_apache.c)
```
## General info
The foundation for the new system is the flexible handling of
sources and their contexts. With the help of macros you
can define special flags for each source-file, where it is
located, in which target context it can work, etc.
The foundation for the new system is the flexible handling of sources and their
contexts. With the help of macros you can define special flags for each
source-file, where it is located, in which target context it can work, etc.
Have a look at the well documented macros
PHP_ADD_SOURCES(_X) in acinclude.m4.
Have a look at the well documented macros `PHP_ADD_SOURCES(_X)` in
`acinclude.m4`.

View File

@ -1,6 +1,9 @@
# 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
@ -22,7 +25,7 @@ $libc = FFI::cdef("
int tz_dsttime;
};
int gettimeofday(struct timeval *tv, struct timezone *tz);
int gettimeofday(struct timeval *tv, struct timezone *tz);
", "libc.so.6");
$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!
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)
@ -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
$p = FFI::new("struct {int x,y;} [2]");
$p[0]->x = 5;
@ -64,7 +76,7 @@ $p[1]->y = 10;
var_dump($p);
```
```
```txt
object(FFI\CData:<struct>[2])#1 (2) {
[0]=>
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
$libc = FFI::cdef("const char * getenv(const char *);", "libc.so.6");
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
$libc = FFI::cdef("extern int errno;", "libc.so.6");
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 following example creates two dimensional array of integers.
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.
```php
$p = FFI::new("int[2][2]");
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
$p = FFI::new("int[2]");
$p[0] = 1;
$p[1] = 2;
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
$pp = FFI::new("struct {int x,y;}[2]");
foreach($pp as $n => &$p) {
$p->x = $p->y = $n;
$p->x = $p->y = $n;
}
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"
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.
``` php
```php
$ffi = FFI::load(__DIR__ . "/printf.h");
$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_SCOPE "libc"
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
```
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->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.
@ -261,7 +323,8 @@ unset($p1); // $p1 is deallocated ($p2 became 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
$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
```
##### 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
make
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).

View File

@ -1,221 +1,204 @@
Introduction
============
# Introduction
LiteSpeed SAPI module is a dedicated interface for PHP integration with
LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the
FastCGI SAPI with there major enhancements: better performance, dynamic
spawning and PHP configuration modification through web server
configuration and .htaccess files.
LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the FastCGI
SAPI with there major enhancements: better performance, dynamic spawning and PHP
configuration modification through web server configuration and `.htaccess`
files.
Our simple benchmark test ("hello world") shows that PHP with
LiteSpeed SAPI has 30% better performance over PHP with FastCGI SAPI,
which is nearly twice the performance that Apache mod_php can deliver.
A simple benchmark test ("hello world") shows that PHP with LiteSpeed SAPI has
30% better performance over PHP with FastCGI SAPI, which is nearly twice the
performance that Apache mod_php can deliver.
A major drawback of FastCGI PHP comparing to Apache mod_php is lacking
the flexibilities in PHP configurations. PHP configurations cannot be
changed at runtime via configuration files like .htaccess files or web
server's virtual host configuration. In shared hosting environment,
each hosting account will has its own "open_basedir" overridden in
server configuration to enhance server security when mod_php is used.
usually, FastCGI PHP is not an option in shared hosting environment
due to lacking of this flexibility. LiteSpeed SAPI is carefully designed
to address this issue. PHP configurations can be modified the same way
as that in mod_php with the same configuration directives.
A major drawback of FastCGI PHP comparing to Apache mod_php is lacking the
flexibilities in PHP configurations. PHP configurations cannot be changed at
runtime via configuration files like `.htaccess` files or web server's virtual
host configuration. In shared hosting environment, each hosting account will has
its own `open_basedir` overridden in server configuration to enhance server
security when mod_php is used. Usually, FastCGI PHP is not an option in shared
hosting environment due to lacking of this flexibility. LiteSpeed SAPI is
carefully designed to address this issue. PHP configurations can be modified the
same way as that in mod_php with the same configuration directives.
PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for
PHP scripting with LiteSpeed web server.
PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for PHP scripting
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:
./configure --with-litespeed
make
You should find an executable called 'php' 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.
```bash
./configure --with-litespeed
make
```
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
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:
## Start PHP from command line
./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,
./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
PHP support is available from our website, at:
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.
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:
1) Login to web administration interface, go to 'Server'->'Ext App' tab,
add an external application of type "LSAPI app", "Command" should be
set to a shell command that executes the PHP binary you just built.
"Instances" should be set to "1". Add "LSAPI_CHILDREN" environment
variable to match the value of "Max Connections". More tunable
environment variable described below can be added.
1. Login to web administration interface, go to 'Server'->'Ext App' tab, add an
external application of type "LSAPI app", "Command" should be set to a shell
command that executes the PHP binary you just built. "Instances" should be
set to "1". Add "LSAPI_CHILDREN" environment variable to match the value of
"Max Connections". More tunable environment variable described below can be
added.
2) Go to 'Server'->'Script Handler' tab, add a script handler
configuration: set 'suffix' to 'php', 'Handler Type' to 'LiteSpeed
API', 'Handler Name' should be the name of external application
just defined.
2. Go to 'Server'->'Script Handler' tab, add a script handler configuration: set
'suffix' to 'php', 'Handler Type' to 'LiteSpeed API', 'Handler Name' should
be the name of external application 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.
Tunings
-------
## Tunings
There are a few environment variables that can be tweaked to control the
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,
Server Managed Mode and Self Managed Mode. In Server Managed Mode,
LiteSpeed web server dynamically spawn/stop PHP processes, in this mode
"Instances" should match "Max Connections" configuration for PHP
external application. To start PHP in Self Managed Mode, "Instances"
should be set to "1", while "LSAPI_CHILDREN" environment variable should
be set to match the value of "Max Connections" and >1. Web Server will
start one PHP process, this process will start/stop children PHP processes
dynamically based on on demand. If "LSAPI_CHILDREN" <=1, PHP will be
started in server managed mode.
There are two ways to let PHP handle multiple requests concurrently, Server
Managed Mode and Self Managed Mode. In Server Managed Mode, LiteSpeed web
server dynamically spawn/stop PHP processes, in this mode "Instances" should
match "Max Connections" configuration for PHP external application. To start
PHP in Self Managed Mode, "Instances" should be set to "1", while
`LSAPI_CHILDREN` environment variable should be set to match the value of "Max
Connections" and greater than 1. Web Server will start one PHP process, this
process will start/stop children PHP processes dynamically based on on demand.
If `LSAPI_CHILDREN` less or equal to 1, PHP will be started in server managed
mode.
Self Managed Mode is preferred because all PHP processes can share one
shared memory block for the opcode cache.
Self Managed Mode is preferred because all PHP processes can share one shared
memory block for the opcode cache.
Usually, there is no need to set value of LSAPI_CHILDREN over 100 in
most server environment.
Usually, there is no need to set value of `LSAPI_CHILDREN` over 100 in most
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
"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` (default: 1/3 of `LSAPI_CHILDREN` or 0)
`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
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_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value is 0.
This controls how many requests each child process will handle before it exits
automatically. Several PHP functions have been identified having memory leaks.
This parameter can help reducing memory usage of leaky PHP functions.
* `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
it exits automatically. Several PHP functions have been identified
having memory leaks. This parameter can help reducing memory usage
of leaky PHP functions.
* `LSAPI_MAX_IDLE_CHILDREN` (default value: 1/3 of `LSAPI_CHILDREN` or
`LSAPI_CHILDREN`)
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
process will wait for a new request before it exits. This option help
releasing system resources taken by idle processes.
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)
* LSAPI_MAX_IDLE_CHILDREN
(default value: 1/3 of LSAPI_CHILDREN or LSAPI_CHILDREN)
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
helps releasing system resources taken by an idle parent process.
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_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 is started by `-b` option, it is
disabled automatically.
* LSAPI_MAX_PROCESS_TIME (default value: 300 seconds)
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
=================================
## Compatibility with Apache mod_php
LSAPI PHP supports PHP configuration overridden via web server configuration
as well as .htaccess.
Since 4.0 release "apache_response_headers" function is supported.
as well as `.htaccess`.
Since 4.0 release `apache_response_headers` function is supported.
## Contact
Contact
=======
For support questions, please post to our free support forum, at:
https://www.litespeedtech.com/support/forum/
For support questions, please post to the free support
[forum](https://www.litespeedtech.com/support/forum/):
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
- Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
- Easy Access to PHP with built-in eval()
- Easy Access to Currently Executing Code
- Userland API
- SAPI Agnostic - Easily Integrated
- PHP Configuration File Support
- JIT Super Globals - Set Your Own!!
- Optional readline Support - Comfortable Terminal Operation
- Remote Debugging Support - Bundled Java GUI
- Easy Operation - See Help :)
* Stepthrough Debugging
* Flexible Breakpoints (Class Method, Function, File:Line, Address, Opcode)
* Easy Access to PHP with built-in eval()
* Easy Access to Currently Executing Code
* Userland API
* SAPI Agnostic - Easily Integrated
* PHP Configuration File Support
* JIT Super Globals - Set Your Own!!
* Optional readline Support - Comfortable Terminal Operation
* Remote Debugging Support - Bundled Java GUI
* 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:
```
cd /usr/src/php-src/sapi
git clone https://github.com/krakjoe/phpdbg
cd ../
```bash
cd /path/to/php-src
./buildconf --force
./configure --enable-phpdbg
./configure
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
parameters as were used by the last execution of *configure*.
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
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):
- -n ignore php ini
- -c search for php ini in path
- -z load zend extension
- -d define php ini entry
* `-n` ignore php ini
* `-c` search for php ini in path
* `-z` load zend extension
* `-d` define php ini entry
The following switches change the default behaviour of phpdbg:
- -v disables quietness
- -s enabled stepping
- -e sets execution context
- -b boring - disables use of colour on the console
- -I ignore .phpdbginit (default init file)
- -i override .phpgdbinit location (implies -I)
- -O set oplog output file
- -q do not print banner on startup
- -r jump straight to run
- -E enable step through eval()
- -l listen ports for remote mode
- -a listen address for remote mode
- -S override SAPI name
* `-v` disables quietness
* `-s` enabled stepping
* `-e` sets execution context
* `-b` boring - disables use of colour on the console
* `-I` ignore .phpdbginit (default init file)
* `-i` override .phpgdbinit location (implies -I)
* `-O` set oplog output file
* `-q` do not print banner on startup
* `-r` jump straight to run
* `-E` enable step through eval()
* `-l` listen ports for remote mode
* `-a` listen address for remote mode
* `-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
https://phpdbg.room11.org
See the [website](https://phpdbg.room11.org) for tutorials/documentation.