Обмен данными с помощью 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
Ответы
Ответ 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}