Обмен данными с Zend (многомерными массивами) - PullRequest
22 голосов
/ 25 апреля 2011

Я встраиваю 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

Строка 1497 в php4delphi.pas - это вызов 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 каждого массива (althought MAKE_STD_ZVAL уже инициализирует refcount к 1), и это лечит AV, но иногда приложение просто исчезает при выполнениисценарий - я подозреваю, что это вызывает бесконечную рекурсию в диспетчере памяти движка, приводит к сбою DLL-библиотеки php и взятию приложения вместе с ней ... При установке refcount в 0 (ноль; при условии, что когда в PHP-сценарии присваивается возвращаемое значение, его refcount будет1, а затем, когда переменная PHP выходит из области видимости, она будет уничтожена) все работает (т. Е. Без сбоев, без AV), но сценарий не будет генерировать какой-либо вывод, просто пустой HTML-файл ...

Iтакже отправьте данные в виде массивов в мою функцию, затем используйте zend_hash_find, zend_hash_get_current_data и т. д. для чтения данных.Может ли это испортить пересчет переменных?Т.е. мне нужно уменьшить refcout переменной, возвращаемой zend_hash_find, когда я закончу с ней?
И безопасно ли повторно использовать эту же переменную при итерации по массиву, т.е.

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 ]

2 голосов
/ 12 января 2012

вот моя работа arround:

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 голос
/ 28 сентября 2011

Поскольку ваш вопрос довольно длинный, я разделю свой ответ на несколько частей.

  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, она может быть освобождена, но в то же время она все еще может использоваться механизмом PHP.Если вы заполняете свои подмассивы строками, убедитесь, что строки не собраны менеджером памяти Delphi.Например, вы можете преобразовать строки в PAnsiChar перед добавлением их в массив
{$IFNDEF COMPILER_VC9}
fnc^.internal_function.function_name := strdup(method_name);
{$ELSE}
fnc^.internal_function.function_name := DupStr(method_name);
{$ENDIF}
...