Passing a variable by reference into a PHP extension - 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).

Related

PHP FFI - return array rom Rust function back to PHP

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
}

Segfault Error when using zend_call_function()

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

Getting function argument value from PHP extension in c++

I have created php extension in c++.In version php 5.6 can able to get currently executed function details. I was getting the arguments value as follows,
if (real_execute_data->function_state.arguments)
{
void **p = real_execute_data->function_state.arguments;
int arg_count = (int)(zend_uintptr_t)* p;
zval *argument_element;
for (i = 0; i < arg_count; i++)
{
argument_element = (zval*)*(p - (arg_count - i));
// here can reads the value from argument_element
}
}
In version Php 7.2,I can't find the function_state structure inside of Zend_execute_data.I tried with _zend_arg_info structure, it gives function argument variable names,not the values.
How can i get the function arguments value in php 7 above?
Yes. I got it.
We can get the function arguments in php 7 and above as follows(zend_execute_data *real_execute_data),
int arg_count = ZEND_CALL_NUM_ARGS(real_execute_data);
int i;
for (i = 1; i <= arg_count; i++)
{
zval *argument_element = ZEND_CALL_ARG(real_execute_data, i);
// we can reads the argument value from "argument_element".
}

Using R, how to reference variable variables (or variables variable) a la PHP [revisited]

