i am trying to warp a C library around a PHP class using PHP's Zend Engine. The constructor function of the class is supposed to take a string and a function pointer. I was able to fetch strings values from the __construct arguments and printing them. However when i fetch the function and try to make it run in the class's constructor. I get a segfault error.
Basically, the end result should like like so:
class TestClass
{
function __construct(callable: $callBack, string: $name);
}
(Naturally, there are other methods in the class, but i had no issue with them).
The C extension looks like so:
PHP_METHOD(TestClass, __construct)
{
zend_string* name;
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "fS", &fci, &fci_cache, &name) == FAILURE)
return;
php_printf("name: %s\n", ZSTR_VAL(name));
if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS) {
php_printf("Called");
}
zval* obj = getThis();
test_class_t* intern;
intern = Z_TSTOBJ_P(obj);
php_printf("Constructor\n");
zend_string_release(name);
if (fci.params)
efree(fci.params);
}
Segfault doesn't happen when zend_call_function is commented out.
I am using 7.2.19 and C99.
Is there a way to solve this issue?
Thanks
You have to specify a zval for the return value of the function before you can call it. You also have to specify the number of parameters you want to pass:
zval retval;
fci.retval = &retval;
fci.param_count = 0;
// Use zend_call_function()
zval_ptr_dtor(&retval);
To use parameters you have to allocate an array (stack or heap) of zvals to hold the values you want to pass. A simple example would be like this:
zval args[2];
ZVAL_LONG(&args[0], 1);
ZVAL_LONG(&args[1], 2);
fci.params = args;
fci.param_count = 2;
// Use zend_call_function() here.
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&args[1]);
In case of heap allocation:
zval *args = emalloc(2 * sizeof(zval));
// Assign, call and dtor as in previous example.
efree(args);
Related
I need to return few values from rust function. Tried to declare function which returns an array
$ffi = FFI::cdef('float get_arr()[2];', './target/release/libphp_rust.dylib');
$array = $ffi->get_arr();
But got an error:
PHP Fatal error: Uncaught FFI\ParserException: function returning array is not allowed at line 1 in /array.php:3
It seems PHP FFI can't work with arrays directly. So I found another solution.
I created C-array from PHP, then passed pointer to it to Rust code and then populated it with Rust function:
$ffi = FFI::cdef('bool get_arr(float (*res)[2]);', './target/release/libphp_rust.dylib');
$array = $ffi->new('float[2]');
$result = $ffi->get_arr(FFI::addr($array));
if ($result) {
var_dump($array);
} else {
//... something went wrong
}
#[no_mangle]
pub extern fn get_arr(array_pointer: *mut [f32;2]) -> bool {
let res = unsafe {
assert!(!array_pointer.is_null());
&mut *array_pointer
};
res[0] = 0.1;
res[1] = 0.2;
return true;
}
This solutions seems to work correct but i have some doubts about it:
Is passing pointers to FFI safe enough and what problems may I face with this in future?
Are Rust arrays fully C-compatible so that I'm able to assign value to it directly by index?
I there better way to achieve what I need? Or maybe are there some good practices about passing complex data structures with FFI?
Thanks
The rules surrounding this are still up in the air, so your example is questionably safe. This should be ok, but requires nightly features:
#![feature(maybe_uninit_extra)]
#![feature(ptr_as_uninit)]
// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
let fat: *mut [f32] = array_pointer;
let res = unsafe { fat.as_uninit_slice_mut().unwrap() };
res[0].write(0.1);
res[1].write(0.2);
true
}
On the stable channel it's just less elegant:
// Make sure you use `extern "C"`. `extern` alone means `extern "Rust"`.
#[no_mangle]
pub extern "C" fn get_arr(array_pointer: *mut [f32; 2]) -> bool {
assert!(!array_pointer.is_null());
unsafe {
let res = array_pointer as *mut f32;
res.add(0).write(0.1);
res.add(1).write(0.2);
}
true
}
I am a PHP programmer trying to learn more of the theory behind PHP, but having trouble connecting the dots between PHP and C. For example, is the arrow operator exactly the same in PHP and C?
Here's what I came up when I researched it:
In C, -> is just an alias, a->b is the same as (*a).b. The arrow operator is just dereferencing a pointer so you interact with the address variable.
In PHP, -> is a reference. It "references the attributes of an instantiated object" (Unknown). But is that the same thing as C?
Note: Today, I learned what pointers are in C.
In PHP, -> is used to access members of a class. C does not have classes.
The closest thing is a struct.
In PHP
class Animal {
public $color;
public $age;
}
$fido = new Animal;
$fido->color = 'white';
$fido->age = 3;
$kitty = new Animal;
$kitty->color = 'brown';
$kitty->age = 5;
// output
echo 'Fido is ' . $fido->color . "age=". $fido->age . "\n";
echo 'Kitty is ' . $kitty->color . "age=". $kitty->age . "\n";
Output is:
Fido is white age=3
Kitty is brown age=5
You can do something similar in C using structs. It's a bit more involved.
Excuse my C. It's quite rusty
struct Animal {
int age;
char color[50];
};
int size = sizeof(struct Animal);
struct Animal * fido = malloc(size);
struct Animal * kitty = malloc(size);
fido->age = 3;
strcpy(fido->color, "white");
kitty->age = 5;
strcpy(kitty->color, "brown");
printf("Fido is %s age=%d\n", fido->color, fido->age);
printf("Kitty is %s age=%d\n", kitty->color, fido->age);
Unless you really want to get into the underlying details, don't overthink PHP references. What that means is that they don't pass around the actual values when doing function calls etc.
Don’t try too hard to find equivalence between the two languages. Their semantics are simply too different, so this will fail.
That said, the dereference operator -> in PHP was likely chosen to visually resemble the member access operator -> in C, and the semantics are somewhat similar, in that both allow you to access a member of a dereferenced object.
I’m not sure what you mean by “In C, -> is just an alias”: The C language has a concept of “alias”, but it’s completely unrelated with the topic at hand.
Rather, -> is an operator, and the expression a->b is defined to be equivalen to (*a).b, as you said correctly. But unlike you said, the object doesn’t need to be allocated on the heap, it can be anywhere in memory. Consider the following:
struct foo {
int i;
};
int main(void) {
struct foo f = {42};
struct foo *pf = &f;
printf("f.i = %d\n", pf->i);
}
Here, pf->i is equivalent to f.i (or (*pf).i). In no case is i allocated on the heap.
In php arrow -> is used to access function of a class.
class A{
function funA(){
return "Hello World";
}
}
$object1 = new A();
$object1->funA;
Object will be
Hello World
You can also access nested objects by arrow operator in PHP.
We will convert string to object. Here is my string:
{
"id":"123456",
"msg":"Have a Nice Day",
"is_active":false,
"total_count":1
}
IF i encode it to JSON
$obj = json_decode($json, false);
I can easily get object value by -> operator
$obj->msg;
OutPut will be
Have a Nice Day
You can do similar in C by using structs.
I need to call a function within a PHP_FUNCTION() function in C to extend PHP, this is a multithread script and The function itself works perfectly using int main(). Here is what I try to achieve.
#define NUM_THREADS 3
char *messages[NUM_THREADS];
void *PrintHello(void *threadid)
{
zend_printf("gP");
int *id_ptr, taskid;
sleep(4);
id_ptr = (int *) threadid;
taskid = *id_ptr;
zend_printf("Thread %d: %s\n", taskid, messages[taskid]);
pthread_exit(NULL);
}
PHP_FUNCTION(hello_world)
{
pthread_t threads[NUM_THREADS];
int *taskids[NUM_THREADS];
int rc, t;
messages[0] = "English: Hello World!";
messages[1] = "French: Bonjour, le monde!";
messages[2] = "Spanish: Hola al mundo";
for(t=0; t < NUM_THREADS; t++)
{
taskids[t] = (int *) malloc(sizeof(int));
*taskids[t] = t;
zend_printf("Creating thread %d\n <br>", t);
rc = pthread_create(&threads[t], NULL, (void* (*) (void*)) pthreads_routine, (void *) taskids[t]);
if (rc) {
zend_printf("ERR; pthread_create() ret = %d\n", rc);
}
}
}
I need to call PrintHello() function from
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) taskids[t]);
Do I also need to register void *PrintHello(void *threadid) in
const zend_function_entry my_functions[] = {
PHP_FE(hello_world, NULL)
PHP_FE_END
};
the var_dump() out put is
Creating thread 0
Creating thread 1
Creating thread 2
NULL
at the top of void *PrintHello(void *threadid) function I have included zend_printf("gP"); line to make sure that the function is invoked and from the looks of out put it is obvious that the function is not invoked.
My environment is Mac OSX , xCode 7.2, PHP 7.0.1
What am I doing wrong?
It appears you have two problems in your code, both of which explain why you're not getting any output.
1) At least in my tests, it appears that zend_printf -> php_printf -> vspprintf is not thread safe. Your code always crashes for me once one of the threads attempts to call zend_printf(). But, even if that weren't the case, there's also:
2) Assuming your php code looks like this:
<?php
hello_world();
What's happening is that when you call pthread_create(), it returns immediately having created the thread, though the thread has not necessarily started to run. Then, hello_world returns once all the threads have been created. Then, your main thread ends because there's nothing else to do.
Once the main thread ends, your spawned threads are immediately terminated. If you're seeing nothing at all, that's because the main thread ends before any of the pthreads are actually scheduled, before they even execute your zend_printf("gP"); line.
If you change your php code to:
<?php
hello_world();
sleep(10);
Then you give the child threads enough time to be scheduled and given CPU time (at which point they will probably crash calling the first zend_printf), and if not, give them enough time to make it past the sleep(4) to get to the zend_printf(Thread id).
If you replace your PrintHello with this:
void *PrintHello(void *threadid)
{
int *id_ptr, taskid;
id_ptr = (int *) threadid;
taskid = *id_ptr;
printf("Thread %d: start\n", taskid, messages[taskid]);
sleep(4);
printf("Thread %d: %s\n", taskid, messages[taskid]);
pthread_exit(NULL);
}
(replace the zend_printf with regular printf), then you'll get your desired output, at least on the cli.
If I run an shm_get_var(), will it return a "reference", keeping the data in shared memory?
I'm wanting to keep an array about 50MB in size in shared memory so that it can be used by multiple processes without having to keep multiple copies of this 50MB array hanging around. If shared memory isn't the answer, does anyone have another idea?
This is the relevant C code snippet from sysvsem.c in PHP 5.2.9 :
/* setup string-variable and serialize */
/* get serialized variable from shared memory */
shm_varpos = php_check_shm_data((shm_list_ptr->ptr), key);
if (shm_varpos < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "variable key %ld doesn't exist", key);
RETURN_FALSE;
}
shm_var = (sysvshm_chunk*) ((char *)shm_list_ptr->ptr + shm_varpos);
shm_data = &shm_var->mem;
PHP_VAR_UNSERIALIZE_INIT(var_hash);
if (php_var_unserialize(&return_value, (const unsigned char **) &shm_data, shm_data + shm_var->length, &var_hash TSRMLS_CC) != 1) {
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "variable data in shared memory is corrupted");
RETURN_FALSE;
}
PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
PHP will have to unserialize the entire value every time you call shm_get, which, on a 50MB array, is going to be really really slow.
How about breaking it up into individual values?
Also you might want to consider using APC's variable cache, which will handle all of the shared memory and locking for you (and will also use a hash table for key lookups)
I'm no expert on this, but would it be possible to write a quick test for this something like the following?
$key = 1234;
//put something small into shared memory
$identifier = shm_attach($key, 1024, 0777);
shm_put_var($identifier, $key, 'shave and a hair cut');
$firstVar = shm_get_var($identifier, $key);
$firstVar .= 'Test String of Doom';
$secondVar = shm_get_var($identifier, $key);
if ($firstVar == $secondVar) {
echo 'shm_get_var passes by reference';
} else {
echo 'shm_get_var passes by value';
}
form the wording of the documentation
shm_get_var() returns the variable
with a given variable_key , in the
given shared memory segment. The
variable is still present in the
shared memory.
I would say yes it's a reference to the shared memory space.
you can use shm_remove()
Check this out: http://php.net/manual/en/function.shm-remove.php
I'm writing a PHP extension that takes a reference to a value and alters it. Example PHP:
$someVal = "input value";
TestPassRef($someVal);
// value now changed
What's the right approach?
Edit 2011-09-13:
The correct way to do this is to use the ZEND_BEGIN_ARG_INFO() family of macros - see Extending and Embedding PHP chapter 6 (Sara Golemon, Developer's Library).
This example function takes one string argument by value (due to the ZEND_ARG_PASS_INFO(0) call) and all others after that by reference (due to the second argument to ZEND_BEGIN_ARG_INFO being 1).
const int pass_rest_by_reference = 1;
const int pass_arg_by_reference = 0;
ZEND_BEGIN_ARG_INFO(AllButFirstArgByReference, pass_rest_by_reference)
ZEND_ARG_PASS_INFO(pass_arg_by_reference)
ZEND_END_ARG_INFO()
zend_function_entry my_functions[] = {
PHP_FE(TestPassRef, AllButFirstArgByReference)
};
PHP_FUNCTION(TestPassRef)
{
char *someString = NULL;
int lengthString = 0;
zval *pZVal = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &someString, &lengthString, &pZVal) == FAILURE)
{
return;
}
convert_to_null(pZVal); // Destroys the value that was passed in
ZVAL_STRING(pZVal, "some string that will replace the input", 1);
}
Before adding the convert_to_null it would leak memory on every call (I've not whether this is necessary after adding ZENG_ARG_INFO() calls).