22

我在我的应用程序中嵌入 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;

我是否必须在某个地方“注册”我创建的子数组?我必须增加或减少refcount或设置is_ref吗?IOW,必须如何设置子数组的 return_value 和 zvals?
我尝试将 1 添加到每个数组的 refcount(虽然 MAKE_STD_ZVAL 已经将 refcount 初始化为 1)并且可以治愈 AV 但有时应用程序在执行脚本时会消失 - 我怀疑它会导致引擎的内存管理器中的无限递归,导致 php DLL 和随身携带应用程序...当将 refcount 设置为 0(零;假设在 PHP 脚本中分配返回值时,它的 refcount 将为 1,然后当 PHP 变量超出范围时,它将被销毁)似乎工作(即没有崩溃,没有 AV)但脚本不会生成任何输出,只是空的 html 文件......

我还将数据作为数组发送到我的函数中,然后使用zend_hash_findzend_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
艾因

4

2 回答 2

2

这是我的工作:

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 为我解决了这个问题,我不知道为什么,只是尝试了很多次,才发现这个。

于 2012-01-12T12:06:32.917 回答
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;
  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;
  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}
于 2011-09-27T21:08:01.263 回答