Comparing PHP arrays using memory references

Is it possible to see if two array variables point to the same memory cell? (they are the same array)

+6
arrays php php-internals
source share
7 answers

Actually, this can be done. Via php extension.

File: 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

File: 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

File: 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);
 }

Then all you have to do is phpize, configure it and do it. Add "extension = / path / to / so / file / modules / test.so" to the php.ini file. Finally, restart the web server just in case.

  <? 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));
 ?>

Returns (at least for me, your memory addresses are likely to be different)

  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)

Thanks to Artefacto pointing this out, but my source code passed arrays by value, so arrays, including the reference one, were recreated in this way and gave you bad memory values. Since then I have changed the code to force all parameters to be passed by reference. This will allow the transfer of links, arrays and objects that are not passed by the php engine. $ w / $ z is the same, but $ w / $ x / $ y is not. The old code actually showed a broken link and the fact that memory addresses would change or match when all variables were passed through several calls to the same function. This is because PHP repeats using the same memory when making multiple calls. Comparing the results of the original function would be futile. New code should fix this problem.

FYI - I am using php 5.3.2.

+13
source share

Your question is actually a little misleading. โ€œpoint to the same memory locationโ€ and โ€œare the same arrayโ€ (which for me means a link to at least PHP), this is not the same thing.

The memory location refers to pointers. Pointers are not available in PHP. Links are not pointers.

Anyway, if you want to check if $b is really a link to $a , this is the closest to the real answer:

 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') 
+9
source share

Links in PHP are a means of accessing the same variable contents by different names. They are not like C pointers; for example, you cannot do pointer arithmetic using them, they are not actual memory addresses, etc.

Conclusion: No, you cannot

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

+8
source share
  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 

The check function was created after 2 minutes or so. It assumes that if you change the reference value, the second link will also have a new add value. This function only works with variables. You can use it with a constant value, function return (if only by reference), etc.

Edit: During testing, I had initial confusion. I continued to use the same variable names ($ a and $ b), with the result that all conditional expressions were yes. That's why:

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

To fix the problem, I gave them a different name:

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

Edit: why the answer is yes and not no

PHP does not disclose pointer information using scripts (does not manipulate the pointer, etc.). However, it allows alias variables (links) that are executed using the '&' reference operator. A function is usually found in pointers, which explains the general confusion. However, pointers are not aliases.

However, if we see the original question, the person wanted to know if $ a is the same as $ b, and not where $ a (or $ b) is in memory. While the earlier requirement applies to both links and pointers, the later applies only to pointers.

+3
source share

Firstly, your question is vague. This can mean several different things:

  • Do variables have the same content? You can use === .
  • Do variables use the same memory?
  • Are these variables in the same set of links? Ie, given the two variables, $a and $b , if I change $a , will it change $b ?

The answer to the second answer is not easy to determine. Jeremy Walton's answer has one significant problem: its function gets by value, so if you pass it a link, you will forcefully split and get the address of the new temporary value. You can force the function to receive the parameter by reference, but then you will have the opposite problem - if you passed the value (using refcount> = 2), you would also be forced to split.

More importantly, the second question is the irrelevant internal detail. Consider the following script:

 $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 

So, the important question is the third. Unfortunately, there is no easy way to determine this. This has been requested several times; see for example this query .

However, there are several options:

  • You can get the name of the variable and see it in the symbol table. It also makes xdebug_debug_zval much more interesting than the lack of debug_zval_dump . This is a simple search in EG(active_symbol_table) for simple variables (but it will be more complicated if you want to include object properties and sizes, etc.), and it will also allow you to implement a solution for the second question.
  • You can also modify Jeremy Walton's answer to make the function receive by reference (you will need the arginfo structure) and get two values โ€‹โ€‹at the same time. Receiving them at the same time can avoid false positives due to duplicate memory addresses (although the problem depends on the use of the function, on the other hand, Jeremy Walton's function always suffers from this problem when receiving links - I can develop on this if necessary but see my comment under his answer).
  • Netcoder's answer, although hacky, also works. The idea is to get two variables by reference, change it and see if the other has changed, and restore the values โ€‹โ€‹at the end.
+2
source share
 function var_name(&$ref){ foreach($GLOBALS as $key => $val){ if($val === $ref) return $key; } } 

This is untested, but what I know about php, vars, is added to GLOBALS as they are loaded into the system, so the first appearance where they are identical should be the original var, but if you have 2 Variables. same i'm not sure how this will react

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

All Articles