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

Posted

技术标签:

【中文标题】与zend(多维数组)交换数据【英文标题】:Interchanging data with zend (multidimensional arrays) 【发布时间】:2011-08-12 07:28:25 【问题描述】:

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

【问题讨论】:

经过一些实验后,我注意到了奇怪的(?)事情 - 当使用像 myFnc(array(array('test'))); 这样的“直接值”调用我的函数时,所有 zval 确实有 refcount 1,但是,当像 $tmp = array(array('test')); myFnc($tmp); 这样调用时,zvals在第 2 级(本例中为数组('test'))确实有 refcount 2,而所有其他级别都有 refcount 1。使用类似的 refcounts 作为我的返回值,原始帖子中的 AV 不会发生,但偶尔 app 会消失没有任何错误消息...仍然像某种内存管理问题一样,只是无法解决!有什么想法吗? 有什么关系,是打开还是关闭?诚实的问题,我在这里比较新。 Sam,你真的读过我的帖子还是只是数了问号?是的,我想修复 AV - 这基本上就是阻止我使用多维数组的原因。我担心内存管理,因为我怀疑这是问题的根本原因。我提到在数组中发送数据,因为我怀疑这可能是问题的一部分,即我没有正确访问它们,因此搞砸了内存管理器,这反过来又导致我的函数返回的数组出现问题(我现在很确定事实并非如此)。哦,关闭问题的建议现在已经被作者删除了? 对不起,Sam,但您似乎还没有阅读我的 OP - 第一段回答了您的问题。扩展功能是我用Delphi写的,当然有源代码可以修改。函数的名称和定义无关紧要,因为所有扩展函数都具有相同的签名。我引用的 DLL (php5ts.dll) 是我尝试使用/嵌入的 PHP 引擎(它来自标准发行版,而不是自定义构建)。山姆,感谢您的帮助,但在我看来,您对手头的主题并不熟悉... @EMBarbosa 两个问题都没有。这个功能对我来说不是必须的,所以我现在已经停止了积极的调查......我仍然想让它工作,但目前它在待办事项列表中的位置很低。 【参考方案1】:

这是我的工作:

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

【讨论】:

【参考方案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, 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;
    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

【讨论】:

Perevoznyk,感谢您的回答,并为延迟做出反应感到抱歉 - 我现在真的很忙其他事情...... php4delphi.pasv 7.2 10/2009,但我已经更改了它(都添加了功能并且只是重新格式化以对我来说更具可读性)。当我将数据发送回 PHP 时,我确实将 str 和 int 索引与数组一起使用。通常我有“根级”索引作为整数,然后“叶数组”有字符串索引……整数索引有什么问题?可以在 Delphi 端使用 StrToInt() 进行索引还是为了安全起见必须是“纯字符串”?向 PHP 发送字符串时,我使用带有“duplicate”参数的 API 调用版本,所以在这方面我应该没问题。

以上是关于与zend(多维数组)交换数据的主要内容,如果未能解决你的问题,请参考以下文章

numpy ndarray 交换多维数组(矩阵)的行/列

客户端通过HTTP协议与服务端交换数据

多维数组与多个数组

noi题库(noi.openjudge.cn) 1.8编程基础之多维数组T1——T10

JS----使用归递获取多维数组中的某列值

Laravel sync() 与多维数组