diff options
author | Peter Kokot <peterkokot@gmail.com> | 2019-03-30 19:39:32 +0100 |
---|---|---|
committer | Peter Kokot <peterkokot@gmail.com> | 2019-03-30 19:39:32 +0100 |
commit | 7915d8bbca2f388e95dd1cae831f3297053d907a (patch) | |
tree | e9f46fae9827d14600fa655dadcc66a2b521fd24 /docs | |
parent | c6eac0d5be24e90faa5206e38cddf2c685712aaa (diff) | |
parent | 8346b4c9f149c1a3d4e0631007e71bbbebd0b87d (diff) | |
download | php-git-7915d8bbca2f388e95dd1cae831f3297053d907a.tar.gz |
Merge branch 'PHP-7.4'
* PHP-7.4:
Move README files to a dedicated docs directory
Diffstat (limited to 'docs')
-rw-r--r-- | docs/input-filter.md | 182 | ||||
-rw-r--r-- | docs/mailinglist-rules.md | 79 | ||||
-rw-r--r-- | docs/output-api.md | 139 | ||||
-rw-r--r-- | docs/parameter-parsing-api.md | 245 | ||||
-rw-r--r-- | docs/release-process.md | 398 | ||||
-rw-r--r-- | docs/self-contained-extensions.md | 167 | ||||
-rw-r--r-- | docs/streams.md | 376 | ||||
-rw-r--r-- | docs/unix-build-system.md | 123 |
8 files changed, 1709 insertions, 0 deletions
diff --git a/docs/input-filter.md b/docs/input-filter.md new file mode 100644 index 0000000000..bca7f29ad2 --- /dev/null +++ b/docs/input-filter.md @@ -0,0 +1,182 @@ +Input Filter Support in PHP 5 +----------------------------- + +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. + +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. + +ZEND_BEGIN_MODULE_GLOBALS(my_input_filter) + zval *post_array; + zval *get_array; + zval *cookie_array; +ZEND_END_MODULE_GLOBALS(my_input_filter) + +#ifdef ZTS +#define IF_G(v) TSRMG(my_input_filter_globals_id, zend_my_input_filter_globals *, v) +#else +#define IF_G(v) (my_input_filter_globals.v) +#endif + +ZEND_DECLARE_MODULE_GLOBALS(my_input_filter) + +zend_function_entry my_input_filter_functions[] = { + PHP_FE(my_get_raw, NULL) + {NULL, NULL, NULL} +}; + +zend_module_entry my_input_filter_module_entry = { + STANDARD_MODULE_HEADER, + "my_input_filter", + my_input_filter_functions, + PHP_MINIT(my_input_filter), + PHP_MSHUTDOWN(my_input_filter), + NULL, + PHP_RSHUTDOWN(my_input_filter), + PHP_MINFO(my_input_filter), + "0.1", + STANDARD_MODULE_PROPERTIES +}; + +PHP_MINIT_FUNCTION(my_input_filter) +{ + ZEND_INIT_MODULE_GLOBALS(my_input_filter, php_my_input_filter_init_globals, NULL); + + REGISTER_LONG_CONSTANT("POST", PARSE_POST, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("GET", PARSE_GET, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("COOKIE", PARSE_COOKIE, CONST_CS | CONST_PERSISTENT); + + sapi_register_input_filter(my_sapi_input_filter); + return SUCCESS; +} + +PHP_RSHUTDOWN_FUNCTION(my_input_filter) +{ + if(IF_G(get_array)) { + zval_ptr_dtor(&IF_G(get_array)); + IF_G(get_array) = NULL; + } + if(IF_G(post_array)) { + zval_ptr_dtor(&IF_G(post_array)); + IF_G(post_array) = NULL; + } + if(IF_G(cookie_array)) { + zval_ptr_dtor(&IF_G(cookie_array)); + IF_G(cookie_array) = NULL; + } + return SUCCESS; +} + +PHP_MINFO_FUNCTION(my_input_filter) +{ + php_info_print_table_start(); + php_info_print_table_row( 2, "My Input Filter Support", "enabled" ); + php_info_print_table_end(); +} + +/* The filter handler. If you return 1 from it, then PHP also registers the + * (modified) variable. Returning 0 prevents PHP from registering the variable; + * you can use this if your filter already registers the variable under a + * different name, or if you just don't want the variable registered at all. */ +SAPI_INPUT_FILTER_FUNC(my_sapi_input_filter) +{ + zval new_var; + zval *array_ptr = NULL; + char *raw_var; + int var_len; + + assert(*val != NULL); + + switch(arg) { + case PARSE_GET: + if(!IF_G(get_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(get_array) = array_ptr; + break; + case PARSE_POST: + if(!IF_G(post_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(post_array) = array_ptr; + break; + case PARSE_COOKIE: + if(!IF_G(cookie_array)) { + ALLOC_ZVAL(array_ptr); + array_init(array_ptr); + INIT_PZVAL(array_ptr); + } + IF_G(cookie_array) = array_ptr; + break; + } + Z_STRLEN(new_var) = val_len; + Z_STRVAL(new_var) = estrndup(*val, val_len); + Z_TYPE(new_var) = IS_STRING; + + var_len = strlen(var); + raw_var = emalloc(var_len+5); /* RAW_ and a \0 */ + strcpy(raw_var, "RAW_"); + strlcat(raw_var,var,var_len+5); + + php_register_variable_ex(raw_var, &new_var, array_ptr); + + php_strip_tags(*val, val_len, NULL, NULL, 0); + + *new_val_len = strlen(*val); + return 1; +} + +PHP_FUNCTION(my_get_raw) +{ + long arg; + char *var; + int var_len; + zval **tmp; + zval *array_ptr = NULL; + + if(zend_parse_parameters(2, "ls", &arg, &var, &var_len) == FAILURE) { + return; + } + + switch(arg) { + case PARSE_GET: + array_ptr = IF_G(get_array); + break; + case PARSE_POST: + array_ptr = IF_G(post_array); + break; + case PARSE_COOKIE: + array_ptr = IF_G(post_array); + break; + } + + if(!array_ptr) { + RETURN_FALSE; + } + + if(zend_hash_find(HASH_OF(array_ptr), var, var_len+5, (void **)&tmp) == SUCCESS) { + *return_value = **tmp; + zval_copy_ctor(return_value); + } else { + RETVAL_FALSE; + } +} diff --git a/docs/mailinglist-rules.md b/docs/mailinglist-rules.md new file mode 100644 index 0000000000..8fafbd717d --- /dev/null +++ b/docs/mailinglist-rules.md @@ -0,0 +1,79 @@ +==================== + 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 +the PHP project. Therefore expect your contributions to be of much less positive +impact if you do not follow these rules. More importantly you can actually +assume that not following these rules will hurt the PHP project. + +PHP is developed through the efforts of a large number of people. +Collaboration is a Good Thing(tm), and mailinglists lets us do this. Thus, +following some basic rules with regards to mailinglist usage will: + + a. Make everybody happier, especially those responsible for developing PHP + itself. + + b. Help in making sure we all use our time more efficiently. + + c. Prevent you from making a fool of yourself in public. + + 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. + + 2. Do not post when you are angry. Any post can wait a few hours. Review + 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. + + 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 + are faced with a lot of requests. Make sure that you have done your + research before posting to the entire developer community. + + 5. Patches have a much greater chance of acceptance than just asking the + PHP developers to implement a feature for you. For one it makes the + discussion more concrete and it shows that the poster put thought and time + into the request. + + 6. If you are posting to an existing thread, make sure that you know what + previous posters have said. This is even more important the longer the + thread is already. + + 7. Please configure your email client to use a real name and keep message + signatures to a maximum of 2 lines if at all necessary. + +The next few rules are more some general hints: + + 1. If you notice that your posting ratio is much higher than that of other + people, double check the above rules. Try to wait a bit longer before + sending your replies to give other people more time to digest your answers + and more importantly give you the opportunity to make sure that you + aggregate your current position into a single mail instead of multiple + ones. + + 2. Consider taking a step back from a very active thread now and then. Maybe + talking to some friends and fellow developers will help in understanding + the other opinions better. + + 3. Do not top post. Place your answer underneath anyone you wish to quote + and remove any previous comment that is not relevant to your post. + + 4. Do not high-jack threads, by bringing up entirely new topics. Please + create an entirely new thread copying anything you wish to quote into the + 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). + +Happy hacking, + +PHP Team diff --git a/docs/output-api.md b/docs/output-api.md new file mode 100644 index 0000000000..3c23c8e8de --- /dev/null +++ b/docs/output-api.md @@ -0,0 +1,139 @@ +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. + + 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 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 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); + + 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(); + + 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(); + + 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(); + + 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(); + + 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 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); + + 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); + + 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); + + 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) { ... } + + 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 + + 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 + + +Open questions + + 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 diff --git a/docs/parameter-parsing-api.md b/docs/parameter-parsing-api.md new file mode 100644 index 0000000000..f65c19a723 --- /dev/null +++ b/docs/parameter-parsing-api.md @@ -0,0 +1,245 @@ +Fast Parameter Parsing API +========================== + +In PHP 7, a "Fast Parameter Parsing API" was introduced. See + + https://wiki.php.net/rfc/fast_zpp + +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. + + +Prototypes +---------- +/* 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. + +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. + +PHP 5.3 includes a new function (actually implemented as macro): + +int zend_parse_parameters_none(); + +This returns SUCCESS if no argument has been passed to the function, +FAILURE otherwise. + +PHP 5.5 includes a new function: + +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 +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 + + +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. + + 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. + + +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! + +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) + +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): + +# php ./scripts/dev/check_parameters.php /path/to/your/sources/ + + +Examples +-------- +/* Gets a long, a string and its length, and a zval */ +zend_long l; +char *s; +size_t s_len; +zval *param; +if (zend_parse_parameters(ZEND_NUM_ARGS(), "lsz", + &l, &s, &s_len, ¶m) == FAILURE) { + return; +} + + +/* Gets an object of class specified by my_ce, and an optional double. */ +zval *obj; +double d = 0.5; +zend_class_entry *my_ce; +if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|d", + &obj, my_ce, &d) == FAILURE) { + return; +} + + +/* Gets an object or null, and an array. + If null is passed for object, obj will be set to NULL. */ +zval *obj; +zval *arr; +if (zend_parse_parameters(ZEND_NUM_ARGS(), "o!a", + &obj, &arr) == FAILURE) { + return; +} + + +/* Gets a separated array which can also be null. */ +zval *arr; +if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/!", + &arr) == FAILURE) { + return; +} + +/* Get either a set of 3 longs or a string. */ +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 + * than a 'size_t', the upper bits might not be initialized + * properly, leading to random crashes on platforms like + * Tru64 or Linux/Alpha. + */ +size_t length; + +if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), + "lll", &l1, &l2, &l3) == SUCCESS) { + /* manipulate longs */ +} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), + "s", &s, &length) == SUCCESS) { + /* manipulate string */ +} else { + /* output error */ + + 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; +} + +for (i = 0; i < num_varargs; i++) { + /* do something with varargs[i] */ +} + +if (varargs) { + efree(varargs); +} + + +/* Function that accepts a string, followed by varargs (1 or more) */ + +char *str; +size_t str_len; +int i, num_varargs; +zval *varargs = NULL; + +if (zend_parse_parameters(ZEND_NUM_ARGS(), "s+", &str, &str_len, &varargs, &num_varargs) == FAILURE) { + return; +} + +for (i = 0; i < num_varargs; i++) { + /* do something with varargs[i] */ +} + +/* Function that takes an array, followed by varargs, and ending with a long */ +zend_long num; +zval *array; +int i, num_varargs; +zval *varargs = NULL; + +if (zend_parse_parameters(ZEND_NUM_ARGS(), "a*l", &array, &varargs, &num_varargs, &num) == FAILURE) { + return; +} + +for (i = 0; i < num_varargs; i++) { + /* do something with varargs[i] */ +} + +/* Function that doesn't accept any arguments */ +if (zend_parse_parameters_none() == FAILURE) { + return; +} diff --git a/docs/release-process.md b/docs/release-process.md new file mode 100644 index 0000000000..fdc2440748 --- /dev/null +++ b/docs/release-process.md @@ -0,0 +1,398 @@ +======================= + PHP Release Process +======================= + +General notes and tips +---------------------- + +1. Do not release on Fridays, Saturdays or Sundays +because the sysadmins can not upgrade stuff then. + +2. Package two days before a release. So if the release is to be on Thursday, +package on Tuesday. Think about timezones as well. + +3. Ensure that the tests on Travis CI are green. +See: https://travis-ci.org/php/php-src/builds +It is recommended to do so a couple of days before the packaging day, to +have enough time to investigate failures, communicate with the authors and +commit the fixes. +The RM for the branch is also responsible for keeping the CI green on +ongoing basis between the releases. Check the CI status for your branch +periodically and resolve the failures ASAP. See more in: +https://wiki.php.net/rfc/travis_ci + +4. Ensure that Windows builds will work before packaging + +5. Follow all steps to the letter. When unclear ask previous RM's (David/Julien/ +Johannes/Stas/Derick/Ilia) before proceeding. Ideally make sure that for the +first releases one of the previous RM's is around to answer questions. For the +steps related to the php/QA/bug websites try to have someone from the webmaster +team (Bjori) on hand. + +6. Verify the tags to be extra sure everything was tagged properly. + +7. Moving extensions from/to PECL requires write access to the destination. +Most developers should have this. + +Moving extensions from php-src to PECL +- Checkout the pecl directory, most likely you want a sparse-root checkout + svn co --depth=empty https://svn.php.net/repository/pecl +- Create a directory for the extension incl. branch and tag structure, + no trunk at this point and commit this to svn + cd pecl; mkdir foo foo/tags foo/branches; svn add foo; svn commit +- Move the extension from php-src to the new location + svn mv https://svn.php.net/repository/php/php-src/trunk/ext/foo \ + https://svn.php.net/repository/pecl/foo/trunk + +If the extension is still usable or not dead, in cooperation with the extension +maintainers if any: +- create the pecl.php.net/foo package and its content, license, maintainer +- create the package.xml, commit +- release the package + +For Moving extensions from PECL to php-src the svn mv has to be done the other +way round. + +Rolling a non stable release (alpha/beta/RC) +-------------------------------------------- + +1. Check windows snapshot builder logs (http://windows.php.net/downloads/snaps/ the last revision) + +2. Check the tests at https://travis-ci.org/php/php-src/builds + +3. run the "scripts/dev/credits" script in php-src and commit the changes in the +credits files in ext/standard. + +4. Checkout the release branch for this release (e.g., PHP-5.4.2) from the main branch. + +5. Bump the version numbers in ``main/php_version.h``, ``Zend/zend.h``, ``configure.ac`` and possibly ``NEWS``. +Do not use abbreviations for alpha and beta. Do not use dashes, you should +``#define PHP_VERSION "5.4.22RC1"`` and not ``#define PHP_VERSION "5.4.22-RC1"`` + +6. Compile and make test, with and without ZTS, using the right Bison version +(for example, for 5.5, Bison 2.4.1 is used) + +7. Check ./sapi/cli/php -v output for version matching. + +8. If all is right, commit the changes to the release branch with ``git commit -a``. + +9. Tag the repository release branch with the version, e.g.: +``git tag -u YOURKEYID php-5.4.2RC2`` + +10. Bump the version numbers in ``main/php_version.h``, ``Zend/zend.h``, ``configure.ac`` and ``NEWS`` +in the *main* branch (PHP-5.4 for example) to prepare for the **next** version. +F.e. if the RC is "5.4.1RC1" then the new one should be "5.4.2-dev" - regardless if we get +a new RC or not. This is to make sure ``version_compare()`` can correctly work. +Commit the changes to the main branch. + +11. Push the changes to the main repo, the tag, the main branch and the release branch : +``git push --tags origin HEAD`` +``git push origin {main branch}`` +``git push origin {release branch}`` + +12. run: ``PHPROOT=. ./scripts/dev/makedist 5.4.2RC2``, this will export the tree, create configure +and build three tarballs (gz, bz2 and xz). + +13. run ``scripts/dev/gen_verify_stub <version> [identity]``, this will sign the tarballs +and output verification information to be included in announcement email + +14. Copy those tarballs (scp, rsync) to downloads.php.net, in your homedir there should be a +directory "public_html/". Copy them into there. If you do not have this directory, create it. + +15. Now the RC can be found on http://downloads.php.net/~yourname, +f.e. http://downloads.php.net/~derick/ + +16. Once the release has been tagged, contact the release-managers@ distribution list +so that Windows binaries can be created. Once those are made, they can be found at +http://windows.php.net/download + +Getting the non stable release (alpha/beta/RC) announced +-------------------------------------------------------- + +1. Update ``qa.git/include/release-qa.php`` with the appropriate information. + See the documentation within release-qa.php for more information, but all releases + and RCs are configured here. Only $QA_RELEASES needs to be edited. + + Example: When rolling an RC, set the 'rc' with appropriate information for the + given version. + + Note: Remember to update the sha256 checksum information. + +2. Update ``web/php.git/include/version.inc`` (X_Y=major_minor version number) + + a. ``$PHP_X_Y_RC`` = "5.4.0RC1" (should be set to "false" before) + + b. ``$PHP_X_Y_RC_DATE`` = "06 September 2007" + +3. Skip this step for non stable releases after GA of minor or major versions + (e.g. announce 7.3.0RC1, but not 7.3.1RC1): + + Add a short notice to phpweb stating that there is a new release, and + highlight the major important things (security fixes) and when it is + important to upgrade. + + a. Call php bin/createNewsEntry in your local phpweb checkout + Use category "frontpage" *and* "releases" for all stable releases. + Use category "frontpage" for X.Y.0 non-stable releases only (news only). + + b. Add the content for the news entry. Be sure to include the text: + "THIS IS A DEVELOPMENT PREVIEW - DO NOT USE IT IN PRODUCTION!" + +4. Commit and push changes to qa and web + +*Wait for web and qa sites to update with new information before sending announce* + +5. Send **separate** emails **To** ``internals@lists.php.net`` and ``php-general@lists.php.net`` +lists pointing out "the location of the release" and "the possible release date of +either the next RC, or the final release". Include in this information the verification +information output by ``gen_verify_stub``. + +6. Send **separate** emails (see example here http://news.php.net/php.pear.qa/5201) **To** +``php-qa@lists.php.net`` and ``primary-qa-tester@lists.php.net``. +These emails are to notify the selected projects about a new release so that they +can make sure their projects keep working. Make sure that you have been setup +as a moderator for ``primary-qa-tester@lists.php.net`` by having someone (Hannes, Dan, +Derick) run the following commands for you: + +``ssh lists.php.net`` + +``sudo -u ezmlm ezmlm-sub ~ezmlm/primary-qa-tester/mod moderator-email-address`` + +Rolling a stable release +------------------------ + +1. Checkout your release branch, you should have created when releasing previous RC +and bump the version numbers in ``main/php_version.h``, ``Zend/zend.h``, ``configure.ac`` and possibly ``NEWS``. + +2. If a CVE commit needs to be merged to the release, then have it committed to +the base branches and merged upwards as usual (f.e commit the CVE fix to 5.3, +merge to 5.4, 5.5 etc...). Then you can cherry-pick it in your release branch. +Don't forget to update NEWS manually in an extra commit then. + +3. Commit those changes. Ensure the tests at https://travis-ci.org/php/php-src/builds are +still passing. + +4. run the "scripts/dev/credits" script in php-src and commit the changes in the +credits files in ext/standard. + +5. Compile and make test, with and without ZTS, using the right Bison version +(for example, for 5.5, Bison 2.4.1 is used) + +6. Check ./sapi/cli/php -v output for version matching. + +7. tag the repository with the version f.e. "``git tag -u YOURKEYID php-5.4.1``" + +8. Push the tag f.e. "``git push origin php-5.4.1``" + +9. run: ``PHPROOT=. ./scripts/dev/makedist 5.4.1``, this will export the tag, create configure +and build three tarballs (gz, bz2 and xz). + Check if the pear files are updated (phar). + On some systems the behavior of GNU tar can default to produce POSIX compliant archives +with PAX headers. As not every application is compatible with that format, creation of +archives with PAX headers should be avoided. When packaging on such a system, the GNU tar +can be influenced by defining the environment variable TAR_OPTIONS='--format=gnu'. + +10. run ``scripts/dev/gen_verify_stub <version> [identity]``, this will sign the tarballs + and output verification information to be included in announcement email + +11. Commit and push all the tarballs and signature files to web/php-distributions.git, + then update the git submodule reference in web/php.git: + ``git submodule init; + git submodule update; + cd distributions; + git fetch; + git pull --rebase origin master; + cd ..; + git commit distributions; + git push;`` +This is to fetch the last commit id from php-distributions.git and commit this +last commit id to web/php.git, then, mirrors will now sync + +12. Once the release has been tagged, contact release managers, windows builders, and package maintainers +so that they can build releases. Do not send this announcement to any public lists. + +Getting the stable release announced +------------------------------------ + +1. Update phpweb/include/releases.inc with the old release info + (updates the download archives) + + a. You can run ``php bin/bumpRelease 7 2`` where the first number is + the major version, and the second number is the minor version + (7.2 in this example). + + b. If that fails for any non-trivially fixable reason, you can + manually copy the old information to include/releases.inc + +2. Update ``phpweb/include/version.inc`` (X_Y=major_minor release number): + + a. ``$PHP_X_Y_VERSION`` to the correct version + + b. ``$PHP_X_Y_DATE`` to the release date + + c. ``$PHP_X_Y_SHA256`` array and update all the SHA256 sums + + d. set ``$PHP_X_Y_RC`` to false! + + e. Make sure there are no outdated "notes" or edited "date" keys in the + ``$RELEASES[X][$PHP_X_VERSION]["source"]`` array + + f. Only for the first revision of a major or minor release bump + ``$PHP_X_VERSION``, ``$PHP_X_DATE`` and ``$PHP_X_RC_DATE``. + +3. Create the release file (releases/x_y_z.php) + Usually we use the same content as for point 6, but included in php template + instead of the release xml. + +4. Update php-qa/include/release-qa.php and add the next version as an QARELEASE + (prepare for next RC) + +5. Update the ChangeLog file for the given major version +f.e. ``ChangeLog-5.php`` from the NEWS file + + a. go over the list and put every element on one line + + b. check for &, < and > and escape them if necessary + + c. remove all the names at the ends of lines + + d. for marking up, you can do the following (with VI): + + I. ``s/^- /<li>/`` + + II. ``s/$/<\/li>/`` + + III. ``s/Fixed bug #\([0-9]\+\)/<?php bugfix(\1); ?>/`` + + IV. ``s/Fixed PECL bug #\([0-9]\+\)/<?php peclbugfix(\1); ?>/`` + + V. ``s/FR #\([0-9]\+\)/FR <?php bugl(\1); ?>/`` + + e. You may want to try php-web/bin/news2html to automate this task + +6. Add a short notice to phpweb stating that there is a new release, and +highlight the major important things (security fixes) and when it is important +to upgrade. + + a. Call php bin/createNewsEntry in your local phpweb checkout + + b. Add the content for the news entry + +7. Commit and push all the changes to their respective git repos + +8. **Check mirrors have been synced before announcing or pushing news** + Try, f.e. http://www.php.net/get/php-5.5.1.tar.bz2/from/a/mirror + Try several mirrors, mirrors may update slowly (may take an hour) + +9. Please note down the sha256 and the PGP signature (.asc). These *must* be + included in the release mail. +10. Wait an hour or two, then send a mail to php-announce@lists.php.net, +php-general@lists.php.net and internals@lists.php.net with a text similar to +http://news.php.net/php.internals/17222. +Please make sure that the mail to php-announce@ is its own completely separate email. +This is to make sure that replies to the announcement on php-general@ or internals@ +will not accidentally hit the php-announce@ mailinglist. + +Re-releasing the same version (or -pl) +-------------------------------------- + +1. Commit the new binaries to ``phpweb/distributions/`` + +2. Update ``phpweb/include/version.inc`` (X_Y=major_minor release number): + + a. If only releasing for one OS, make sure you edit only those variables + + b. ``$PHP_X_Y_VERSION`` to the correct version + + c. ``$PHP_X_Y_DATE`` to the release date + + d. ``$PHP_X_Y_SHA256`` array and update all the SHA256 sums + + e. Make sure there are no outdated "notes" or edited "date" keys in the + ``$RELEASES[X][$PHP_X_VERSION]["source"]`` array + +3. Add a short notice to phpweb stating that there is a new release, and +highlight the major important things (security fixes) and when it is important +to upgrade. + + a. Call php bin/createNewsEntry in your local phpweb checkout + + b. Add the content for the news entry + +4. Commit all the changes (``include/version.inc``, ``archive/archive.xml``, +``archive/entries/YYYY-MM-DD-N.xml``) + +5. Wait an hour or two, then send a mail to php-announce@lists.php.net, +php-general@lists.php.net and internals@lists.php.net with a text similar to +the news entry. +Please make sure that the mail to php-announce@ is its own completely separate email. +This is to make sure that replies to the announcement on php-general@ or internals@ +will not accidentally hit the php-announce@ mailinglist. + +Forking a new release branch +---------------------------- + +1. One week prior to cutting X.Y.0beta1, warn internals@ that your version's branch + is about to be cut, and that PHP-X.Y will be moving into feature freeze. + Try to be specific about when the branch will be cut. + Example: http://news.php.net/php.internals/99864 + +2. Just prior to cutting X.Y.0beta1, create the new branch locally. + Add a commit on master after the branch point clearing the NEWS, UPGRADING + and UPGRADING.INTERNALS files, updating the version in configure.ac (run + ./configure to automatically update main/php_versions.h, too) and Zend/zend.h. + Also list the new branch in CONTRIBUTING.md. + Example: http://git.php.net/?p=php-src.git;a=commit;h=a63c99b + Push the new branch and the commit just added to master. + +3. Immediately notify internals@ of the branch cut and advise the new merging order: + Example: http://news.php.net/php.internals/99903 + +4. Update php-web:git.php and wiki.php.net/vcs/gitworkflow to reflect the new branch: + Example: https://github.com/php/web-php/commit/74bcad4c770d95f21b7fbeeedbd76d943bb83f23 + +5. Notify nlopess@ to add PHP_X_Y tag to gcov.php.net + +Preparing for the initial stable version (PHP X.Y.0) +---------------------------------------------------- + +1. About the time you release the first RC, remind the documentation team + (phpdoc@lists.php.net) to write the migration guide. See to it that they + have done it before you release the initial stable version, since you want + to link to it in the release announcements. + +2. Timely get used to the differences in preparing and announcing a stable + release. + +Prime the selection of the Release Managers of the next version +--------------------------------------------------------------- + +1. About three months before the scheduled release of the first alpha of the + next minor or major release, issue a call for volunteers on + internals@lists.php.net (cf. http://news.php.net/php.internals/98652). + +2. Make sure that there are two or more volunteers, and hold a vote if necessary + (see https://wiki.php.net/rfc/releaseprocess#release_managers_selection). + +3. Help the new release managers with their first steps. + +New Release Manager Checklist +----------------------------- + +1. Email systems@ to get setup for access to downloads.php.net and to be added to the + release-managers@ distribution list. + +2. Create a GPG key for your @php.net address and publish it by editing `include/gpg-keys.inc` + in the `web-php` repository, adding the output of `gpg --fingerprint "$USER@php.net"`. Let + one or more of the previous RMs sign your key. Publish your public key to pgp.mit.edu with: + `gpg --keyserver pgp.mit.edu --send-keys $KEYID` + +3. Request karma to edit main/php_version.h and Zend/zend.h. Possibly karma for other restricted parts of + php-src might come in question. To edit main/php_version.h in a release branch, + you need release manager karma in global_avail. + +4. Request karma for web/qa.git and web/php.git for publishing release announcements. + +5. Request moderation access to php-announce@lists.php.net and primary-qa-tester@lists.php.net lists, to + be able to moderate your release announcements. All the announcements should be sent from + the @php.net alias. diff --git a/docs/self-contained-extensions.md b/docs/self-contained-extensions.md new file mode 100644 index 0000000000..1aaec6d6c4 --- /dev/null +++ b/docs/self-contained-extensions.md @@ -0,0 +1,167 @@ +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: + + - Configuration file (config.m4) + - Source code for your module + + We will describe now how to create these and how to put things + together. + +PREPARING YOUR SYSTEM + + While the result will run on any system, a developer's setup needs these + tools: + + GNU autoconf + GNU libtool + GNU m4 + + All of these are available from + + ftp://ftp.gnu.org/pub/gnu/ + +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. + +------------------------------------------------------------------------------ +PHP_ARG_ENABLE([foobar], + [whether to enable foobar], + [AS_HELP_STRING([--enable-foobar], + [Enable foobar])]) + +if test "$PHP_FOOBAR" != "no"; then + PHP_NEW_EXTENSION(foobar, foo.c bar.c, $ext_shared) +fi +------------------------------------------------------------------------------ + + 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. + + 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 + + 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. + + 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 + + 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 + + $ /php/bin/phpize + + This will automatically copy the necessary build files and create + configure from your config.m4. + + And that's it. You now have a self-contained extension. + +INSTALLING A SELF-CONTAINED EXTENSION + + An extension can be installed by running: + + $ ./configure \ + [--with-php-config=/path/to/php-config] + $ make install + +ADDING SHARED MODULE SUPPORT TO A MODULE + + In order to be useful, a self-contained extension must be loadable + as a shared module. I will explain now how you can add shared module + support to an existing module called foo. + + 1. In config.m4, use PHP_ARG_WITH/PHP_ARG_ENABLE. Then you will + automatically be able to use --with-foo=shared[,..] or + --enable-foo=shared[,..]. + + 2. In config.m4, use PHP_NEW_EXTENSION(foo,.., $ext_shared) to enable + building the extension. + + 3. Add the following lines to your C source file: + + #ifdef COMPILE_DL_FOO + ZEND_GET_MODULE(foo) + #endif + +PECL SITE CONFORMITY + + 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 + + #define PHP_FOO_VERSION "1.2.3" + + This macros has to be used within your foo_module_entry to indicate the + extension version. diff --git a/docs/streams.md b/docs/streams.md new file mode 100644 index 0000000000..6ef69c733a --- /dev/null +++ b/docs/streams.md @@ -0,0 +1,376 @@ +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? +============ +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. +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. + +The main functions are: + +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); +PHPAPI size_t php_stream_printf(php_stream * stream, + const char * fmt, ...); +PHPAPI int php_stream_eof(php_stream * stream); +PHPAPI int php_stream_getc(php_stream * stream); +PHPAPI char *php_stream_gets(php_stream * stream, char *buf, size_t maxlen); +PHPAPI int php_stream_close(php_stream * stream); +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. + +Opening Streams +=============== +In most cases, you should use this API: + +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. + +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: + +PHPAPI php_stream *php_stream_fopen_from_file(FILE *file, const char *mode); + Convert a FILE * into a stream. + +PHPAPI php_stream *php_stream_fopen_tmpfile(void); + 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. + +There are some network enabled relatives in php_network.h: + +PHPAPI php_stream *php_stream_sock_open_from_socket(int socket, int persistent); + 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. + +PHPAPI php_stream *php_stream_sock_open_unix(const char *path, int persistent, + struct timeval *timeout); + Open a UNIX domain socket. + + +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: + +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. + +If you want to read the contents of a stream into an allocated memory buffer, +you should use: + +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. + +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: + +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. + +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: Writing to the stream may not affect the original source, so it +only makes sense to use this for read-only use. + +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(); + +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. + +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: + +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); + +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: +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 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 +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: + + if (php_stream_is(stream, PHP_STREAM_IS_STDIO)) { + /* can safely cast to FILE* with no adverse side effects */ + } + +You can use: + +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: + +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. + +Stream Internals +================ + +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 +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 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: + +PHPAPI php_stream * php_stream_fopen(const char * filename, const char * mode) +{ + FILE * fp = fopen(filename, mode); + php_stream * ret; + + if (fp) { + ret = php_stream_alloc(&php_stream_stdio_ops, fp, 0, 0, mode); + if (ret) + return ret; + + fclose(fp); + } + return NULL; +} + +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). + +The prototype for php_stream_alloc is this: + +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). + +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 +====================================== + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +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 #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 +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. + +For structured state you might have this: + +struct my_state { + MYSQL conn; + MYSQL_RES * result; +}; + +struct my_state * state = pemalloc(sizeof(struct my_state), persistent); + +/* initialize the connection, and run a query, using the fields in state to + * hold the results */ + +state->result = mysql_use_result(&state->conn); + +/* now allocate the stream itself */ +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 +example). + +For example, for reading from this weird MySQL stream: + +static size_t php_mysqlop_read(php_stream * stream, char * buf, size_t count) +{ + struct my_state * state = (struct my_state*)stream->abstract; + + if (buf == NULL && count == 0) { + /* in this special case, php_streams is asking if we have reached the + * end of file */ + if (... at end of file ...) + return EOF; + else + return 0; + } + + /* pull out some data from the stream and put it in buf */ + ... mysql_fetch_row(state->result) ... + /* we could do something strange, like format the data as XML here, + and place that in the buf, but that brings in some complexities, + 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: + +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. +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. diff --git a/docs/unix-build-system.md b/docs/unix-build-system.md new file mode 100644 index 0000000000..8db9bcde2f --- /dev/null +++ b/docs/unix-build-system.md @@ -0,0 +1,123 @@ +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) + +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). + + +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. + + + +If You Are Developing PHP + + + + +Extension developers: + +Makefile.ins are abandoned. The files which are to be compiled +are specified in the config.m4 now using the following macro: + +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. + +The full syntax: + +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. + +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: + +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). + +Now, you need to tell the build system that you want to build files +in a directory called $ext_builddir/lib: + +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. + +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. + +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: + +Instead of using PHP_SAPI=foo/PHP_BUILD_XYZ, you will need to type + +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). + +For example for APXS: + +PHP_SELECT_SAPI(apache, shared, sapi_apache.c mod_php.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. + +Have a look at the well documented macros +PHP_ADD_SOURCES(_X) in acinclude.m4. |