Does PHP automatically close the file after a file(); function, or does it require fclose(); or similar?
Does PHP automatically close the file after a file(); function, or does it require fclose(); or similar?
No, file() doesn't require a fclose() call. You can see that in the source code of the function that it all ends nice and clean, so you don't have to call fclose() or do anything similar, you simple can call file().
Source code:
/* {{{ proto array file(string filename [, int flags[, resource context]])
Read entire file into an array */
PHP_FUNCTION(file)
{
char *filename;
size_t filename_len;
char *p, *s, *e;
register int i = 0;
char eol_marker = '\n';
zend_long flags = 0;
zend_bool use_include_path;
zend_bool include_new_line;
zend_bool skip_blank_lines;
php_stream *stream;
zval *zcontext = NULL;
php_stream_context *context = NULL;
zend_string *target_buf;
/* Parse arguments */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|lr!", &filename, &filename_len, &flags, &zcontext) == FAILURE) {
return;
}
if (flags < 0 || flags > (PHP_FILE_USE_INCLUDE_PATH | PHP_FILE_IGNORE_NEW_LINES | PHP_FILE_SKIP_EMPTY_LINES | PHP_FILE_NO_DEFAULT_CONTEXT)) {
php_error_docref(NULL, E_WARNING, "'" ZEND_LONG_FMT "' flag is not supported", flags);
RETURN_FALSE;
}
use_include_path = flags & PHP_FILE_USE_INCLUDE_PATH;
include_new_line = !(flags & PHP_FILE_IGNORE_NEW_LINES);
skip_blank_lines = flags & PHP_FILE_SKIP_EMPTY_LINES;
context = php_stream_context_from_zval(zcontext, flags & PHP_FILE_NO_DEFAULT_CONTEXT);
stream = php_stream_open_wrapper_ex(filename, "rb", (use_include_path ? USE_PATH : 0) | REPORT_ERRORS, NULL, context);
if (!stream) {
RETURN_FALSE;
}
/* Initialize return array */
array_init(return_value);
if ((target_buf = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) != NULL) {
s = target_buf->val;
e = target_buf->val + target_buf->len;
if (!(p = (char*)php_stream_locate_eol(stream, target_buf))) {
p = e;
goto parse_eol;
}
if (stream->flags & PHP_STREAM_FLAG_EOL_MAC) {
eol_marker = '\r';
}
/* for performance reasons the code is duplicated, so that the if (include_new_line)
* will not need to be done for every single line in the file. */
if (include_new_line) {
do {
p++;
parse_eol:
add_index_stringl(return_value, i++, s, p-s);
s = p;
} while ((p = memchr(p, eol_marker, (e-p))));
} else {
do {
int windows_eol = 0;
if (p != target_buf->val && eol_marker == '\n' && *(p - 1) == '\r') {
windows_eol++;
}
if (skip_blank_lines && !(p-s-windows_eol)) {
s = ++p;
continue;
}
add_index_stringl(return_value, i++, s, p-s-windows_eol);
s = ++p;
} while ((p = memchr(p, eol_marker, (e-p))));
}
/* handle any left overs of files without new lines */
if (s != e) {
p = e;
goto parse_eol;
}
}
if (target_buf) {
zend_string_free(target_buf);
}
php_stream_close(stream);
}
/* }}} */
See at the end 3rd line from the bottom it ends the function nice and clean:
php_stream_close(stream);
Related
I'm porting a php5 tot php7, but don't understand how to correctly use zend_string since it gives me errors when compiling. I followed the phpng guide on the changes in php7. Most functions i could port easily, but this function is giving me a headache.
The php5 version of the module looks like this:
PHP_FUNCTION(swe_houses)
{
char *arg = NULL;
int hsys_len, rc;
char *hsys = NULL;
double tjd_ut, geolat, geolon;
double cusps[37], ascmc[10];
int i, houses;
zval *cusps_arr, *ascmc_arr;
if(ZEND_NUM_ARGS() != 4) WRONG_PARAM_COUNT;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddds",
&tjd_ut, &geolat, &geolon, &hsys, &hsys_len) == FAILURE) {
return;
}
if (hsys_len < 1)
return;
rc = swe_houses(tjd_ut, geolat, geolon, hsys[0], cusps, ascmc);
/* create 2 index array, and 1 assoc array */
array_init(return_value);
MAKE_STD_ZVAL(cusps_arr);
array_init(cusps_arr);
if (hsys[0] == 'G')
houses = 37;
else
houses = 13;
for(i = 0; i < houses; i++)
add_index_double(cusps_arr, i, cusps[i]);
MAKE_STD_ZVAL(ascmc_arr);
array_init(ascmc_arr);
for(i = 0; i < 10; i++)
add_index_double(ascmc_arr, i, ascmc[i]);
add_assoc_zval(return_value, "cusps", cusps_arr);
add_assoc_zval(return_value, "ascmc", ascmc_arr);
add_assoc_long(return_value, "rc", rc);
}
So the guide says i need to replace "char *hsys" into "zend_string *hsys = null". And replaced "MAKE_STD_ZVAL" functions to "ZVAL_NEW_ARR". In the zend_parse_parameters function i changed the "s" parameter to "S".
So eventually i changed the code to look like this:
PHP_FUNCTION(swe_houses)
{
zend_string *arg = NULL;
size_t hsys_len, rc;
zend_string *hsys = NULL;
double tjd_ut, geolat, geolon;
double cusps[37], ascmc[10];
size_t i, houses;
zval *cusps_arr, *ascmc_arr;
if(ZEND_NUM_ARGS() != 4) WRONG_PARAM_COUNT;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "dddS",
&tjd_ut, &geolat, &geolon, &hsys, &hsys_len) == FAILURE) {
return;
}
if (hsys_len < 1)
return;
rc = swe_houses(tjd_ut, geolat, geolon, hsys[0], cusps, ascmc);
/* create 2 index array, and 1 assoc array */
array_init(return_value);
/*******************************/
/* removed for php 7 */
/* MAKE_STD_ZVAL(cusps_arr); */
/*******************************/
ZVAL_NEW_ARR(cusps_arr);
array_init(cusps_arr);
if (hsys[0] == 'G')
houses = 37;
else
houses = 13;
for(i = 0; i < houses; i++)
add_index_double(cusps_arr, i, cusps[i]);
/*******************************/
/* removed for php 7 */
/* MAKE_STD_ZVAL(ascmc_arr); */
/*******************************/
ZVAL_NEW_ARR(ascmc_arr);
array_init(ascmc_arr);
for(i = 0; i < 10; i++)
add_index_double(ascmc_arr, i, ascmc[i]);
add_assoc_zval(return_value, "cusps", cusps_arr);
add_assoc_zval(return_value, "ascmc", ascmc_arr);
add_assoc_long(return_value, "rc", rc);
}
But on compilling it gives me the following errors:
/home/hermes/php-sweph/latest/php-sweph/sweph.c:926:42: error:
incompatible type for argument 4 of ‘swe_houses’
rc = swe_houses(tjd_ut, geolat, geolon, hsys[0], cusps, ascmc);
^
In file included from /home/hermes/php-sweph/latest/php-
sweph/sweph.c:23:0:
/usr/local/include/swephexp.h:742:16: note: expected ‘int’ but argument is
of type ‘zend_string {aka struct _zend_string}’
ext_def( int ) swe_houses(
^
/home/hermes/php-sweph/latest/php-sweph/sweph.c:939:14: error: invalid
operands to binary == (have ‘zend_string {aka struct _zend_string}’ and
‘int’)
if (hsys[0] == 'G')
First things first, zend_string is not at all like a char* or char[], therefore you cannot access elements simply by referencing an index. It's a struct not an array.
This is what zend_string is:
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
What I would do is change hsys back to char* so you can use the regular string functions and reference the elements like an array.
Here is an example of what I think will work and I will comment what I changed about it.
PHP_FUNCTION(swe_houses) {
char *arg = NULL; /* char */
size_t hsys_len, rc;
char *hsys = NULL; /* char */
double tjd_ut, geolat, geolon;
double cusps[37], ascmc[10];
size_t i, houses;
zval cusps_arr, ascmc_arr; /* removed pointer */
if(ZEND_NUM_ARGS() != 4) WRONG_PARAM_COUNT;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ddds",
&tjd_ut, &geolat, &geolon, &hsys, &hsys_len) == FAILURE) {
return;
}
if (hsys_len < 1)
return;
rc = swe_houses(tjd_ut, geolat, geolon, hsys[0], cusps, ascmc);
/* create 2 index array, and 1 assoc array */
array_init(return_value);
/* ZVAL_NEW_ARR(cusps_arr); unneeded */
array_init(&cusps_arr); /* added & */
if (hsys[0] == 'G')
houses = 37;
else
houses = 13;
for(i = 0; i < houses; i++)
add_index_double(&cusps_arr, i, cusps[i]); /* added & */
/* ZVAL_NEW_ARR(ascmc_arr); unneeded */
array_init(&ascmc_arr); /* added & */
for(i = 0; i < 10; i++)
add_index_double(&ascmc_arr, i, ascmc[i]); /* added & */
/* this may cause issues, not sure though */
add_assoc_zval(return_value, "cusps", &cusps_arr); /* added & */
add_assoc_zval(return_value, "ascmc", &ascmc_arr); /* added & */
add_assoc_long(return_value, "rc", rc);
}
I have been trying to figure out why one of my variables attaches some garbage value the second time I make a call in gdb but cannot figure it out. I have diagnosed the problem but don't know how to fix it.
On the first call of "curl http://localhost:8080/cat.html", here is what the variables return:
temp = 0x7fffffff9c74 "/cat.html HTTP/1.1\r\n"
request_target = 0x607080 "/cat.html"
abs_path = 0x7fffffffbdd0 "/cat.html"
query = 0x7fffffff9dd0 ""
This works fine and the terminal does show the html file. However, the second time I make the call is where the problem arises.
On the second call of the same address, the following variables are returned:
temp = 0x7fffffff9c74 "/cat.html HTTP/1.1\r\n"
request_target = 0x607080 "/cat.html\327\254\367\377\177"
abs_path = 0x7fffffffbdd0 "/cat.html\327\254\367\377\177"
query = 0x7fffffff9dd0 ""
And then I will receive the 404 file not found error.
Does anybody know why and where I am accumulating all these random characters ("\327\254\367\377\177")? I do believe that it is coming from the temp variable, but I have tried for hours to fix it but I still haven't been able to.
Below are my "parse" and "load" functions:
bool parse(const char* line, char* abs_path, char* query)
{
// GET /cat.html?name="Alice" HTTP/1.1
if (strncmp(line, "GET ", 4) != 0)
{
error(405);
return false;
}
else if (line[4] != '/')
{
error(501);
return false;
}
else if (strncmp(strrchr(line, ' '), " HTTP/1.1", 9) != 0)
{
error(505);
return false;
}
char* temp = strchr(line, '/');
int after_length = strlen(strrchr(line, ' '));
char* request_target = malloc(strlen(temp) - after_length);
strncpy(request_target, temp, strlen(temp) - after_length);
if ((strchr(request_target, '"') != NULL || strchr(request_target, ' ') != NULL))
{
error(400);
return false;
}
if (strchr(request_target, '?') == NULL)
{
strcpy(abs_path, request_target);
strcpy(query, "");
}
else
{
int q_length = strlen(strchr(request_target, '?'));
if (q_length != 2)
{
strncpy(abs_path, request_target, strlen(request_target) - q_length);
strcpy(query, &strchr(request_target, '?')[1]);
}
else
{
strncpy(abs_path, request_target, strlen(request_target) - 2);
strcpy(query, "");
}
}
free(request_target);
return true;
}
bool load(FILE* file, BYTE** content, size_t* length)
{
if (file == NULL)
{
error(500);
return false;
}
*content = NULL;
*length = 0;
BYTE buffer[BYTES];
ssize_t bytes = fread(buffer, 1, sizeof(buffer), file);
while (bytes != 0)
{
*content = realloc(*content, *length + bytes);
memcpy(*content + *length, buffer, bytes);
*length += bytes;
bytes = fread(buffer, 1, sizeof(buffer), file);
}
return true;
}
Our distributed database have php client which is developed on php extension.
We store our database object in persistant_list.
Problem:
From the log we find, for the same process, sometimes it can't find the database object from persistent_list, have to init the db object in persistant_list, but later(probably 1s)it can't find the same key-value again. It seems the value in persistent list is destroyed. Based on my poor knowledge about php, the values in persistent_list only destroyed by zend_hash_del or web server down. Source code:
if (zend_hash_find(&EG(persistent_list), hash_key, hash_key_len+1, (void **) &le) == FAILURE) {
tc = tair_init();
last_rst = tair_startup(tc,uri);
if(last_rst != TAIR_RETURN_SUCCESS){
return -1;
}
zend_rsrc_list_entry new_le;
new_le.type = le_tair;
new_le.ptr = tc;
/* register new persistent connection */
if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
tair_deinit(tc);
tc = NULL;
} else {
rsrc_id = zend_list_insert(tc,le_tair);
}
}else if (le->type != le_tair || le->ptr == NULL) {
zend_hash_del(&EG(persistent_list), hash_key, hash_key_len+1);
tc = tair_init();
last_rst = tair_startup(tc,uri);
if(last_rst != TAIR_RETURN_SUCCESS){
return -1;
}
zend_rsrc_list_entry new_le;
new_le.type = le_tair;
new_le.ptr = tc;
if (zend_hash_update(&EG(persistent_list), hash_key, hash_key_len+1, (void *) &new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE) {
tair_deinit(tc);
tc = NULL;
} else {
rsrc_id = zend_list_insert(tc,le_tair);
}
}else {
tc = (tair_handler)le->ptr;
rsrc_id = zend_list_insert(tc,le_tair);
}
PHP_MINIT_FUNCTION(tair)
{
REGISTER_INI_ENTRIES();
tair_set_loglevel(tair_globals.log_level);
le_tair = zend_register_list_destructors_ex(NULL,tair_dtor,"Tair session", module_number);
return SUCCESS;
}
Can anyone tell me what's wrong with my php zend engine? Btw the client side use Nginx+fpm.
What is the best solution to get the $_POST data from multiple checkboxes that have the same name attribute, WITHOUT using something like this;
<input type="checkbox" name="some_value[]">
<input type="checkbox" name="some_value[]">
I'm using Unbounce to make a landing page, and they currently don't offer any way of setting the name attribute to something custom including the '[]' to denote to PHP to put the values in an array.
You can read the POST data yourself using something like:
$formData = file_get_contents('php://input');
However, for parsing the "application/x-www-form-urlencoded", you'll want to find a third-party library somewhere, as all of the native PHP options exhibit the same behaviour (later keys override earlier ones) that you would find with the normal $_POST structure.
Here's a "toy" implementation of a user-land version of parse_str(), with the added bonus that 'duplicate' values are turned into an array. I make no claims as to the quality of this code to handle all the possible edge-cases of "application/x-www-form-urlencoded" data:
<?php
$form = file_get_contents('php://input');
$arg_sep = ini_get('arg_separator.input');
$max = ini_get('max_input_vars');
$token = strtok($form, $arg_sep);
$data = [];
while (false !== $token && $processed < $max) {
if (false !== ($pos = strpos($token, '='))) {
list($key, $value) = explode('=', $token);
$value = urldecode($value);
if (strlen($key)) {
if (isset($data[$key])) {
if (is_array($data[$key])) {
array_push($data[$key], $value);
} else {
$data[$key] = [$data[$key], $value];
}
} else {
$data[$key] = $value;
}
}
}
$token = strtok($arg_sep);
++$processed;
}
var_dump($data);
For comparison, here are the guts of PHP's internal implementation - note that there is much more to it than this, but this is the heart of the key/value parsing logic:
switch (arg) {
case PARSE_GET:
case PARSE_STRING:
separator = (char *) estrdup(PG(arg_separator).input);
break;
case PARSE_COOKIE:
separator = ";\0";
break;
}
var = php_strtok_r(res, separator, &strtok_buf);
while (var) {
val = strchr(var, '=');
if (arg == PARSE_COOKIE) {
/* Remove leading spaces from cookie names, needed for multi-cookie header where ; can be followed by a space */
while (isspace(*var)) {
var++;
}
if (var == val || *var == '\0') {
goto next_cookie;
}
}
if (++count > PG(max_input_vars)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded " ZEND_LONG_FMT ". To increase the limit change max_input_vars in php.ini.", PG(max_input_vars));
break;
}
if (val) { /* have a value */
size_t val_len;
size_t new_val_len;
*val++ = '\0';
php_url_decode(var, strlen(var));
val_len = php_url_decode(val, strlen(val));
val = estrndup(val, val_len);
if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) {
php_register_variable_safe(var, val, new_val_len, &array TSRMLS_CC);
}
efree(val);
} else {
size_t val_len;
size_t new_val_len;
php_url_decode(var, strlen(var));
val_len = 0;
val = estrndup("", val_len);
if (sapi_module.input_filter(arg, var, &val, val_len, &new_val_len TSRMLS_CC)) {
php_register_variable_safe(var, val, new_val_len, &array TSRMLS_CC);
}
efree(val);
}
next_cookie:
var = php_strtok_r(NULL, separator, &strtok_buf);
}
I am creating a PHP extension in C to access the SPI interface. So far I have gotten pretty much everything working: php_spi on Github
However, I cannot seem to make the $options parameter in the constructor optional. My working code is like this:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lla", &bus, &chipselect, &options) == FAILURE) {
return;
}
_this_zval = getThis();
_this_ce = Z_OBJCE_P(_this_zval);
options_hash = HASH_OF(options);
char device[32];
sprintf(device, "/dev/spidev%d.%d", bus, chipselect);
// If the device doesn't exists, error!
if(access(device, F_OK) == -1) {
char error[128];
sprintf(error, "The device %s does not exist", device);
php_error(E_ERROR, error);
}
// If we can't open it, error!
long fd = open(device, O_RDWR);
if (fd < 0) {
char error[128];
sprintf(error, "Could not open %s for read/write operations, are you running as root?", device);
php_error(E_ERROR, error);
}
// Set the file descriptor as a class property
zend_update_property_long(_this_ce, _this_zval, "device", 6, fd TSRMLS_DC);
// Default property values
uint8_t mode = SPI_MODE_0;
uint8_t bits = 8;
uint32_t speed = 500000;
uint16_t delay = 0;
// Loop through the options array
zval **data;
for(zend_hash_internal_pointer_reset(options_hash);
zend_hash_get_current_data(options_hash, (void **)&data) == SUCCESS;
zend_hash_move_forward(options_hash)) {
char *key;
int len;
long index;
long value = Z_LVAL_PP(data);
if(zend_hash_get_current_key_ex(options_hash, &key, &len, &index, 1, NULL) == HASH_KEY_IS_STRING) {
// Assign the value accordingly
if(strncmp("mode", key, len) == 0) {
switch(value) {
case SPI_MODE_1:
mode = SPI_MODE_1;
break;
case SPI_MODE_2:
mode = SPI_MODE_2;
break;
case SPI_MODE_3:
mode = SPI_MODE_3;
break;
default:
mode = SPI_MODE_0;
break;
}
}
else if(strncmp("bits", key, len) == 0) {
bits = value;
}
else if(strncmp("speed", key, len) == 0) {
speed = value;
}
else if(strncmp("delay", key, len) == 0) {
delay = value;
}
}
}
However, if I follow the suggestions of all the documentation I can find and add a pipe between the l and the a, like so:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|a", &bus, &chipselect, &options) == FAILURE) {
Then my extension silently fails - can anyone offer me any advice?
Assuming options is a zval*, if you do this:
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|a", &bus, &chipselect, &options) == FAILURE) {
return;
}
...if options is not passed (that is, you omit the third optional argument), options will not be initialized or modified. Later, you do this:
options_hash = HASH_OF(options);
Therefore, you're using either an uninitialized pointer, or a NULL pointer, which is undefined behavior. This is most likely causing a segmentation fault, causing your PHP script to fail.
What you should do is something like:
zval* options = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|a", &bus, &chipselect, &options) == FAILURE) {
return;
}
// ...
if (options != NULL) {
options_hash = HASH_OF(options);
}
...and handle every instance of options (and options_hash) with a condition that checks if it's NULL or not.