• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 可变变量

    有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。一个普通的变量通过声明来设置,例如:

    <?php
    $a = 'hello';
    ?>
    

    一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。在上面的例子中hello使用了两个美元符号($)以后,就可以作为一个可变变量的变量了。例如:

    <?php
    $$a = 'world';
    ?>
    

    这时,两个变量都被定义了:$a的内容是“hello”并且$hello的内容是“world”。因此,以下语句:

    <?php
    echo "$a ${$a}";
    ?>
    

    与以下语句输出完全相同的结果:

    <?php
    echo "$a $hello";
    ?>
    

    它们都会输出:hello world。

    要将可变变量用于数组,必须解决一个模棱两可的问题。这就是当写下$$a[1]时,解析器需要知道是想要$a[1]作为一个变量呢,还是想要$$a作为一个变量并取出该变量中索引为[1]的值。解决此问题的语法是,对第一种情况用${$a[1]},对第二种情况用${$a}[1]

    类的属性也可以通过可变属性名来访问。可变属性名将在该调用所处的范围内被解析。例如,对于$foo->$bar表达式,则会在本地范围来解析$bar并且其值将被用于$foo的属性名。对于$bar是数组单元时也是一样。

    也可使用花括号来给属性名清晰定界。最有用是在属性位于数组中,或者属性名包含有多个部分或者属性名包含有非法字符时(例如来自json_decode()或SimpleXML)。

    Example #1 可变属性示例

    <?php
    class foo {
        var $bar = 'I am bar.';
        var $arr = array('I am A.', 'I am B.', 'I am C.');
        var $r   = 'I am r.';
    }
    $foo = new foo();
    $bar = 'bar';
    $baz = array('foo', 'bar', 'baz', 'quux');
    echo $foo->$bar . "\n";
    echo $foo->$baz[1] . "\n";
    $start = 'b';
    $end   = 'ar';
    echo $foo->{$start . $end} . "\n";
    $arr = 'arr';
    echo $foo->$arr[1] . "\n";
    echo $foo->{$arr}[1] . "\n";
    ?>
    

    以上例程会输出:


    I am bar.
    I am bar.
    I am bar.
    I am r.
    I am B.
    Warning

    注意,在 PHP 的函数和类的方法中,超全局变量不能用作可变变量。$this变量也是一个特殊变量,不能被动态引用。

    <?php
     //You can even add more Dollar Signs
     $Bar = "a";
     $Foo = "Bar";
     $World = "Foo";
     $Hello = "World";
     $a = "Hello";
     $a; //Returns Hello
     $$a; //Returns World
     $$$a; //Returns Foo
     $$$$a; //Returns Bar
     $$$$$a; //Returns a
     $$$$$$a; //Returns Hello
     $$$$$$$a; //Returns World
     //... and so on ...//
    ?>
    
    It may be worth specifically noting, if variable names follow some kind of "template," they can be referenced like this:
    <?php
    // Given these variables ...
    $nameTypes  = array("first", "last", "company");
    $name_first  = "John";
    $name_last  = "Doe";
    $name_company = "PHP.net";
    // Then this loop is ...
    foreach($nameTypes as $type)
     print ${"name_$type"} . "\n";
    // ... equivalent to this print statement.
    print "$name_first\n$name_last\n$name_company\n";
    ?>
    This is apparent from the notes others have left, but is not explicitly stated.
    <?php
    // $variable-name = 'parse error';
    // You can't do that but you can do this:
    $a = 'variable-name';
    $$a = 'hello';
    echo $variable-name . ' ' . $$a; // Gives   0 hello
    ?>
    For a particular reason I had been using some variable names with hyphens for ages. There was no problem because they were only referenced via a variable variable. I only saw a parse error much later, when I tried to reference one directly. It took a while to realise that illegal hyphens were the cause because the parse error only occurs on assignment.
    If you want to use a variable value in part of the name of a variable variable (not the whole name itself), you can do like the following:
    <?php
    $price_for_monday = 10;
    $price_for_tuesday = 20;
    $price_for_wednesday = 30;
    $today = 'tuesday';
    $price_for_today = ${ 'price_for_' . $today};
    echo $price_for_today; // will return 20
    ?>
    
    PHP actually supports invoking a new instance of a class using a variable class name since at least version 5.2
    <?php
    class Foo {
      public function hello() {
       echo 'Hello world!';
      }
    }
    $my_foo = 'Foo';
    $a = new $my_foo();
    $a->hello(); //prints 'Hello world!'
    ?>
    Additionally, you can access static methods and properties using variable class names, but only since PHP 5.3
    <?php
    class Foo {
      public static function hello() {
       echo 'Hello world!';
      }
    }
    $my_foo = 'Foo';
    $my_foo::hello(); //prints 'Hello world!'
    ?>
    
    Another use for this feature in PHP is dynamic parsing.. 
    Due to the rather odd structure of an input string I am currently parsing, I must have a reference for each particular object instantiation in the order which they were created. In addition, because of the syntax of the input string, elements of the previous object creation are required for the current one. 
    Normally, you won't need something this convolute. In this example, I needed to load an array with dynamically named objects - (yes, this has some basic Object Oriented programming, please bare with me..)
    <?php
      include("obj.class");
      // this is only a skeletal example, of course.
      $object_array = array();
      // assume the $input array has tokens for parsing.
      foreach ($input_array as $key=>$value){
       // test to ensure the $value is what we need.
         $obj = "obj".$key;
         $$obj = new Obj($value, $other_var);
         Array_Push($object_array, $$obj);
       // etc..
      }
    ?>
    Now, we can use basic array manipulation to get these objects out in the particular order we need, and the objects no longer are dependant on the previous ones.
    I haven't fully tested the implimentation of the objects. The scope of a variable-variable's object attributes (get all that?) is a little tough to crack. Regardless, this is another example of the manner in which the var-vars can be used with precision where tedious, extra hard-coding is the only alternative.
    Then, we can easily pull everything back out again using a basic array function: foreach.
    <?php
    //...
      foreach($array as $key=>$object){
       echo $key." -- ".$object->print_fcn()." <br/>\n";
      } // end foreach  
    ?>
    Through this, we can pull a dynamically named object out of the array it was stored in without actually knowing its name.
    You may think of using variable variables to dynamically generate variables from an array, by doing something similar to: -
    <?php
     foreach ($array as $key => $value) 
     {
     $$key= $value;
     }
    ?>
    This however would be reinventing the wheel when you can simply use: 
    <?php
    extract( $array, EXTR_OVERWRITE);
    ?>
    Note that this will overwrite the contents of variables that already exist.
    Extract has useful functionality to prevent this, or you may group the variables by using prefixes too, so you could use: -
    EXTR_PREFIX_ALL
    <?php
    $array =array("one" => "First Value",
    "two" => "2nd Value",
    "three" => "8"
            );
          
    extract( $array, EXTR_PREFIX_ALL, "my_prefix_");
      
    ?>
    This would create variables: -
    $my_prefix_one 
    $my_prefix_two
    $my_prefix_three
    containing: -
    "First Value", "2nd Value" and "8" respectively
    Even outside a function or a class method variable variables cannot be used with PHP's Superglobal arrays. See the example bellow:
    <?php
    $a = '$GLOBALS["a"]';
    function foo()
    {
        global $a;
        echo $$a;
    }
    echo $GLOBALS["a"], PHP_EOL; // $GLOBALS["a"]
    echo ${$a}; // PHP Notice: Undefined variable: $GLOBALS["a"]
    Sometimes you might wish to modify value of an existing variable by its name. This is easily accomplishable with a combination of using "passing by reference" and "variable variables".
    $first_var = 1;
    $second_var = 2;
    $third_var = 3;
    $which_one = array_rand('first', 'second', 'third');
    //Let's consider the result is "second".
    $modifier = $$which_one; //Now $modifier has value 2.
    $modifier++; //Now $modifier's value is 3.
    echo $second_var; //Prints out 2
    //Consider we wish to modify the value of $second_var
    $modifier = &$$which_one; //Simply passing by reference
    $modifier++; //Now value of $second_var is 3 too.
    echo $second_var; //Prints out 3
    It's that simple!
    These are the scenarios that you may run into trying to reference superglobals dynamically. Whether or not it works appears to be dependent upon the current scope.
    <?php
    $_POST['asdf'] = 'something';
    function test() {
      // NULL -- not what initially expected
      $string = '_POST';
      var_dump(${$string});
      // Works as expected
      var_dump(${'_POST'});
      // Works as expected
      global ${$string};
      var_dump(${$string});
    }
    // Works as expected
    $string = '_POST';
    var_dump(${$string});
    test();
    ?>
    
    Adding an element directly to an array using variables:
    <?php
    $tab = array("one", "two", "three") ;
    $a = "tab" ;
    $$a[] ="four" ; // <==== fatal error
    print_r($tab) ;
    ?>
    will issue this error:
    Fatal error: Cannot use [] for reading
    This is not a bug, you need to use the {} syntax to remove the ambiguity.
    <?php
    $tab = array("one", "two", "three") ;
    $a = "tab" ;
    ${$a}[] = "four" ; // <==== this is the correct way to do it
    print_r($tab) ;
    ?>
    
    It's also valuable to note the following:
    <?php
    ${date("M")} = "Worked";
    echo ${date("M")};
    ?>
    This is perfectly legal, anything inside the braces is executed first, the return value then becomes the variable name. Echoing the same variable variable using the function that created it results in the same return and therefore the same variable name is used in the echo statement. Have fun ;).
    This is somewhat redundant, but I didn't see an example that combined dynamic reference of *both* object and attribute names.
    Here's the code:
    <?php
    class foo
    {
      var $bar;
      var $baz;
      function foo()
      {
        $this->bar = 3;
        $this->baz = 6;
      }
    }
    $f = new foo();
    echo "f->bar=$f->bar f->baz=$f->baz\n";
    $obj = 'f';
    $attr = 'bar';
    $val = $$obj->{$attr};
    echo "obj=$obj attr=$attr val=$val\n";
    ?>
    And here's the output:
    f->bar=3 f->baz=6
    $obj=f $attr=bar $val=3
    While not relevant in everyday PHP programming, it seems to be possible to insert whitespace and comments between the dollar signs of a variable variable. All three comment styles work. This information becomes relevant when writing a parser, tokenizer or something else that operates on PHP syntax.
    <?php
      $foo = 'bar';
      $
      /*
        I am complete legal and will compile without notices or error as a variable variable.
      */
        $foo = 'magic';
      echo $bar; // Outputs magic.
    ?>
    Behaviour tested with PHP Version 5.6.19
    Note that normal variable variables will not be parsed in double-quoted strings. You'll have to use the braces to make it work, to resolve the ambiguity. For example:
    <?php
    $varname = "foo";
    $foo = "bar";
    print $$varname; // Prints "bar"
    print "$$varname"; // Prints "$foo"
    print "${$varname}"; // Prints "bar"
    ?>
    
    Variable Class Instantiation with Namespace Gotcha:
    Say you have a class you'd like to instantiate via a variable (with a string value of the Class name)
    <?php
    class Foo 
    { 
      public function __construct() 
      { 
        echo "I'm a real class!" . PHP_EOL;
      }
    }
    $class = 'Foo';
    $instance = new $class;
    ?>
    The above works fine UNLESS you are in a (defined) namespace. Then you must provide the full namespaced identifier of the class as shown below. This is the case EVEN THOUGH the instancing happens in the same namespace. Instancing a class normally (not through a variable) does not require the namespace. This seems to establish the pattern that if you are using an namespace and you have a class name in a string, you must provide the namespace with the class for the PHP engine to correctly resolve (other cases: class_exists(), interface_exists(), etc.) 
    <?php
    namespace MyNamespace;
    class Foo 
    { 
      public function __construct() 
      { 
        echo "I'm a real class!" . PHP_EOL;
      }
    }
    $class = 'MyNamespace\Foo';
    $instance = new $class;
    ?>
    
    I found another undocumented/cool feature: variable member variables in classes. It's pretty easy:
    <?php
    class foo {
     function bar() {
      $bar1 = "var1";
      $bar2 = "var2";
      $this->{$bar1}= "this ";
      $this->{$bar2} = "works";
     }
    }
    $test = new foo;
    $test->bar();
    echo $test->var1 . $test->var2;
    ?>
    
    This is a handy function I put together to allow variable variables to be used with arrays.
    To use the function, when you want to reference an array, send it in the form 'array:key' rather than 'array[key]'.
    For example:
    <?php
    function indirect ($var, $value)   // Replaces $$var = $value
    {
      $var_data = $explode($var, ':');
      if (isset($var_data[1]))
      {
       ${$var_data[0]}[$var_data[1]] = $value;
      }
      else
      {
       ${$var_data[0]} = $value;
      }
    }
    $temp_array = array_fill(0, 4, 1);
    $temp_var = 1;
    $int_var_list = array('temp_array[2]', 'temp_var');
    while (list($key, $var_name) = each($int_var_list))
    {
      // Doesn't work - creates scalar variable called "$temp_array[2]" 
      $$var_name = 0;
    }
    var_dump($temp_array);
    echo '<br>';
    var_dump($temp_var);
    echo '<br>';
    // Does work!
    $int_var_list = array('temp_array:2', 'temp_var');
    while (list($key, $var_name) = each($int_var_list))
    {
      indirect($var_name, 2);
    }
    var_dump($temp_array);
    echo '<br>';
    var_dump($temp_var);
    echo '<br>';
    ?>
    
    One interesting thing I found out: You can concatenate variables and use spaces. Concatenating constants and function calls are also possible.
    <?php
    define('ONE', 1);
    function one() {
      return 1;
    }
    $one = 1;
    ${"foo$one"} = 'foo';
    echo $foo1; // foo
    ${'foo' . ONE} = 'bar'; 
    echo $foo1; // bar
    ${'foo' . one()} = 'baz';
    echo $foo1; // baz
    ?>
    This syntax doesn't work for functions:
    <?php
    $foo = 'info';
    {"php$foo"}(); // Parse error
    // You'll have to do:
    $func = "php$foo";
    $func();
    ?>
    Note: Don't leave out the quotes on strings inside the curly braces, PHP won't handle that graciously.
    This example may help to overcome the limitation on $this.
    Populate automatically fields of an object form a $_GET variable.
    <?php
    class pp{
      var $prop1=1,$prop2=2,$prop3=array(3,4,5);
      function fun1(){
       $vars=get_class_vars('pp');
       while(list($var,$value)=each($vars)){
            $ref=& $this->$var;
            $ref=$_GET[$var];
       } // while
       var_dump($this);
      }
    }
    $_GET['prop1']="uno";
    $_GET['prop2']="dos";
    $_GET['prop3']=array('tres','cuatro','cinco','seis');
    $p=new pp();
    $p->fun1();
    ?>
    output is ...
    object(pp)#1 (3) {
     ["prop1"]=>
     &string(3) "uno"
     ["prop2"]=>
     &string(3) "dos"
     ["prop3"]=>
     &array(4) {
      [0]=>
      string(4) "tres"
      [1]=>
      string(6) "cuatro"
      [2]=>
      string(5) "cinco"
      [3]=>
      string(4) "seis"
     }
    }
    The 'dollar dereferencing' (to coin a phrase) doesn't seem to be limited to two layers, even without curly braces. Observe:
    <?php
    $one = "two";
    $two = "three";
    $three = "four";
    $four = "five";
    echo $$$$one; //prints 'five'.
    ?>
    This works for L-values as well. So the below works the same way:
    <?php
    $one = "two";
    $$one = "three";
    $$$one = "four";
    $$$$one = "five";
    echo $$$$one; //still prints 'five'.
    ?>
    NOTE: Tested on PHP 4.2.1, Apache 2.0.36, Red Hat 7.2
    By the way...
    Variable variables can be used as pointers to objects' properties:
    <?php
    class someclass {
     var $a = "variable a";
     var $b = "another variable: b";
     }
    $c = new someclass;
    $d = "b";
    echo $c->{$d};
    ?>
    outputs: another variable: b
    The feature of variable variable names is welcome, but it should be avoided when possible. Modern IDE software fails to interpret such variables correctly, regular find/replace also fails. It's a kind of magic :) This may really make it hard to refactor code. Imagine you want to rename variable $username to $userName and try to find all occurrences of $username in code by checking "$userName". You may easily omit:
    $a = 'username';
    echo $$a;
    For example #1,line:
    echo $foo->$baz[1] . "\n";
    It should be:
    echo $foo->{$baz[1]} . "\n";
    to run correctly.
    In 5.4 "Dynamic class references require the fully qualified class name (with the namespace in it) because at runtime there is no information about the current namespace." is still true.
    Neither simple class name nor containing subnamespace works.
    Initial source: https://bugs.php.net/bug.php?id=45197
    The example given in the php manual is confusing!
    I think this example it's easier to understand: 
    <?php
    //Let's create a new variable: $new_variable_1
    $var_name = "new_variable_1"; //$var_name will store the NAME of the new variable
    //Let's assign a value to that [$new_variable_1] variable:
    $$var_name = "value 1"; //Value of $new_variable_1 = "value 1"
    echo "VARIABLE: " . $var_name;
    echo "<br />";
    echo "VALUE: " . $$var_name;
    ?>
    The OUTPUT is:
    VARIABLE: new_variable_1
    VALUE: value 1 
    You can also create new variables in a loop:
    <?php
    for( $i = 1; $i < 6; $i++ )
    {
    $var_name[] = "new_variable_" . $i; //$var_name[] will hold the new variable NAME
    }
    ${$var_name[0]} = "value 1"; //Value of $new_variable_1 = "value 1"
    ${$var_name[1]} = "value 2"; //Value of $new_variable_2 = "value 2"
    ${$var_name[2]} = "value 3"; //Value of $new_variable_3 = "value 3"
    ${$var_name[3]} = "value 4"; //Value of $new_variable_4 = "value 4"
    ${$var_name[4]} = "value 5"; //Value of $new_variable_5 = "value 5"
    echo "VARIABLE: " . $var_name[0] . "\n";
    echo "<br />";
    echo "VALUE: " . ${$var_name[0]};
    ?>
    The OUTPUT is:
    VARIABLE: new_variable_1
    VALUE: value 1
    Variable variables techniques do not work when one of the "variables" is a constant. The example below illustrates this. This is probably the desired behavior for constants, but was confusing for me when I was trying to figure it out. The alternative I used was to add the variables I needed to the $GLOBALS array instead of defining them as constants. 
    <?php
    define("DB_X_NAME","database1");
    define("DB_Y_NAME","database2");
    $DB_Z_NAME="database3";
    function connectTo($databaseName){
    global $DB_Z_NAME;
    $fullDatabaseName="DB_".$databaseName."_NAME";
    return ${$fullDatabaseName};
    }
    print "DB_X_NAME is ".connectTo("X")."<br>";
    print "DB_Y_NAME is ".connectTo("Y")."<br>";
    print "DB_Z_NAME is ".connectTo("Z")."<br>";
    ?>
    [Editor Note: For variable constants, use constant() --Philip]
    There is no need for the braces for variable object names...they are only needed by an ambiguity arises concerning which part of the reference is variable...usually with arrays.
    <?php
    class Schlemiel {
    var $aVar = "foo";
    }
    $schlemiel = new Schlemiel;
    $a = "schlemiel";
    echo $$a->aVar;
    ?>
    This code outputs "foo" using PHP 4.0.3.
    Hope this helps...
    - Jordan