8

Just wondering if there is a way around this (or if it is even possible).

So I have a section of code similar to this. I have a string, with a value that contains square brackets, similar to those used when accessing an array key. I want to create that array key itself by using the strings value. I hope this code makes a little more sense of what I mean.

// String that has a value similar to an array key
$string = 'welcome["hello"]';
$$string = 'thisworks';

// I could then print the array keys value like this
print( $welcome["hello"] );

// This would hopefully output 'thisworks'.

Can't seem to get it to work correctly, however. Is it possible (or how else can I go about it)?

Jason McCreary
  • 71,546
  • 23
  • 135
  • 174
Matthew Ruddy
  • 905
  • 4
  • 17
  • 31
  • 2
    use an array from the beginning, then you don't have these issues. – hakre May 05 '12 at 23:37
  • Are you trying to parse JSON, perhaps? Describe where this data is coming from a little more, if you could. – Cory Carson May 05 '12 at 23:35
  • Not using JSON. This is a very simplified version of what really is going on, but in essence multi-dimensional arrays are used to construct a new array of options. Currently I am using regexp to detect if the string contains square brackets, and if it does, an if statement does a load of stuff to create an array out of it. As you can guess, trying to make the above method work would save all of if checking, and would work for both options that contain no square brackets and those that do. – Matthew Ruddy May 06 '12 at 00:00
  • 1
    If you show your actual code, I'm pretty sure much better suggestion can be given to you. If you really look for something that matches the currently specified needs: this has been asked before (and answered). http://stackoverflow.com/questions/7003559/use-strings-to-access-potentially-large-multidimensional-arrays – hakre May 06 '12 at 00:23
  • May be you can hack (extend) [this Kohana class](http://kohanaframework.org/3.1/guide/api/Config) to meet your criteria. – Shiplu Mokaddim May 06 '12 at 06:13
  • Thanks @hakre for that link it is very helpful. I will also look at the Kohanna class suggestion, sounds interesting. Thanks both! – Matthew Ruddy May 06 '12 at 11:02

5 Answers5

7

The following is an example following your variable name syntax that resolves array members as well:

// String that has a value similar to an array key
$string = 'welcome["hello"]';

// initialize variable function (here on global variables store)
$vars = $varFunc($GLOBALS);

// alias variable specified by $string
$var = &$vars($string);

// set the variable
$var = 'World';

// show the variable
var_dump($welcome["hello"]); # string(5) "World"

With the following implementation:

/**
 * @return closure
 */
$varFunc = function (array &$store) {
    return function &($name) use (&$store)
    {
        $keys = preg_split('~("])?(\\["|$)~', $name, -1, PREG_SPLIT_NO_EMPTY);
        $var = &$store;
        foreach($keys as $key)
        {
            if (!is_array($var) || !array_key_exists($key, $var)) {
                $var[$key] = NULL;
            }
            $var = &$var[$key];
        }
        return $var;
    };
};

As you can not overload variables in PHP, you are limited to the expressiveness of PHP here, meaning there is no write context for variable references as function return values, which requires the additional aliasing:

$var = &$vars($string);

If something like this does not suit your needs, you need to patch PHP and if not an option, PHP is not offering the language features you're looking for.

See as well the related question: use strings to access (potentially large) multidimensional arrays.

Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836
  • Out of interest, is there a similar way to do this without the use of anonymous functions? Just for backwards compatibility support. Sorry if it is a stupid question, this sort of PHP is new to me (anonymous functions, using '&' before variables, the use of 'use'). – Matthew Ruddy May 06 '12 at 14:59
  • Backwards compatible down to which PHP version? And sure it is, you can simulate any closure with a class. – hakre May 06 '12 at 15:01
  • In essence this code is part of a Wordpress plugin, so I try to comply with its current minimum requirements. I'd say as long as it works with PHP 5.2.4, it would be fine, but I'm aware that anonymous functions were only introduced in PHP 5.3. Anyway, thanks again, I'm currently trying to understand it fully and hopefully get a compatible solution working. – Matthew Ruddy May 06 '12 at 15:08
  • @MatthewRuddy: I have searched for (but not found yet) another answer of mine that explains how to turn an anonymous function with a `use` clause into an object. That's principally the code you're looking for. – hakre May 06 '12 at 15:19
  • Don't worry, I will try my best to figure it out. Thank you for your solution regardless. Have learned a lot from it. – Matthew Ruddy May 06 '12 at 15:28
5

In Variable variables characters in a string treated literally. They do not interpret whats inside the string. So [] characters in the $string is not treated as array notation. Rather its treated as a part of the variable name.

after you execute $$string = 'thisworks'; PHP creates a variable with name welcome["hello"] literally with value set to thisworks.

To print it use this notation,

print (${'welcome["hello"]'});
Shiplu Mokaddim
  • 56,364
  • 17
  • 141
  • 187
  • Possibly a stupid question, but I assume there is no way to tell PHP to stop taking it literally? – Matthew Ruddy May 06 '12 at 00:12
  • @MatthewRuddy Definitely not. Why do you need it so much? – RReverser May 06 '12 at 00:23
  • Its just an idea I've been trying to get to work. I already have a working alternative, but a method similar to my original post would cut down a lot of lines and be slightly more efficient. I always like to experiment with ways of doing things better. – Matthew Ruddy May 06 '12 at 10:52
1

No, you can't do that in such way. Alternatively, you can set reference variable using

$welcomeHello = &$welcome["hello"]

and then use

$welcomeHello = 'thisworks'

for setting item content.

This would be more correct and even faster way for setting array items.

RReverser
  • 1,940
  • 14
  • 15
0

You can use eval, although I really wouldn't recommend it in a production environment, it does have its benefits.

<?
$string = 'welcome["hello"]';
eval('$' . $string . " = 'thisworks';");

// This also works : eval("$$string = 'thisworks';");

// I could then print the array keys value like this
print( $welcome["hello"] ); // Prints 'thisworks'
Bryan
  • 6,682
  • 2
  • 17
  • 21
  • 2
    but don't forget one thing. Eval is EVIL. – ahmetunal May 05 '12 at 23:36
  • Heavily agreed, it's so incredibly susceptible to someone hacking up the content inside of the eval, especially if it's dynamic, and running any command they desire in your script. – Bryan May 05 '12 at 23:37
  • Such a shame, because this almost seems like the perfect solution (that doesn't involved adding extra functions). Can I make it safer using 'call_user_func'? – Matthew Ruddy May 06 '12 at 17:13
0

You could use two variables, one for holding the referenced variable name and the other for holding the key:

<?php
$myArray = array('foo', 'bar', 'baz');
$varName = 'myArray';
$varKey  = 1;
echo ${$varName}[$varKey];
// will echo "bar"

As per why the curly braces for dereferencing $varName:

In order to use variable variables with arrays, you have to resolve an ambiguity problem. That is, if you write $$a[1] then the parser needs to know if you meant to use $a[1] as a variable, or if you wanted $$a as the variable and then the [1] index from that variable. The syntax for resolving this ambiguity is: ${$a[1]} for the first case and ${$a}[1] for the second.

(from https://www.php.net/manual/en/language.variables.variable.php)

JotaDeAA
  • 35
  • 6