tags:

views:

65

answers:

5

Now, before you jump at how you shouldn't mix scopes: I realize this. However, this is a case where either scope mixing must occur or major code duplication must occur—nothing around it. And I'd prefer scope mixing.

That said, I'd like this code:

function a() {
    $a = "a";
    $b = "b";
    $c = "c";
}
function b() {
    a();
    echo $a . $b . $c;
}
b(); // Output: abc
echo $a; // Should raise a notice that $a is undefined

to be able to work as commented. I know it's not possible in most languages—I was able to do it in Ruby, though; and wonder if you can do it with PHP.

The names of variables aren't known beforehand in the real situation.

Again, it's code duplication or this—absolutely no way around it.

Also, it'd be okay if a had to be something like a('b') or something.


In reality, the code is this:

static function renderError($what, $vararray) {
    foreach($vararray as $key => $val) { /* this foreach is the code we want to decouple */
        $key = 'e_'.$key;
        $$key = htmlspecialchars($val);
    }
    ob_clean();
    exit(eval('?>'.file_get_contents(ROOT."/templates/$what.php")));
}

With a call like E_Render::renderError('NotFound', array( 'requested_url' => '/notfound', 'misspelling' => '/reallynotfound' ));

Then, in templates/NotFound.php, you'd have something like:

<html>
<body>
<?php echo $e_requested_url; ?> could not be found. Did you mean <?php echo $e_misspelling: ?>?
</body>
</html>

We'd really rather not have our template authors do anything more than that...although $e['requested_url'] could be done if nothing better was available.

A: 

why can't you return $a from a()?

function a() {
    $a = "a";
    return $a;
}
function b() {
    $a = a();
    echo $a;
}
b(); // Output: a
echo $a; // Should raise a notice that $a is undefined

this does not break scope.

Crayon Violent
See edited question. I need to be able to declare multiple variables and have them NOT in an array. Also, I won't know the names of the variables before hand in the real situation.
aharon
okay can you explain a) why it can't be an array, because you can just return an array and really what's the difference between $a vs. $array['a'] and b) give more context to your problem because I have yet to see a reason scope has to be broken.
Crayon Violent
also if you don't know the variable names before hand, how would you know what to use in b()? Again, please give more context to the issue
Crayon Violent
See edited response, please. Although I believe that NullUserException gave me the right method with `extract()`.
aharon
+5  A: 

That's why we have OO:

class Foo {

    function a() {
        $this->a = "a";
        $this->b = "b";
        $this->c = "c";
    }

    function b() {
        $this->a();
        echo $this->a . $this->b . $this->c;
    }
}

$f = new Foo;
$f->b(); // Output: abc
echo $a; // Should raise a notice that $a is undefined

Alternatively:

function a() {
    $a = "a";
    $b = "b";
    $c = "c";

    return compact('a', 'b', 'c');
}

function b() {
    extract(a());
    echo $a . $b . $c;
}

See: compact(), extract()

Otherwise I don't see a way of doing this without drastically changing the language.

PS: If you feel this feature is so important, why don't you just use Ruby?

NullUserException
Keeping variable names $a, $b, $c is critical. Oh well.
aharon
+1 You got it first. You did not declare $a,$band $c as class members ?
NAVEED
@NAVEED Don't have to. PHP can add class members at runtime
NullUserException
@aha See my alternative answer
NullUserException
extract() was exactly what I was looking for. Thanks!
aharon
A: 

I do not understand why you cannot return them as an array. Besides the suggestion @NullUserException gave you, I would suggest the following approach (if you do not take the OOP route).

function a() {
  $a = "a";
  $b = "b";
  $c = "c";

  return array($a, $b, $c);
}

function b() {
  list($a, $b, $c) = a();
  echo $a . $b . $c;
}
mhitza
+1  A: 

There's no way to do what you're asking given the restrictions you are imposing. There's never going to be a good reason to do exactly what you are trying to do. There's plenty of better solutions.

Anyway, the closest you'll get is passing by reference:

<?php
function a(&$a, &$b, &$c)
{
  $a = 1;
  $b = 2;
  $c = 3;
}

function b()
{
  a($a, $b, $c);
}
?>
konforce
+1  A: 

I just ran this code

-- var1.php

<?php

function a($vars) {
   foreach ($vars as $var => $val) {
      $$var = $val;
   }

   echo eval('?>' . file_get_contents('var2.php') . '<?php ');
};


a(array('a' => 'foo', 'b' => 'bar', 'c' => 'baz'));

-- var2.php

<html>
   <body>
      <div><?= '$a = "' . $a . '"' ?></div>
      <div><?= '$b = "' . $b . '"' ?></div>
      <div><?= '$c = "' . $c . '"' ?></div>
   </body>
</html>

And it outputs :

$a = "foo"
$b = "bar"
$c = "baz"

The reason being that since eval() keeps the current scope, any variable declared locally inside the function will also be available locally inside the eval'ed string. Same thing with $this.

** UPDATE **

Since eval() is evil and should be avoided (as kindly suggested), here's a rewrite using simple templating. This way, your designer only has to know the variable name available (in the specs) :

-- var1.php

<?php

function showError($error, $vars) {

   $template = file_get_contents("{$error}.php");
   $keys = array();
   $values = array();
   foreach ($vars as $var => $val) {
      $keys[] = '@{e_'.$var.'}';
      $values[] = $val;
   }

   echo str_replace($keys, $values, $template);
};

showError('template1', array('value' => 300, 'message' => 'Something foo'));

-- template1.php

<html>
   <body>
      <div>Error <span style="font-weight: bold; color: red;">@{e_value}</span></div>
      <div>The message was : <em>@{e_message}</em></div>
   </body>
</html>
Yanick Rochon
-1 For suggesting `eval()` and short tags.
NullUserException
... alright, fair enough about the eval. As for the short tags, this is your opinion. I'm updating the answer so there is no eval() at all. Happy?
Yanick Rochon
Short tags are a nightmare. Many servers have them disabled and leave people clueless, leading to questions like [this](http://stackoverflow.com/questions/2476072/tags-not-working-in-php-5-3-1) and [this](http://stackoverflow.com/questions/2476072/tags-not-working-in-php-5-3-1). And they are also not standards-compliant. This is a stupid "feature" that shouldn't have been added in the first place.
NullUserException
I've been using justhost and 1and1, and been working with Ubuntu as my development station and all have the option enabled... I understand the confusion, but I disagree; this "feature" is pretty useful (and IMO I like it). So far, I've only seen Windows servers have them disabled. But I could be mistaken. In short, I rarely saw this problem and even when encountered, it is pretty easy to fix/get around. Enough said.
Yanick Rochon
Besides, I didn't suggest using short tags at the beginning of the source code, but for output (<?= ?>) only. (http://www.bin-co.com/php/articles/using_php_short_tags.php)
Yanick Rochon
For something like this—where it's a supplied file that's guaranteed to be code as safe as anything else in the package—eval is okay, and in fact much better performing than a templating system would be. Of course, eval shouldn't be used with user supplied data—but this is almost like using it as an include; which I *hope* you would not object to. :)
aharon
@aharon, I kept the eval portion of my answer because I supposed exactly what you're saying. Normally, I too would not recommend it, and also because eval *can* be slower than str_replace (I'm not talking about preg_replace/_all here). But since this is an error reporting system, then either solution, IMHO, is acceptable.
Yanick Rochon