"Global" variable scope in PHP - php

I have a section of code like the following:
---- file.php ----
require_once("mylib.php");
function($a,$b)
{
$r = $_GLOBALS['someGlobal'];
echo $r;
}
---- mylib.php ----
$_GLOBALS['someGlobal'] = "Random String";
This is a bit trivialized, but is the exact problem I have an I haven't found some related stuff, but nothing that answers my question directly.
When I call function($a,$b) nothing is echo'd, that is - $r is "empty" as if nothing was ever assigned to $_GLOBALS['someGlobal'];
In addition, I have tried with the following:
global $someGlobal;
$someGlobal = "Random String";
Same thing, no effect.
Also, in file.php if I try with global, or with just $someGlobal it still does not work.
As far as I know, from the documentation on php.net using global $someGlobal in mylib.php (and having that inserted in the top level of file.php) that it would not actually do much since it's already at the "top-level" of the scope hierarchy as far as I can tell. However, I thought registering it as a global might allow it to be accessed from inside the function, but this is clearly not the case.
Can anybody explain why, and explain how to get around this?
Edit: I should not that in file.php if I use $_GLOBALS['someGlobal']; the value is recovered fine if it's not in a function.

Wrong variable name. It's $GLOBALS not $_GLOBALS
http://www.php.net/manual/en/reserved.variables.globals.php

From the docs, there is no _ in the $GLOBALS variable:
This works fine for me:
$GLOBALS['glob'] = "string";
function foob() {
echo $GLOBALS['glob'];
}
foob();

It's $GLOBALS, not $_GLOBALS!

Related

How can I check which variables aren't set in an included file, prior to interpretation, or should I?

