Сравните массивы PHP, используя ссылки на память - PullRequest
6 голосов
/ 06 ноября 2010

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

Ответы [ 7 ]

15 голосов
/ 26 ноября 2010

На самом деле это можно сделать.Через расширение php.

Файл: config.m4

PHP_ARG_ENABLE(test, whether to enable test Extension support, [ --enable-test   Enable test ext support])

if test "$PHP_TEST" = "yes"; then
  AC_DEFINE(HAVE_TEST, 1, [Enable TEST Extension])
  PHP_NEW_EXTENSION(test, test.c, $ext_shared)
fi

Файл: 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,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
#if ZEND_MODULE_API_NO >= 20010901
    PHP_TEST_EXT_VERSION,
#endif
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_TEST
ZEND_GET_MODULE(test)
#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, настроить и сделать это.Добавьте «extension = / path / to / so / file / modules / test.so» в файл php.ini.И, наконец, перезапустите веб-сервер, на всякий случай.

<?php
  $x = array("123"=>"123");
  $w = $x;
  $y = $x;
  $z = &$x;
  var_dump(getaddress4($w,$x,$y,$z));
  var_dump(getaddress($w));
  var_dump(getaddress($x));
  var_dump(getaddress($y));
  var_dump(getaddress($z));
?>

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

string '
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 за указаниеэто, но мой исходный код передавал массивы по значению, поэтому воссоздавал массивы, включая ссылочный, и давал вам неверные значения памяти.С тех пор я изменил код, чтобы все параметры передавались по ссылке.Это позволит ссылкам, массивам и объектам передаваться в unmoled движком php.$ w / $ z - это то же самое, но $ w / $ x / $ y - нет.Старый код фактически показывал разрыв ссылок и тот факт, что адреса памяти изменились или совпали, когда все переменные были переданы в сравнении с несколькими вызовами одной и той же функции.Это потому, что PHP будет использовать одну и ту же память при выполнении нескольких вызовов.Сравнивать результаты оригинальной функции было бы бесполезно.Новый код должен решить эту проблему.

К вашему сведению - я использую php 5.3.2.

9 голосов
/ 24 ноября 2010

Ваш вопрос на самом деле немного вводит в заблуждение.«указать на одну и ту же ячейку памяти» и «являются одним и тем же массивом» (что для меня означает, что это ссылка, по крайней мере, в 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')
8 голосов
/ 23 ноября 2010

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

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

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

3 голосов
/ 25 ноября 2010
        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)найден.Принимая во внимание, что более раннее требование применяется как к ссылкам, так и к указателям, более позднее требование относится только к указателям.

2 голосов
/ 28 ноября 2010

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

  • Имеют ли переменные одинаковое содержимое? Для этого вы можете использовать ===.
  • Переменные используют внутренне одну и ту же память?
  • Находятся ли эти переменные в одном наборе ссылок? То есть, учитывая две переменные, $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, хотя и хакерский, также работает. Идея состоит в том, чтобы получить две переменные по ссылке, изменить одну и посмотреть, изменилась ли другая, восстановив значения в конце.
0 голосов
/ 24 ноября 2010
function var_name(&$ref){
    foreach($GLOBALS as $key => $val){
       if($val === $ref) return $key;
    }
}

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

0 голосов
/ 06 ноября 2010
 $a["unqiue-thing"] = 1;
 if($b["unique-thing"] == 1) // a and b are the same
...