与zend交换数据多维数组

问题描述

|| 我正在使用PHP4Delphi组件与PHP5ts.dll交互,将PHP嵌入我的应用程序(用Delphi 2010编写)中。 我猜我的程序充当了PHP(sapi模块?)的扩展,因为它注册了一些可以在PHP脚本中使用的函数和常量...无论如何,在使用简单数据类型时效果很好,但是当我尝试使用多维数组时作为返回值我得到错误
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
PHP4delphi.pas中的1497行被称为
tsrm_shutdown();
在我看来,垃圾收集器在脚本末尾崩溃了,所以我怀疑我没有正确地将数据发送回引擎... 因此,我的问题是应该如何将多维数组发送回PHP? 我正在使用的模式是
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,0);  
   // populate subarray with data,including other subarrays
   ...
   // add subarray to the main array
   add_index_zval(return_value,x,subArray);
end;
我是否必须在某个地方“注册”我创建的子数组?我是否必须增加或减少
refcount
或设置
is_ref
? IOW,必须如何设置子数组的return_value和zvals? 我尝试对每个数组的refcount加1(尽管MAKE_STD_ZVAL已经将refcount初始化为1),并且可以治愈AV,但是有时执行脚本时应用只是消失了-我怀疑它会导致引擎的内存管理器无限递归,崩溃了PHP DLL,并带走了应用程序... 当将refcount设置为0(零;假设在PHP脚本中分配了返回值时,它的refcount将为1,然后当PHP变量超出范围时它将被销毁),所有方法似乎都可以正常工作(即,不崩溃,没有AV),但脚本不会生成任何输出,只是空的html文件... 我也将数据作为数组发送到函数中,然后使用
zend_hash_find
zend_hash_get_current_data
等读取数据。这会搞乱变量的引用吗?即我完成处理后,必须减少
zend_hash_find
返回的变量的refcout吗? 并且在遍历数组时重用相同的变量是否安全,即
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);
还是应该在循环的每次迭代中创建/释放Val? TIA 爱因     

解决方法

这是我的工作范围:
function InitSubArray(TSRMLS_DC : pointer):pzval;
begin
  Result := MAKE_STD_ZVAL;
  Result^.refcount:=2;
  Result^._type:=IS_ARRAY;
  InitPHPArray(Result,TSRMLS_DC);
end;
将refcount设置为2可以为我解决问题,我不知道为什么,只是尝试了很多次,才发现了这一点。     ,由于您的问题很长,我将把答案分成几个部分。 PHP4Delphi组件充当SAPI模块。 ISAPI SAPI模块被用作其原型 您正在使用哪个版本的PHP4Delphi?在我的副本中,调用tsrm_shutdown();位于行1509,而不是1497 我建议以以下方式读取数组:
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;
有人报告了有关数组整数索引的问题。我建议将索引更改为字符串:
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,0);
  _array_init(smonths,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;
tsrm_shutdown中的错误通常与内存管理和Delphi字符串有关。 php5ts.dll具有内置的内存管理器,它独立于Delphi内存管理器工作。从Delphi的角度来看,当字符串引用等于零时,可以将其释放,但与此同时,PHP引擎仍可以使用它。 如果用字符串填充子数组,请确保Delphi内存管理器未收集字符串。例如,您可以在将字符串添加到数组之前将其转换为PAnsiChar
{$IFNDEF COMPILER_VC9}
fnc^.internal_function.function_name := strdup(method_name);
{$ELSE}
fnc^.internal_function.function_name := DupStr(method_name);
{$ENDIF}