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

时间:2011-04-25 12:36:24

标签: delphi delphi-2010 php-extension 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;

我是否必须“注册”我创建的子阵列?我是否必须增加或减少refcount或设置is_ref? IOW,如何设置子阵列的return_value和zvals? 我尝试在每个数组的refcount中添加1(虽然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
AIN

2 个答案:

答案 0 :(得分: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为我解决问题,我不知道为什么,只是多次尝试,并找到了这个。

答案 1 :(得分:1)

由于你的问题很长,我会在几个部分中分开我的答案。

  1. PHP4Delphi组件充当SAPI模块。 ISAPI SAPI模块用作它的原型
  2. 您使用的是什么版本的PHP4Delphi?在我的副本中调用tsrm_shutdown();位于1509线,而不是1497
  3. 我建议以下列方式阅读数组:
  4. 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. 有些人报告了数组整数索引的问题。我建议将索引更改为string:
    2. 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的角度来看字符串引用等于零时,它可以被解除分配,但同时它仍然可以被PHP引擎使用。 如果使用字符串填充子数组,请确保Delphi内存管理器不收集字符串。例如,您可以在将字符串添加到数组
      2. 之前将其转换为PAnsiChar
        {$IFNDEF COMPILER_VC9}
        fnc^.internal_function.function_name := strdup(method_name);
        {$ELSE}
        fnc^.internal_function.function_name := DupStr(method_name);
        {$ENDIF}