Подтвердить что ты не робот

Обмен данными с помощью zend (многомерных массивов)

Я встраиваю PHP в свое приложение (написанное в Delphi 2010), используя компонент PHP4Delphi для взаимодействия с php5ts.dll. Я предполагаю, что моя программа действует как расширение для PHP (sapi module?), Поскольку она регистрирует некоторые функции и константы, которые могут использоваться в 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();

Мне кажется, что сборщик мусора разбился в конце script, поэтому я подозреваю, что я не отправляю данные правильно на двигатель... таким образом, мой вопрос заключается в том, как предполагается отправить многомерные массивы на 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, но иногда приложение просто исчезает при выполнении script - я подозреваю, что он вызывает бесконечную рекурсию в менеджере memmory Engine, сбой php DLL и приложение с ним... При установке refcount на 0 (ноль; если предположить, что при возврате значения в PHP script он будет равен 1, а затем, когда переменная PHP выходит из области действия, она будет уничтожена) все, похоже, работают (то есть без сбоев, нет AV), но script не будет генерировать какой-либо вывод, просто пустой html файл...

Я также отправляю данные в виде массивов в свою функцию, затем использую 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?

ТИА
Ain

4b9b3361

Ответ 1

вот моя работа 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, чтобы решить проблему, я не знаю, почему, просто много раз пробовал и нашел это.

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