Interchanging data with zend (multidimensional arrays)
Asked Answered
M

2

22

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.
Stack list
(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 is

var 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 experimented with adding 1 to each array's refcount (althought MAKE_STD_ZVAL already initializes refcount to 1) and that cures the AV but then sometimes app just disappears when executing the script - I suspect it causes infinite recursion in engine's memeory manager, crashing the php DLL and taking the app with it... When seting refcount to 0 (zero; assuming that when return value is assigned in the PHP script it's refcount will be 1 and then when the PHP variable goes out of scope it will be destroyed) all seems to work (ie no crash, no AV) but the script wont generate any output, just empty html file...

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?
And is it safe to reuse same variable when iterating over an array, ie

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
ain

Marchpast answered 25/4, 2011 at 12:36 Comment(10)
After some experimenting I noticed strange(?) thing - when calling my function with "direct value" like myFnc(array(array('test'))); all zvals do have refcount 1, however, when calling like $tmp = array(array('test')); myFnc($tmp); then the zvals at the 2 level (array('test') in this case) do have refcount 2, while all other levels have refcount 1. Using similar refcounts for my return value the AV in original post won't happen, but occasionally app just disappears without any error message... still loos like some sort of mem managing problem, just can't put my finger on it! Any ideas?Marchpast
What does it matter, is it open or is it closed? Honest question, I'm relatively new here.Marchpast
Sam, did you actually read my post or did you just counted the question marks? Yes, I want to fix the AV - thats basically what prevents me to use multi-dimensional arrays. I worry about the mem managment as I suspect this is the root cause of the problem. I mention sending data in in arrays as I suspected that this might be part of the problem, ie I dont access them correctly and thus screw up the mem manager which in turn causes problems with the array my function returns (I'm now pretty sure this is not the case).Oh,and the suggestion to close the question is now gone-deleted by the author?Marchpast
Sorry, Sam, but it appears that you haven't read my OP - the very first paragraph ansvers your questions. The extension function is written by me in Delphi, so of course I do have source code and can modify it. The name and definition of the function are irrelevant as all extension functions do have same signature. The DLL I refer to (php5ts.dll) is the PHP engine I try to use/embed (it's from standard distribution, not custom build). Sam, thanks for trying to help but it seems to me that you are not familiar with the topic at hand...Marchpast
Sam, why are you so desperate to teach me? You don't like my post, you downvoted, and thats should be it, just leave it at that. And btw, it has been 4 months, not 6, since I posted the question.Marchpast
I just don't want to see it in "Delphi unanswered questions", because I already looked at it LOL, talk about ego :) and answered it answer ought to be useful, not just something to get the post out of unanswered list. I believe 6 months is enough time again, check your math, it's 4 months!Marchpast
Sam, Your main concern seems to be that someone might upvote the post you don't like. Get over it, it's not like youre loosing something because of that, is it?! And please point me to the FAQ or SO rule which says that unanswered questions must be deleted. I believe that the question might provide useful information for those who have similar problem and thus I may leave it as is. And btw there is many unanswered questions which are older than mine, why don't you harass those?Marchpast
Hi... just got to this post. I am curious... Did you tried any other memory manager? Or enabling full debug of FastMM and look if it gives you more information?Megargee
@Megargee No for both questions. The feature isn't must have for me so I have stopped active investigation for now... I'd still would like to get it to work but currently it is pretty low on todo list.Marchpast
Hey ain, the whole EU went bust because nobody could solve your bug/"question"! (;-D) I hear Google got together with Microsoft and even the Chinese military tried their best but nobody could solve the mysterious ain bug! What a funny joke! (;-D)Bathyal
P
2

here 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.

Peeve answered 12/1, 2012 at 12:6 Comment(0)
E
1

As your question quite long I will split my answer in few parts.

  1. PHP4Delphi component acts as SAPI module. The ISAPI SAPI module was used as a prototype for it
  2. What version of PHP4Delphi are you using? In my copy the call to tsrm_shutdown(); is located on line 1509, not 1497
  3. 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;
  1. 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;
  1. 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}
Elboa answered 27/9, 2011 at 21:8 Comment(2)
Perevoznyk, thanks for the answer and sorry for the delay before reacting - I'm really busy right now with other stuff...Marchpast
The php4delphi.pas is v 7.2 10/2009 but I have changed it (both added functionality and just reformated to be more readable to me). I do use both str and int indices with arrays when I send data back to PHP. Usually I have "root level" indices as ints and then the "leaf arrays" have string indices... what problems have there been with int indices? Would it be OK to use StrToInt() at Delphi side for indices or must it be "pure string" to be on the safe side? When sending string to PHP I use the version of API calls which take "dublicate" parameter, so I should be OK in that respect.Marchpast

© 2022 - 2024 — McMap. All rights reserved.