与 zend 交换数据(多维数组)

发布于 2024-11-03 15:23:36 字数 1965 浏览 5 评论 0原文

我将 PHP 嵌入到我的应用程序中(用 Delphi 2010 编写),使用 PHP4Delphi 组件与 php5ts.dll 进行交互。 我想我的程序充当 PHP 的扩展(sapi 模块?),因为它注册了一些可以在 PHP 脚本中使用的函数和常量...无论如何,在使用简单数据类型时效果很好,但是当我尝试使用多维数组时作为返回值,我收到错误

模块“php5ts.dll”中地址 01CD3C35 处的访问冲突。读取地址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 添加到每个数组的引用计数(尽管 MAKE_STD_ZVAL 已经将引用计数初始化为 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
艾因

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

北城半夏 2024-11-10 15:23:37

这是我的工作安排:

function InitSubArray(TSRMLS_DC : pointer):pzval;
begin
  Result := MAKE_STD_ZVAL;
  Result^.refcount:=2;
  Result^._type:=IS_ARRAY;
  InitPHPArray(Result,TSRMLS_DC);
end;

将引用计数设置为 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.

許願樹丅啲祈禱 2024-11-10 15:23:37

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

  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}

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}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文