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
Related
I'm trying to build a static code analysis tool and I would like to check if the variables in a file are defined. Currently I'm using nikic/PHP-Parser (https://github.com/nikic/PHP-Parser), but I'm not sure if what I'm attempting is even possible.
So my question is: is it possible to check whether is (possibly) set. So does the variable contain a different value than null? Since the code is not executed in static analysis I feel like it might be impossible to "guess" whether the variable might be null, before giving it to a function for example.
An example:
$page = Expertise::find(get_the_ID());
$relatedNews = $page->connectedNews->take(-3)->reverse();
The second line might give us an exception in this case, when $page turned out to be null. I would like to detect these kinds of instabilities in the code using static analysis.
Here's a piece of code of what I'm attempting using PHP-Parser.
class NodeVisitor extends NodeVisitorAbstract
{
public function enterNode(Node $node)
{
if ($node instanceof Node\Expr\Variable) {
// is not null (is set)
// or if that's not possible: is defined before reference?
}
}
}
Edit: to be more clear on why I'm doing this, I'm trying to build an application that detects possible 500 errors without knowing anything about the execution of the code.
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
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
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!
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);