In a previous Using R, how to reference variable variables (or variables variable) a la PHP[post]
I asked a question about something in R analagous to PHP $$ function:
Using R stats, I want to access a variable variable scenario similar to PHP double-dollar-sign technique: http://php.net/manual/en/language.variables.variable.php
Specifically, I am looking for a function in R that is equivalent to $$ in PHP.
The get( response works for strings (characters).
lapply is a way to loop over lists
Or I can loop over and get the values ...
for(name in names(vars))
{
val = vars[[name]];
I still haven't had the $$ function in R answered, although the lapply solved what I needed in the moment.
`$$` <- function
that allows any variable type to be evaluated. That is still the question.
UPDATES
> mlist = list('four'="score", 'seven'="years");
> str = 'mlist$four'
> mlist
$four
[1] "score"
$seven
[1] "years"
> str
[1] "mlist$four"
> get(str)
Error in get(str) : object 'mlist$four' not found
> mlist$four
[1] "score"
Or how about attributes for an object such as mobj#index
UPDATES #2
So let's put specific context on the need. I was hacking the texreg package to build a custom latex output of 24 models of regression for a research paper. I am using plm fixed effects, and the default output of texreg uses dcolumns to center, which I don't like (I prefer r#{}l, so I wanted to write my own template. The purpose for me, to code this, is for me to write extensible code that I can use again and again. I can rebuild my 24 tables across 4 pages in seconds, so if the data change, or if I want to tweak the function, I immediately have a nice answer. The power of abstraction.
As I hacked this, I wanted to get more than the number of observations, but also the number of groups, which can be any user defined index. In my case it is "country" (wait for it, hence, the need for variable variables).
If I do a lookup of the structure, what I want is right there: model$model#index$country which would be nice to simply call as $$('model$model#index$country'); where I can easily build the string using paste. Nope, this is my workaround.
getIndexCount = function(model,key="country")
{
myA = attr(summary(model)$model,"index");
for(i in 1:length(colnames(myA)))
{
if(colnames(myA)[i] == key) {idx = i; break;}
}
if(!is.na(idx))
{
length(unique(myA[,idx]));
} else {
FALSE;
}
}
UPDATES #3
Using R, on the command line, I can type in a string and it gets evaluated. Why can't that internal function be directly accessed, and the element captured that then gets printed to the screen?
There is no equivalent function in R. get() works for all types, not just strings.
Here is what I came up with, after chatting with the R-bug group, and getting some ideas from them. KUDOS!
`$$` <- function(str)
{
E = unlist( strsplit(as.character(str),"[#]") );
k = length(E);
if(k==1)
{
eval(parse(text=str));
} else {
# k = 2
nstr = paste("attributes(",E[1],")",sep="");
nstr = paste(nstr,'$',E[2],sep="");
if(k>2) {
for(i in 3:k)
{
nstr = paste("attributes(",nstr,")",sep="");
nstr = paste(nstr,'$',E[i],sep="");
}
}
`$$`(nstr);
}
}
Below are some example use cases, where I can directly access what the str(obj) is providing... Extending the utility of the '$' operator by also allowing '#' for attributes.
model = list("four" = "score", "seven"="years");
str = 'model$four';
result = `$$`(str);
print(result);
matrix = matrix(rnorm(1000), ncol=25);
str='matrix[1:5,8:10]';
result = `$$`(str);
print(result);
## Annette Dobson (1990) "An Introduction to Generalized Linear Models".
## Page 9: Plant Weight Data.
ctl <- c(4.17,5.58,5.18,6.11,4.50,4.61,5.17,4.53,5.33,5.14);
trt <- c(4.81,4.17,4.41,3.59,5.87,3.83,6.03,4.89,4.32,4.69);
group <- gl(2, 10, 20, labels = c("Ctl","Trt"));
weight <- c(ctl, trt);
lm.D9 <- lm(weight ~ group);
lm.D90 <- lm(weight ~ group - 1); # omitting intercept
myA = anova(lm.D9); myA; str(myA);
str = 'myA#heading';
result = `$$`(str);
print(result);
myS = summary(lm.D90); myS; str(myS);
str = 'myS$terms#factors';
result = `$$`(str);
print(result);
str = 'myS$terms#factors#dimnames';
result = `$$`(str);
print(result);
str = 'myS$terms#dataClasses#names';
result = `$$`(str);
print(result);
After realizing the back-tick can be a bit tedious, I chose to update the function, calling it access
access <- function(str)
{
E = unlist( strsplit(as.character(str),"[#]") );
k = length(E);
if(k==1)
{
eval(parse(text=str));
} else {
# k = 2
nstr = paste("attributes(",E[1],")",sep="");
nstr = paste(nstr,'$',E[2],sep="");
if(k>2) {
for(i in 3:k)
{
nstr = paste("attributes(",nstr,")",sep="");
nstr = paste(nstr,'$',E[i],sep="");
}
}
access(nstr);
}
}

PHP extension for Linux: reality check needed!

Okay, I've written my first functional PHP extension. It worked but it was a proof-of-concept only. Now I'm writing another one which actually does what the boss wants.
What I'd like to know, from all you PHP-heads out there, is whether this code makes sense. Have I got a good grasp of things like emalloc and the like, or is there stuff there that's going to turn around later and try to bite my hand off?
Below is the code for one of the functions. It returns a base64 of a string that has also been Blowfish encrypted. When the function is called, it is supplied with two strings, the text to encrypt and encode, and the key for the encryption phase. It's not using PHP's own base64 functions because, at this point, I don't know how to link to them. And it's not using PHP's own mcrypt functions for the same reason. Instead, it links in the SSLeay BF_ecb_encrypt functions.
PHP_FUNCTION(Blowfish_Base64_encode)
{
char *psData = NULL;
char *psKey = NULL;
int argc = ZEND_NUM_ARGS();
int psData_len;
int psKey_len;
char *Buffer = NULL;
char *pBuffer = NULL;
char *Encoded = NULL;
BF_KEY Context;
int i = 0;
unsigned char Block[ 8 ];
unsigned char * pBlock = Block;
char *plaintext;
int plaintext_len;
int cipher_len = 0;
if (zend_parse_parameters(argc TSRMLS_CC, "ss", &psData, &psData_len, &psKey, &psKey_len) == FAILURE)
return;
Buffer = (char *) emalloc( psData_len * 2 );
pBuffer = Buffer;
Encoded = (char *) emalloc( psData_len * 4 );
BF_set_key( &Context, psKey_len, psKey );
plaintext = psData;
plaintext_len = psData_len;
for (;;)
{
if (plaintext_len--)
{
Block[ i++ ] = *plaintext++;
if (i == 8 )
{
BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
pBuffer += 8;
cipher_len += 8;
memset( Block, 0, 8 );
i = 0;
}
} else {
BF_ecb_encrypt( Block, pBuffer, &Context, BF_ENCRYPT );
cipher_len += 8;
break;
}
}
b64_encode( Encoded, Buffer, cipher_len );
RETURN_STRINGL( Encoded, strlen( Encoded ), 0 );
}
You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?
If there are any other glaring errors, I'd really appreciate knowing.
emalloc() allocates memory per request, and it's free()'d automatically when the runtime ends.
You should, however, compile PHP with
--enable-debug --enable-maintainer-zts
It will tell you if anything goes wrong (it can detect memory leaks if you've used the e*() functions and report_memleaks is set in your php.ini).
And yes, you should efree() Buffer.
You'll notice that I have two emalloc calls, for Encoded and for Buffer. Only Encoded is passed back to the caller, so I'm concerned that Buffer won't be freed. Is that the case? Should I use malloc/free for Buffer?
Yes, you should free it with efree before returning.
Although PHP has safety net and memory allocated with emalloc will be freed at the end of the request, it's still a bug to leak memory and, depending you will warned if running a debug build with report_memleaks = On.

Categories