Сравнение массивов PHP с использованием ссылок на память

Можно ли увидеть, указывают ли две переменные массива на одну и ту же ячейку памяти? (они являются одним и тем же массивом)

Solutions Collecting From Web of "Сравнение массивов PHP с использованием ссылок на память"

Собственно, это можно сделать. Через расширение php.

Файл: config.m4

 PHP_ARG_ENABLE (проверьте, нужно ли включить поддержку тестирования расширения, [--enable-test Включить поддержку тестирования ext])

 если тест «$ PHP_TEST» = «да»;  тогда
   AC_DEFINE (HAVE_TEST, 1, [Включить расширение TEST])
   PHP_NEW_EXTENSION (test, test.c, $ ext_shared)
 фи

Файл: php_test.h

 #ifndef PHP_TEST_H
 #define PHP_TEST_H 1

 #define PHP_TEST_EXT_VERSION "1.0"
 #define PHP_TEST_EXT_EXTNAME "test"

 PHP_FUNCTION (getaddress4);
 PHP_FUNCTION (GetAddress);

 extern zend_module_entry test_module_entry;
 #define phpext_test_ptr & test_module_entry

 #endif

Файл: test.c

 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif

 #include "php.h"
 #include "php_test.h"

 ZEND_BEGIN_ARG_INFO_EX (func_args, 1, 0, 0)
 ZEND_END_ARG_INFO ()

 static function_entry test_functions [] = {
     PHP_FE (getaddress4, func_args)
     PHP_FE (getaddress, func_args)
     {NULL, NULL, NULL}
 };

 zend_module_entry test_module_entry = {
 #if ZEND_MODULE_API_NO> = 20010901
     STANDARD_MODULE_HEADER,
 #endif
     PHP_TEST_EXT_EXTNAME,
     test_functions,
     НОЛЬ,
     НОЛЬ,
     НОЛЬ,
     НОЛЬ,
     НОЛЬ,
 #if ZEND_MODULE_API_NO> = 20010901
     PHP_TEST_EXT_VERSION,
 #endif
     STANDARD_MODULE_PROPERTIES
 };

 #ifdef COMPILE_DL_TEST
 ZEND_GET_MODULE (тест)
 #endif

 PHP_FUNCTION (getaddress4)
 {
     zval * var1;
     zval * var2;
     zval * var3;
     zval * var4;
     char r [500];
     if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC, "aaaa", & var1, & var2, & var3, & var4) == FAILURE) {
       RETURN_NULL ();
     }
     sprintf (r, "\ n% p -% p -% p -% p \ n% p -% p -% p -% p", var1, var2, var3, var4, Z_ARRVAL_P (var1), Z_ARRVAL_P (var2) , Z_ARRVAL_P (var3), Z_ARRVAL_P (var4));
     RETURN_STRING (r, 1);
 }

 PHP_FUNCTION (GetAddress)
 {
     zval * var;
     char r [100];
     if (zend_parse_parameters (ZEND_NUM_ARGS () TSRMLS_CC, "a", & var) == FAILURE) {
       RETURN_NULL ();
     }
     sprintf (r, "% p", Z_ARRVAL_P (var));
     RETURN_STRING (r, 1);
 }

Тогда все, что вам нужно сделать, это phpize, настроить его и сделать. Добавьте в файл php.ini «extension = / path / to / so / file / modules / test.so». И, наконец, перезапустите веб-сервер, на всякий случай.

 <? PHP
   $ x = array ("123" => "123");
   $ w = $ x;
   $ y = $ x;
   $ z = & $ x;
   var_dump (getaddress4 ($ ш, $ х, у, $ $ г));
   var_dump (GetAddress ($ w));
   var_dump (GetAddress ($ х));
   var_dump (GetAddress ($ у));
   var_dump (GetAddress ($ г));
 ?>

Возвращает (по крайней мере для меня, ваши адреса памяти, вероятно, будут разными)

 строка '
 0x9efeb0 - 0x9effe0 - 0x9ef8c0 - 0x9efeb0
 0x9efee0 - 0x9f0010 - 0x9ed790 - 0x9efee0 '(length = 84)

 string '0x9efee0' (length = 8)

 string '0x9f0010' (length = 8)

 string '0x9ed790' (length = 8)

 string '0x9efee0' (length = 8)

Благодаря Artefacto для указания этого, но мой исходный код передавал массивы по значению, поэтому таким образом были воссозданы массивы, включая ссылочный, и давали вам плохие значения памяти. С тех пор я изменил код, чтобы принудительно передать все параметры по ссылке. Это позволит передавать ссылки, массивы и объекты, которые не передаются движком php. $ w / $ z – одно и то же, но $ w / $ x / $ y – нет. Старый код на самом деле показал обрыв ссылки и тот факт, что адреса памяти будут меняться или совпадать, когда все переменные были переданы через несколько вызовов одной и той же функции. Это связано с тем, что PHP повторяет использование одной и той же памяти при выполнении нескольких вызовов. Сравнение результатов исходной функции было бы бесполезным. Новый код должен устранить эту проблему.

FYI – я использую php 5.3.2.

Ваш вопрос на самом деле немного вводит в заблуждение. «указывают на одно и то же место памяти» и «являются одним и тем же массивом» (что для меня означает ссылку на, по крайней мере, на PHP), это не одно и то же.

Места памяти относятся к указателям. Указатели недоступны в PHP. Ссылки не являются указателями.

