与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_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 是的
【问题讨论】:
经过一些实验后,我注意到了奇怪的(?)事情 - 当使用像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.pas
是 v 7.2 10/2009
,但我已经更改了它(都添加了功能并且只是重新格式化以对我来说更具可读性)。当我将数据发送回 PHP 时,我确实将 str 和 int 索引与数组一起使用。通常我有“根级”索引作为整数,然后“叶数组”有字符串索引……整数索引有什么问题?可以在 Delphi 端使用 StrToInt()
进行索引还是为了安全起见必须是“纯字符串”?向 PHP 发送字符串时,我使用带有“duplicate”参数的 API 调用版本,所以在这方面我应该没问题。以上是关于与zend(多维数组)交换数据的主要内容,如果未能解决你的问题,请参考以下文章