3

In my code, I want to try fetching $_POST or $_GET variables, and unsetting them as soon as they were requested once.

This function returns the method used, which is simple enough. Unfortunately I cannot simply return the $_POST or $_GET variables directly or by reference, because I wouldn't be able to unset them (unsetting by reference doesn't work). So, I return variable names:

class Input {
  protected function interface_get($method): string {
    switch ($method) {
      case 'post':
        return '_POST';
        break;
      case 'get':
        return '_GET';
        break;
      case 'ajax':
        // Not supported yet.
        return null;
        break;
      default:
        return null; // returns '_GET' or '_POST'
    }
  }
  public function fetch_eval(string $method, ?string $request = null) { // ...fetch('post', 'username')
    if ($request !== null) {
      $request = '[\'' . $request . '\']'; // "['username']"
    }
    $request = $this->interface_get($method) . $request; #"$_POST['username']"
    eval('$return = $' . $request . ';'); #$return = $_POST['username'];
    eval('unset($' . $request . ');'); #unset($_POST['username']);
    return $return;
  }
  public function fetch_varvar(string $method, ?string $request = null) {
    $interface = $this->interface_get($method); #$interface = '_POST';
    if ($request === null) {
      $return = (${$interface});
      unset(${$interface});
    } else {
      $return = ${$interface}; #"Notice:  Undefined variable: _POST in [...]"
      $return = ${$interface}[$request]; #"Warning:  Illegal string offset 'email' in [...]"
      unset($interface[$request]);
    }
    return $result;
}
}
// set $_POST = ['email'=>'spam@me'];
$in = new Input();
echo $in->fetch_eval('post', 'email'); #'spam@me'
// $_POST = [];

// set $_POST = ['email'=>'spam@me']; again
echo $in->fetch_varvar('post', 'email'); #'Notice:  Undefined variable: _POST [...]'

The fun part is processing that output. Here is my fetch function, the easiest, but dirtiest, I think, way:

I tried using variable variables, it worked in a test script:

// $_POST = [0 => 5, 'bob' => 5];
$e = '_POST';
$var = 'bob';
echo ${$e}[$var]; #5
unset(${$e}[$var]);
echo ${$e}[$var ]; #NOTICE Undefined index: bob on line number 22
// Works as expected.

But it did not work in my script (Undefined variable: _POST [...].). I thought maybe the reason was that it was in a class, but anyway I couldn't solve it. What's more, if you put these functions outside the class and remove the $this->-es, they work! But not inside the class.

If someone could tell me why my latter code doesn't work, I would appreciate it. But in any case, would you say using eval() is reasonable? I know some people avoid it no matter what. Obviously in this case it opens up a pretty huge vulnerability, so rather than sanitizing and worrying I would prefer to avoid it altogether.

I would like to keep interface_get() as a separate function, but if needed, I can duplicate it inside fetch() too, I suppose.

Thank you very much in advance.

7
  • I recommend two updates to your question, which will help everyone here to help you more effectively. First, discuss what you found when you single-stepped through your code with the debugger. Give more details than "doesn't work". Second, remove all the code you posted and replace it with something that people can copy/paste into their debuggers. Commented Mar 13, 2018 at 22:53
  • I don't get why you need $request or variable variables at all. Can't you simply do something like if ($_POST) { unset($_POST); } and if ($_GET) { unset($_GET); }? Commented Mar 13, 2018 at 22:55
  • GreatBigBore - Okay, I'm on it. Obsidian - In that situation I would be basically putting the switch construct inside the fetch() function. It's not a big deal, but I would like to know how to decouple them properly, if at least for educational purposes. Commented Mar 13, 2018 at 23:00
  • 1
    Just for information, @DwarfVader, using $GLOBALS[$interface] works (instead of ${$interface}). Commented Mar 13, 2018 at 23:18
  • 2
    "Note: Variable variables Superglobals cannot be used as variable variables inside functions or class methods." Commented Mar 13, 2018 at 23:42

2 Answers 2

1

You could use $GLOBALS[$interface] to get your data.

Assuming $_POST and $_GET is always defined, however, you could check if the keys are defined before to get it and unset it, to avoid warnings.

public function fetch(string $method, ?string $request = null) {
    $interface = $this->interface_get($method); #$interface = '_POST';
    if ($request === null) {
        $result = $GLOBALS[$interface];
        unset($GLOBALS[$interface]);
        return $result;
    }
    if (isset($GLOBALS[$interface][$request])) {
        $result = $GLOBALS[$interface][$request];
        unset($GLOBALS[$interface][$request]);
        return $result;
    }
    return null;
}
Sign up to request clarification or add additional context in comments.

1 Comment

@DwarfVader, I added this answer that is not intended to be accepted, but simply, hope to be useful.
1

@Syscall and @Ignacio Vazquez-Abrams have provided answers to the question in comments.

As per PHP documentation (https://php.net/manual/en/language.variables.superglobals.php):

Superglobals cannot be used as variable variables inside functions or class methods.

Which is why it wasn't working inside the Input class. For it to work, I had to use the $GLOBALS variable like this:

public function fetch(string $method, ?string $request = null) {
    $interface = $this->interface_get($method); #$interface = '_POST';
    if ($request === null) {
        $return = $GLOBALS[$interface];
        unset($GLOBALS[$interface]);
    } else {
        $return = $GLOBALS[$interface][$request];
        unset($GLOBALS[$interface][$request]);
    }
    return $return;
}

This code worked. Thank you very much for your input. There might be a better, or more elegant way to achieve what I was looking for. In that case, further input is always welcome.

3 Comments

Well explained answer.
If you use a wrapper to access the globals and apparently want to prevent repeat access I would deal with this directly. The class just accepts globals as arrays in the constructor, after creating the class you unset them immediately, that means outside access is no longer possible. Then you can just unset the class properties after access and don't need to mess with globals inside the class.
@ChristianM that is fantastic and much more logical. I actually didn't like the fact that I had to adjust globals while inside the class functions, it seemed messy. This is more elegant.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.