Во всяком случае, если вы хотите проверить, действительно ли $b является ссылкой на $a , это самый близкий вам ответ:

 function is_ref_to(&$a, &$b) { if (is_object($a) && is_object($b)) { return ($a === $b); } $temp_a = $a; $temp_b = $b; $key = uniqid('is_ref_to', true); $b = $key; if ($a === $key) $return = true; else $return = false; $a = $temp_a; $b = $temp_b; return $return; } $a = array('foo'); $b = array('foo'); $c = &$a; $d = $a; var_dump(is_ref_to($a, $b)); // false var_dump(is_ref_to($b, $c)); // false var_dump(is_ref_to($a, $c)); // true var_dump(is_ref_to($a, $d)); // false var_dump($a); // is still array('foo') 

Ссылки на PHP – это средство доступа к одному и тому же содержимому переменной разными именами. Они не похожи на C-указатели; например, вы не можете выполнять арифметику указателей, используя их, они не являются фактическими адресами памяти и т. д.

Вывод: нет, вы не можете

От: http://www.php.net/manual/en/language.references.whatare.php

  function check(&$a,&$b){ // perform first basic check, if both have different values // then they're definitely not the same. if($a!==$b)return false; // backup $a into $c $c=$a; // get some form of opposite to $a $a=!$a; // compare $a and $b, if both are the same thing, // this should be true $r=($a===$b); // restore $a from $c $a=$c; // return result return $r; } $a=(object)array('aaa'=>'bbb'); $b=&$a; echo check($a,$b) ? 'yes' : 'no'; // yes $c='aaa'; $d='aaa'; echo check($c,$d) ? 'yes' : 'no'; // no $e='bbb'; $f='ccc'; echo check($e,$f) ? 'yes' : 'no'; // no 

Функция «проверка» была создана через 2 минуты или около того. Предполагается, что если вы измените значение ссылки, вторая ссылка также получит новое значение. Эта функция работает только с переменными. Вы можете использовать его против постоянного значения, возврата функции (если только по ссылке) и т. Д.

Изменить: во время тестирования у меня была начальная путаница. Я продолжал использовать одни и те же имена переменных ($ a и $ b), в результате чего все условные выражения были «да». Вот почему:

 $a='aaa'; $b=&$a; // a=aaa b=aaa $a='ccc'; $b='ddd'; // a=ddd b=ddd <= a is not ccc! 

Чтобы исправить эту проблему, я дал им другое имя:

 $a='aaa'; $b=&$a; // a=aaa b=aaa $c='ccc'; $d='ddd'; // c=ccc d=ddd <= c is now correct 

Изменить: почему ответ «да», а не «нет»,

PHP не раскрывает информацию указателя с помощью скриптов (не манипулирует указателем и т. Д.). Однако он допускает переменные псевдонимов (ссылки), выполняемые с помощью ссылочного оператора «&». Функция обычно находится в указателях, что объясняет общую путаницу. Тем не менее, указатели не являются псевдонимами.

Однако, если мы видим исходный вопрос, человек хочет знать, что $ a совпадает с $ b, а не где в памяти $ a (или $ b). В то время как более раннее требование применяется как к ссылкам, так и к указателям, более поздний применяется только к указателям.

Во-первых, ваш вопрос расплывчатый. Это может означать несколько разных вещей:

  • Имеют ли переменные одинаковый контент? Для этого вы можете использовать === .
  • Используют ли переменные внутри одной и той же памяти?
  • Являются ли эти переменные в одном и том же наборе ссылок? Т.е., учитывая две переменные, $a и $b , если я изменю $a , изменит ли он $b ?

Ответ на второй ответ определить нелегко. Ответ Джереми Уолтона имеет одну серьезную проблему – его функция получает по значению, поэтому, если вы передадите ей ссылку, вы принудительно разделите ее и получите адрес нового временного значения. Вы можете заставить функцию получить параметр по ссылке, но тогда у вас возникнет противоположная проблема – если вы передали значение (с помощью refcount> = 2), вы также принудительно разделили бы.

Что еще более важно, второй вопрос – нерелевантная внутренняя деталь. Рассмотрим следующий сценарий:

 $a = 1; $b = $a; //addresses of $a and $b are the same function force_sep(&$a) { } force_sep($b); //force_sep is a no-op, but it forced a separation; now addresses are not equal 

Поэтому важный вопрос – третий. К сожалению, нет простого способа определить это. Это было запрошено несколько раз; см., например, этот запрос .

Однако есть несколько вариантов:

  • Вы можете получить имя переменной и посмотреть ее в таблице символов. Это также делает xdebug_debug_zval гораздо более интересным, чем недостаток debug_zval_dump . Это простой поиск в EG(active_symbol_table) для простых переменных (но будет более сложным, если вы хотите включить свойства объекта и размеры и т. Д.), И это также позволит вам реализовать решение для второго вопроса.
  • Вы также можете изменить ответ Джереми Уолтона, чтобы заставить функцию получить по ссылке (вам понадобится структура arginfo) и получить два значения одновременно. Получение их в одно и то же время может избежать ложных срабатываний из-за повторных адресов памяти (хотя проблема зависит от использования функции, с другой стороны, функция Джереми Уолтона всегда страдает от этой проблемы при получении ссылок – я могу разработать на этом, если необходимо, но см. мой комментарий под его ответом).
  • Ответ netcoder, хотя и хакерский, также работает. Идея состоит в том, чтобы получить две переменные по ссылке, изменить одну и посмотреть, изменился ли другой, и восстановить значения в конце.
 function var_name(&$ref){ foreach($GLOBALS as $key => $val){ if($val === $ref) return $key; } } 

Это непроверено, но то, что я знаю о php, vars, добавляется в GLOBALS, поскольку они загружаются в систему, поэтому первое появление, где они идентичны, должно быть исходным var, но если у вас есть 2 Variables Точно так же i ' не знаю, как это будет реагировать

  $a["unqiue-thing"] = 1; if($b["unique-thing"] == 1) // a and b are the same