I have Googled for the past couple hours trying to figure out how to do this. I just want to be clear that my issue is not this issue or that issue, because I am not trying to check inside the script if the variables are set. I am trying to check outside it, to see if they're set / passed to the included file before they're interpreted, or at least meaningfully interpreted to the point where an error is thrown. Let me explain.
A little Background
I am creating a utility package for internal usage at the company I work for. I have chosen to render templates one of two ways: including them or outputting the rendered string.
public function render($context = array()) {
do_action(self::TAG_CLASS_NAME.'_render_view', $this, $context);
if ( empty( $this->html ) ) {
ob_start();
$this->checkContext($context);
extract( $context );
require_once $this->getFullPath();
$renderedView = ob_get_contents();
ob_end_clean();
$this->html = $renderedView;
return $renderedView;
} else {
return $this->html;
}
}
public function includeView($context = array()) {
do_action(self::TAG_CLASS_NAME.'_include_view', $this, $context);
extract( $context );
include $this->getFullPath();
}
The problem
Inside of the render method, I start some output buffering. This is so I can have the interpreter evaluate the code and output the HTML as a string (without taking the eval() hit. Inside my unit tests, I experiemented with what would happen if I left out a context that was inside the template itself. For example: If I have a context array that looks like:
$context = array(
'message' => 'Morning'
);
And an associated template that looks like this:
<?php echo "Hello ".$name."! Good ".$message; ?>
Or this
<p>Hello <?php echo $name; ?>! Good <?php echo $message; ?></p>
Doesn't matter how it's formatted, as long as the context vars are passed to it correctly. Anyway, leaving out the $name in the context will result in a "Undefined variable: $name" E_NOTICE message. Which makes sense. How do you 'capture' that undefined variable before it creates the notice?
I have tried to use:
$rh = fopen($this->getFullPath(), 'r');
$contents = fread($rh, filesize($this->getFullPath()));
fclose($rh);
Where $contents outputs:
"<?php echo sprintf("Hello %s, Good %s.", $name, $greeting); ?>"
The next logical step (for me anyway, thus the question) is to extract the vars in that string. So I briefly started down the road of creating a regex to match on that string and capture the variables, but ended up on here, because I felt like I was duplicating work. I mean, the PHP interpreter already does this effectively, so there must be a way to utilize the built-in functionality. Maybe?
All this to say, I want to do something similar to this psuedo code:
protected function checkContext($context) {
require $filename;
$availVars = get_defined_vars()
if ( $availVars !== $context ) {
setUnDefinedVar = null
}
}
Having said that, this may not even be the right way to do it, but what is? Do I let the interpreter fail on an undefined variable upon inclusion of the file? If I let it fail, am I exposing myself to any security vulnerabilities? Note: I am not setting any variables in templates via $_GET or $_POST.
Any answers are much appreciated. Thank you ahead of time.
I recommend using getters and setters within your class. There may be a better solution but this is how I do it. Since you're trying to access variables in the global scope you would add the following method to the class calling the included file:
public function __get($variable)
{
if (array_key_exists( $variable, $GLOBALS ))
return $GLOBALS[$variable];
return null;
}
So now in your included file you would access variables by using the
$this->{$variableName}
In your specific case, it would look like...
<?php echo "Hello ".$this->name."! Good ".$this->message; ?>
However please note, if the variable requested is defined in the scope of the calling class then that particular member variable will be returned. Not one defined in the global scope.
A better explanation of this overloading operator may be found here PHP Magic Methods

Will empty variables impact my code? (PHP)

In other words, say I have
$existingVariable = 'This is set';
echo thisFunction($existingVariable, $nonExistingVariable);
//included file
function thisFunction($existingVariable){
echo $existingVariable;
}
$nonExistingVariable is no longer there because the included file has changed.
So the way I understand it, $nonExistingVariable would = '' or NULL, right? Does this have any real impact on my code? I'll remove them (or add them back to the included file) before release, but I was just curious if having non-existing variables as an argument risked functionality issues.
It will not affect the function of your code, unless you are using func_get_args(); to work with your arguments instead of just specifying them (you are specifying them, so it won't have any affect)
I.E. you could be doing:
function test() {
$args = func_get_args();
$b = $args[0];
$c = $args[1];
echo "$b\n$c";
}
test('dog','cat');
outputs:
dog
cat
It will affect the readability and user-friendliness of your code moving forward though, as you may try and copy and paste a function call from an old area of code, and get stuck trying to figure out why the variable isn't getting passed into the function (because its no longer an argument).
Why not just remove it, if its not being used anymore?
if the variable doesn't exist then yes PHP will give you an error. "Undefined Variable". You can assign a NULL value, $nonExistingVariable= NULL; Or you can do
if (!empty($nonExistingVariable))
To prevent errors

Require/Include and Undefined Variables in PHP

SOLVED: The error had nothing to do with require or include. It was completely unrelated to the question. Sorry about that. :(
I thought I had an understanding of require/include until now. I have a file named file_one that has something like this, $user_data = return_user_data_as_array(). My second file file_two, calls $user_data. Now, when I do a include 'file_two.php' into the file_one page after the functions and variables have been called, the page returns with undefined variable user_data. I thought that if you include/require a file into your php code it would pick up any variables written in the original file? How would I fix this?
Edit: I also want to mention that although the undefined variable error is popping up on my screen, the "undefined" variables are correctly echoed out.
File One
//this require_once holds the function user_data()
require_once 'core/init.php';
$user_id = 1;
$profile_data = user_data($mysqli, $user_id);
//this calls file two
require_once 'file_two.php';
File Two or file_two.php
echo $profile_data['full_name'];
The key thing here is to have your include before not after the function call. Also just include the function and then call it in file two.
I think you must have an error elsewhere on the page. If you're defining the variable outside of a function or class and you're including the file that uses it after it's been defined it should work fine:
file_one.php
<?php
function return_user_data_as_array(){
return array('name'=>'BenD');
}
$user_data = return_user_data_as_array();
include('file_two.php');
?>
file_two.php
<?php
print_r( $user_data );
?>
Result:
Array
(
[name] => BenD
)
This should all work fine. Your error is probably arising elsewhere (for instance, you might be calling $user_data from inside a function without specifying global $user_data, or you're calling $user_data somewhere in file_one.php accidentally?)
EDIT
Now that you've shown some code, I imagine that the problem is that user_data() isn't returning an array that includes the full_name key. Try print_r($profile_data) to see what the array looks like. I'm guessing that the problem lies in what's being returned from user_data() (if anything! make sure it includes a return $x clause in there! I've spent a lot of time trying to figure out a logical error when the only problem was that my function didn't return anything)
The answer had nothing to do with the problem in the question. The code in the question is correct.

simplifying debugging

Is there a way to achieve the following?
$myvar = 'x';
debug($myvar);
// outputs the following
// myvar value is x
Obviously, for this to happen, the debug function needs to be able to get the variable name passed to it.
Is there a magic constant for that?
And if there isn't, please recommend alternative ways that would simplify the debugging.
Of course, I'm aware of the option where you pass the variable name as a separate argument,
debug('myvar',$myvar);
but my goal is exactly avoid doing that.
Displaying variable name and its value for variable in global scope
Yes, there is, but you will need to pass the name instead:
function debug($var_name) {
printf('%s value is %s', $var_name, var_export($GLOBALS[$var_name], true));
}
or, if you want only value without the parsable formatting:
function debug($var_name) {
printf('%s value is %s', $var_name, $GLOBALS[$var_name]);
}
Displaying variable name and its value for variable in local scope
Attention: This works only for variables in global scope. To do the same for local scope, you will probably need a solution employing get_defined_vars(), like that:
printf('%s value is %s', $var_name, get_defined_vars()[$var_name]);
This cannot be simply enclosed within debug() function. This is because get_defined_vars() returns array representing variables in the scope where get_defined_vars() is called, and we do not need the scope where debug() is defined, don't we?
Unified solution
Unified solution could use global scope as default, but also accept some array representing local scope, so the definition could be:
function debug($var_name, $scope_vars=null) {
if ($scope_vars === null) {
$scope_vars = $GLOBALS;
};
printf('%s value is %s', $var_name, var_export($scope_vars[$var_name], true));
}
and then you can call it like that in global scope:
debug('myvar');
or like that in local scope, passing local scope array:
debug('myvar', get_defined_vars());
Working example
For working example see this demonstration: http://ideone.com/NOtn6
Does it help?
There is also great Google Chrome extension PHP Console with php library that allows to:
See errors & exception in Chrome JavaScript console & in notification popups.
Dump any type variable.
Execute PHP code remotely.
Protect access by password.
Group console logs by request.
Jump to error file:line in your text editor.
Copy error/debug data to clipboard (for testers).
Recommend everyone!
var_dump($my_var);
this is known as print debugging and considered as a very inefficient way for at least four reasons: you waste your time adding and then removing debugging code, debugging code slows down your web, not only the process where you're debugging, you can forget to remove the code after debugging is finished, it's takes time to find and analyze the results -- you see them in the continuous log(s) on the server.
A better aproach is to install special debugger extension and work with your code from an IDE inteded for php like PHPEd

How to avoid call-time pass-by-reference deprecated error in PHP?

I'm trying to reduce the warnings that are sent to my apache server log.
One warning is:
Call-time pass-by-reference has been deprecated.
It is hard for me to imagine why this was deprecated since it is such a useful programming feature, basically I do this:
public function takeScriptsWithMarker(&$lines, $marker) {
...
}
and I call this function repeatedly getting results back from it and processing them but also letting the array $lines build up by being sent into this method repeatedly.
To reprogram this would be extensive.
I don't want to just "turn off warnings" since I want to see other warnings.
So, as call-by-reference is deprecated, what is the "accepted way" to attain the functionality of this pattern: namely of sending an array of strings into a method, have them be changed by the method, then continuing to use that array?
Actually, there's no problem with the way you define the function. Is a problem with the way you call the function. So for your example, instead of calling it like:
takeScriptsWithMarker(&$lines, $marker);
You'd call it like:
takeScriptsWithMarker($lines, $marker); // no ampersands :)
So the feature is still available. But I don't know the reason behind this change.
like noted above in a previous answer, the issue is at CALL time, not definition time.. so you could define a function as:
function foo(&$var1,$var2,$var3=null){
// procesing here
}
then call as:
$return = foo($invar1,$invar2);
your first invar is passed by reference, second one is not.
the error appears when you try to call like so:
$return = foo(&$invar1,$invar2);
You can set allow_call_time_pass_reference to true in your php.ini file. But it's a hack.
You could pass an array with a reference in:
public function takeScriptsWithMarker(array(&$lines, $marker))
which should only take a small amount of refactoring at the other end.
You could pass in the array, let it manipulate it, and then "return" it, instead of messing with the original reference. It shouldn't be too hard to just include a return and assignment.
public function takeScriptsWithMarker($lines, $marker) {
//...
return $lines;
}
Usage:
$lines = takeScriptsWithMarker($lines, $marker);

Categories