使用zend(多维数组)交换数据

ain*_*ain 22 delphi php-extension delphi-2010 php-internals php4delphi

我在我的应用程序中嵌入PHP(在Delphi 2010中编写),使用PHP4Delphi组件与php5ts.dll连接.我想我的程序充当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, nil, 0);  
   // populate subarray with data, including other subarrays
   ...
   // add subarray to the main array
   add_index_zval(return_value, x, subArray);
end;
Run Code Online (Sandbox Code Playgroud)

我是否必须"注册"我创建的子阵列?我必须增加或减少refcount或设置is_ref?IOW,如何设置子数组的return_value和zvals?
我尝试在每个数组的refcount中添加1(虽然MAKE_STD_ZVAL已经将refcount初始化为1)并且治愈AV但是有时应用程序只是在执行脚本时消失 - 我怀疑它会导致引擎的记忆管理器无限递归,崩溃php DLL和用它来获取应用程序...当将refcount设置为0(零;假设在PHP脚本中分配返回值时,它的引用计数将为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);
Run Code Online (Sandbox Code Playgroud)

或者循环的每次迭代是否应该创建/释放Val?

TIA
ain

Per*_*nyk 1

由于你的问题很长,我将把我的答案分成几个部分。

  1. PHP4Delphi 组件充当 SAPI 模块。ISAPI SAPI 模块被用作它的原型
  2. 您使用什么版本的 PHP4Delphi?在我的副本中,调用 tsrm_shutdown(); 位于 1509 行,而不是 1497 行
  3. 我建议按以下方式读取数组:
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;
Run Code Online (Sandbox Code Playgroud)
  1. 有些人报告了数组整数索引的问题。我建议将索引更改为字符串:
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;
Run Code Online (Sandbox Code Playgroud)
  1. tsrm_shutdown 中的错误通常与内存管理和 Delphi 字符串有关。php5ts.dll 有内置内存管理器,它独立于 Delphi 内存管理器工作。从 Delphi 的角度来看,当字符串引用等于 0 时,它可以被释放,但同时它仍然可以被 PHP 引擎使用。如果用字符串填充子数组,请确保 Delphi 内存管理器不会收集这些字符串。例如,您可以在将字符串添加到数组之前将其转换为 PAnsiChar
{$IFNDEF COMPILER_VC9}
fnc^.internal_function.function_name := strdup(method_name);
{$ELSE}
fnc^.internal_function.function_name := DupStr(method_name);
{$ENDIF}
Run Code Online (Sandbox Code Playgroud)