Press "Enter" to skip to content

如何获取一个变量的名字

比如, 我提供一个查询服务, 用户可以提交一个人的名字和年龄做为查询条件.
假设我要查询一个名字叫做"laruence", 年龄是27的人, 我认为这个人的定义的查询token可以写做:

   laruence=27

不幸的是, 当这样的一个token做为query string提交给服务器的处理脚本的时候, 你就会发现, 诶,,我不知道用户名是什么,,,
好吧, 于是, 你就只好这么写:

username=laruence&age=27

那么, 能否获取到一个变量的名字呢?
首先, 从可能性上分析,
我们知道, 在C语言中, 所有的符号在编译器都被"替换"掉了.
而在PHP中, 所有的变量都存储在称为"符号表"的HastTable结构中. 在解析执行的过程中, 依旧保留着着"符号"信息, 所以, 肯定是可以获取到的.
而在PHP中, 符号的作用域是和活动符号表相关联的. 同一时间, 只有一个活动符号表.
那么怎么理解活动符号表和符号表呢?
对于PHP来说, 当前活动的符号表是保存在全局变量EG(active_symbol_table)中的, 而于此同时, 还有个全局符号表保存在EG(symbol_table)中, 在进入一个函数调用的执行体之前, 会生成一个新的active_symbol_table, 并且会保持一个调用栈式样的符号表栈:EG(symtable_cache), 以便在退出函数调用的时候, 恢复之前的活动符号表(作用域).
同时在PHP中, 不能实现作用域继承, 也就是不能直接访问作用域外层的符号(需要加上golbal声明), 而如果加上global的声明的话, 也会在当前的活动作用域生成一个copy, 也就是说, 不存在在当前作用域可见的符号是保存在全局符号表的.
如上分析, 我们只需要在当前的活动符号表中, 就可以找到我们需要的变量的名称,
当然, 有了这些还不够, 我们如何在PHP的脚本中实现获取当前的符号表呢?

  get_defined_vars
 

然而有一个问题要注意, 就是get_defined_vars返回的是当前活动符号表中定义的变量名, 也就是说, 如果你需要包装一个函数, 类似于:

  get_variable_name($var)

并且尝试在这个函数中通过get_defined_vars来获取在调用get_variable_name时刻的符号表是行不通的.
所以, 我们获取变量名字的函数, 应该是下面的这个样子:

  get_variable_name($var, $scope)

现在, 已经得到了当前活动的符号表, 接下来, 如何得到变量的名字呢?
显然, 我们需要根据变量的值去查询这个表, 找到值等于要找值的变量, 但是, 这样做又有一个问题, 那就是, 可能会有多个变量的值相等啊?
所以, 我们需要给这个变量一个唯一值. 而要是想要这个变量, 那么, 我们的这个目的函数的形式又要变一下了:

  get_variable_name(&$var, $scope)

接下来完善这个函数体吧:

function get_variable_name(&$var, $scope = NULL) {
       if (NULL == $scope) {
          $scope = $GLOBALS;
       }
       $tmp  = $var;
       $var   = "tmp_exists_" . mt_rand();
       $name = array_search($var, $scope, TRUE);
       $var   = $tmp;
       return $name;
}

另外, 有一个问题就是, 如果有多个变量之间有引用, 那么这个函数只是返回最先定义的变量名..
另外, 你也可以参考: http://php.net/manual/en/language.variables.php
关于作用域, 你也可以参看我之前的文章: 深入理解PHP原理之变量作用域(Scope in PHP)

20 Comments

  1. 小阳光
    小阳光 December 17, 2018

    鸟哥,我测试要写成如下才对,不然不传第二个参数$scope,就会$scope = $GLOBALS然后array_search函数会去无限递归查找最后返回false
    function get_variable_name(&$var, $scope = NULL) {
    if (NULL == $scope) {
    $scope = $GLOBALS;
    }
    $tmp = $var;
    $var = “tmp_exists_” . mt_rand();
    $name = array_search($var, $scope); //去掉 true 才对 不然 递归查找会返回false
    $var = $tmp;
    return $name;
    }

  2. Anonymous
    Anonymous November 7, 2013

    当前 符号表不是 global 时
    $var = “tmp_exists_” . mt_rand();
    无法 改变 $scope 里面的值
    PHP Version 5.4.16

  3. skyinghua
    skyinghua April 19, 2012

    自己跑跑代码就知道了

  4. zerox
    zerox January 13, 2012

    你好,对于函数体有两个地方不明白,希望能给个小的demo解答一下:
    1、第一个参数 &$var 应该传递什么进去
    2、$var = “tmp_exists_” . mt_rand(); 这句是什么意思?
    谢谢!

  5. HillTop
    HillTop May 3, 2011

    大哥,能不能给我推荐一点中高级的书籍啊!

  6. wclssdn
    wclssdn December 28, 2010

    我也没太理解… 你举的例子是说姓名和年龄. 那如果需要姓名年龄电话这三个, 查询字符串应该怎么写? 迷糊……..

  7. soulfree
    soulfree December 24, 2010

    好文章,前段时间正好研究这个问题.

  8. Shiwei Hu
    Shiwei Hu December 22, 2010

    Hi,
    话说,我以为。。。如果你要找到那个laruence的话,是不是直接遍历$_REQUEST好一点?或者说,你设置了laruence=27,如何在服务器端就知道你要的是laruence而不是别的呢?
    可能我没有理解你的文章,再研究研究。。。

  9. 雪候鸟
    雪候鸟 December 20, 2010

    @rzhome 好问题,,,,哈哈,,真是好问题… 其实我是为了引出这个问题,,现在看起来,这个假设有点问题,,鸡和蛋的问题…

  10. rzhome
    rzhome December 19, 2010

    菜鸟来学习了
    我想知道laruence=27做为query string提交给服务器的处理脚本
    调用get_variable_name(&$var, $scope)函数时$var的值如何取得呢?

  11. fifsky
    fifsky December 9, 2010

    前几天写框架的时候还想过这个问题,之前的大部分框架在Controller给View传值的时候都是采用
    $this->assign(‘abc’,$abc);
    我就想,为什么不是$this->assign($abc);然后View层自动得到变量的名称abc还原成$abc呢?
    当时找到采用遍历$GLOBALS的方式,后来打印了一下$GLOBALS,发现这个数组还是很大的,其结果得不偿失,后来就放弃了

  12. 雪候鸟
    雪候鸟 December 8, 2010

    @jaceju 🙂 我忘了写个例子了

  13. jaceju
    jaceju December 8, 2010

    忘了 get_defined_vars() 可以在 function 裡用,可以把它的結果當成 $scope 的值,就可以取得 function 裡的變數名了…

  14. jaceju
    jaceju December 8, 2010

    請教您,如果不是 Global 的變數,那麼 $scope 該怎麼指定?

Leave a Reply

Your email address will not be published. Required fields are marked *