I'm embeding PHP in my app (writen in Delphi 2010), using PHP4Delphi component to interface with php5ts.dll. I guess my program acts as extension for the PHP (sapi module?) as it registers some functions and constants which can be used in PHP scripts... anyway, works well when using simple data types, but when I try to use multidimensional array as return value I get error
Access violation at address 01CD3C35 in module 'php5ts.dll'. Read of address 0231E608.
(000A2C35){php5ts.dll} [01CD3C35] destroy_op_array + $35
(004C4D61){myApp.exe } [008C5D61] php4delphi.TPHPEngine.ShutdownEngine (Line 1497, "php4delphi.pas" + 17) + $7
The line 1497 in php4delphi.pas is call to tsrm_shutdown();
To me it looks like garbage collector crashing at the end of the script, so I'm suspecting I do not send data correctly back to the engine... thus my question is how one is supposed to send multidimensional arrays back to PHP?
The pattern I'm using isvar subArray: pzval;
_array_init(return_value, nil, 0);
for x := 0 to Data.Count-1 do begin
subArray := MAKE_STD_ZVAL;
_array_init(subArray, nil, 0);
// populate subarray with data, including other subarrays
...
// add subarray to the main array
add_index_zval(return_value, x, subArray);
end;
Do I have to somewhere "register" the subarrays I create? Do I have to increase or decrease refcount
or set is_ref
? IOW, how must the return_value and zvals of the subarrays to be set up?
I also send data as arrays into my function, then use zend_hash_find
, zend_hash_get_current_data
etc to read the data. Could this mess up the refcounts of the variables? Ie do I have to decrease refcout of the variable returned by zend_hash_find
when I'm done with it?
var Val: pppzval;
new(Val);
zend_hash_internal_pointer_reset(aZendArr^.value.ht);
for x := 1 to zend_hash_num_elements(aZendArr^.value.ht) do begin
zend_hash_get_current_data(aZendArr^.value.ht, Val);
// read data from Val to local variable and do something with it
zend_hash_move_forward_ex(aZendArr^.value.ht, nil);
end;
Dispose(Val);
or should each iteration of the loop create / free Val?
TIA
ainhere is my work arround:
function InitSubArray(TSRMLS_DC : pointer):pzval;
begin
Result := MAKE_STD_ZVAL;
Result^.refcount:=2;
Result^._type:=IS_ARRAY;
InitPHPArray(Result,TSRMLS_DC);
end;
Set the refcount to 2 solve the problem for me, I don't know why, just tried many times, and found this.
As your question quite long I will split my answer in few parts.
- PHP4Delphi component acts as SAPI module. The ISAPI SAPI module was used as a prototype for it
- What version of PHP4Delphi are you using? In my copy the call to tsrm_shutdown(); is located on line 1509, not 1497
- I would propose to read the array in the following way:
procedure TForm1.ExecuteGetArray(Sender: TObject;
Parameters: TFunctionParams; var ReturnValue: Variant;
ZendVar: TZendVariable; TSRMLS_DC: Pointer);
var
ht : PHashTable;
data: ^ppzval;
cnt : integer;
variable : pzval;
tmp : ^ppzval;
begin
ht := GetSymbolsTable;
if Assigned(ht) then
begin
new(data);
if zend_hash_find(ht, 'ar', 3, data) = SUCCESS then
begin
variable := data^^;
if variable^._type = IS_ARRAY then
begin
SetLength(ar, zend_hash_num_elements(variable^.value.ht));
for cnt := 0 to zend_hash_num_elements(variable^.value.ht) -1 do
begin
new(tmp);
zend_hash_index_find(variable^.value.ht, cnt, tmp);
ar[cnt] := tmp^^^.value.str.val;
freemem(tmp);
end;
end;
end;
freemem(data);
end;
end;
- Some people reported about the problems with integer indexes for arrays. I would propose to change the index to string:
procedure TPHPExtension1.PHPExtension1Functions1Execute(Sender: TObject;
Parameters: TFunctionParams; var ReturnValue: Variant; ZendVar : TZendVariable;
TSRMLS_DC: Pointer);
var
pval : pzval;
cnt : integer;
months : pzval;
smonths : pzval;
begin
pval := ZendVar.AsZendVariable;
if _array_init(pval, nil, 0) = FAILURE then
begin
php_error_docref(nil , TSRMLS_DC, E_ERROR, 'Unable to initialize array');
ZVAL_FALSE(pval);
Exit;
end;
months := MAKE_STD_ZVAL;
smonths := MAKE_STD_ZVAL;
_array_init(months, nil, 0);
_array_init(smonths, nil, 0);
for cnt := 1 to 12 do
begin
add_next_index_string(months, PChar(LongMonthNames[cnt]), 1);
add_next_index_string(smonths, PChar(ShortMonthNames[cnt]), 1);
end;
add_assoc_zval_ex(pval, 'months', strlen('months') + 1, months);
add_assoc_zval_ex(pval, 'abbrevmonths', strlen('abbrevmonths') + 1, smonths);
end;
- The error in tsrm_shutdown is usually related to the memory management and Delphi strings. php5ts.dll has built-in memory manager and it works independent from the Delphi memory manager. When the string reference is equal to zero from the Delphi point of view it can be deallocated, but at the same time it can be still in use by the PHP engine. If you populate your subarrays with strings make sure the strings are not collected by the Delphi memory manager. For example you can convert the strings to PAnsiChar before adding it to the array
{$IFNDEF COMPILER_VC9}
fnc^.internal_function.function_name := strdup(method_name);
{$ELSE}
fnc^.internal_function.function_name := DupStr(method_name);
{$ENDIF}
精彩评论