Check whether variable is defined using nikic/php-parser - php

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.

Related

Testing method with no output

I have the following method I want to test:
class SomeObject {
public function actionFromSomeController() {
$obj = new OtherObject();
$obj -> setAttributes();
$obj -> doAction();
}
}
class OtherObject {
private $_attr;
public function setAttributes() {
$this -> _attr = 'something';
Database :: execute('INSERT INTO table VALUES (' . $this -> _attr . ')');
$fileObj = new FileObj();
$content = $fileObj -> getSomeFileContent();
// do something else
}
public function doAction() {
echo $this -> _attr;
}
}
Now I want to test this method, its output depends on database content and one file on the server. It does a lot of things on the way, and the output is just one ID and success => 1.
How should I test it properly?
Some ideas on how to test small code pieces like this:
Generate test-data and pass it to your methods (also, fake database return data or file contents)
Use echo / var_dump() / die() to check property and variable content at different positions in your methods
Also use these commands to check whether execution reaches a certain point (for example to see whether a function got called or not)
If something doesn't work as expected without an error message: Check line by line with the above methods until you find the problem
Consider using interfaces and dependency injection if your code gets bigger - this is a bit over-the-top for this amount of code, but can be a tremendous time-saver when your application becomes big
Testing is never an automatic process and you will always have to think about what makes sense to do and what not. These things to do are never magic but basic PHP.
You should consider letting your scripts throw errors/exceptions if something goes wrong. Writing "silent" applications is almost never good since you can, if you really need a silent execution for production environments, just turn off error reporting and have the same effect. Many PHP functions return something special on failure and/or success and you can check for this. Database handlers do so, too. Do yourself a favor and use these return values!

How to properly call a class while providing error reporting at the class-caller level

I am writing fresh code, as part of refactoring an older legacy codebase.
Specifically, I am writing a Device class that will be used to compute various specifications of a device.
Device class depends on device's model number and particle count and I can call it as $device = new Device($modelNumber, $particleCount);
Problem: since this class will go into existing legacy code, I have no direct influence on if this class will be called properly. For Device to work, it needs to have correct model number and correct particle count. If it does not receive the proper configuration data, internally device will not be initialized, and the class will not work. I think that I need to find a way to let the caller know that there was an error, in case an invalid configuration data was supplied. How do I structure this to be in line with object oriented principles?
Or, alternatively, do I need to concern myself with this? I think there is a principle that if you supply garbage, you get garbage back, aka my class only needs to work properly with proper data. If improper data is supplied, it can bake a cake instead, or do nothing (and possibly fail silently). Well, I am not sure if this principle will be great. I do need something to complain if supplied configuration data is bad.
Here is some code of what I am thinking:
$device = new Device($x, $y);
$device->getData();
The above will fail or produce bad or no data if $x or $y are outside of device specs. I don't know how to handle this failure. I also want to assume that $device is valid when I call getData() method, and I can't make that assumption.
or
$device = new Device($x, $y);
if ($device->isValid())
$device->getData();
else
blow_up("invalid device configuration supplied");
The above is better, but the caller has to now they are to call isValid() function. This also "waters down" my class. It has to do two things: 1) create device, 2) verify device configuration is valid.
I can create a DeviceChecker class that deals with configuration vefication. And maybe that's a solution. It bothers me a little that DeviceChecker will have to contain some part of the logic that is already in Device class.
Questions
what problem am I trying to solve here? Am I actually trying to design an error handling system in addition to my "simple class" issue? I think I probably am... Well, I don't have the luxury of doing this at the moment (legacy code base is huge). Is there anything I can do now that is perhaps localized to the pieces of code I touch? That something is what I am looking for with this question.
I think you need to use below code to verify your passed arguments in construct
class Device {
public function __constructor($modelNumber, $particleCount) {
if(!$this->isValid($modelNumber, $particleCount) {
return false; //or return any error
}
}
}
This will check the passed params are valid or not and create object based on that only, otherwise return false or any error.

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

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

Php: Disable debug function in runtime

in php i wrote my own debug function which have two arguments: text and a level of message. However i could be also you the php functions for triggering errors. But to debug in development i use sometimes like this:
debug($xmlobject->asXML(),MY_CONSTANT);
now i want to know whether it is a lack of performance in non debug executing because the arguments are calculated indepent whether they will be used inside function? and how to do that right that is only calculated if i need?
Thanks for your help,
Robert
If you write the following portion of code :
debug($xmlobject->asXML(),MY_CONSTANT);
Then, no matter what the debug() function does, $xmlobject->asXML() will be called and executed.
If you do not want that expression to be evaluated, you must not call it; I see two possible solutions :
Remove the useless-in-production calls to the debug() function, not leaving any debugging code in your source files,
Or make sure they are only executed when needed.
In the second case, a possibility would be to define a constant to configure whether or not you are in debug-mode, and, then, only call debug() when needed :
if (DEBUG) {
debug($xmlobject->asXML(),MY_CONSTANT);
}
Of course, the makes writting debbuging-code a bit harder... and there is a bit of performance-impact (but far smaller than executing the actual code for nothing).
The arguments are sended by value, ergo the method ->asXML() is executed always.

Categories