Inexplicable type hinting in PHP code - php

I'm analyzing some PHP code which is running on a server I don't have full access to. I can read the phpinfo though. The code seems to run fine on the server. In my local environment I just can't get the code to run as I get a "Catchable Fatal Error" at some call of a method using type hinting.
someMethod(string $str) {
// Do something...
}
The error says the following: "Argument 1 passed to ... must be an instance of path\of\namespace\string, string given ...".
There is no use keyword with a string class nor can I find anything trough a grep command in the folders of the development environment.
Are there any PHP modules, extensions that can make such a type hinting work? The server and my development environment are using PHP 5.4.25.
What could the live system possibly provide to make such code run? Might it use some other programming language based on PHP like Hack? The rest of the code is pretty straight PHP!

You mention that there is no use statement or namespace declaration that alludes to a 'string' class in the code. Does the code use an Autoloader?
Two possible issues at hand here:
Paths - It is possible that the live environment has another path set up, and/or that a file/class is being loaded/searched for via this 'other path' (outside the code you may be looking at).
Error Handling - Another possible cause is if there is an error handler in the production environment that always returns true.
I had this exact issue where a type-hint wasn't resolving in development, but I didn't realize until we pushed it live and the error handler was no longer registered.
Obviously a fatal parse error can not be stopped, but it turns out a catch-able error can be ignored if the error handler returns true. And this is what I am putting my money on in your case.
Additionally, it is very important to note that there is no way to type-hint a scalar, as one user pointed out.
A simpler way to say it is "it is not possible to type-hint anything that can be represented by a string." This is due to the way in which PHP handles it's more primitive variables, they can all be type-cast to one another, and therefore (because 1, "1" and true can all be == 1, == '1' and == true) it is not actually possible for the interpreter as it is written to actually catch and enforce scalar type-hints.
Answer this question: Is that variable supposed to be $str = "something"; or $str = new string(); (ie, a string or an object)?
If it is supposed to be a string, remove the type-hint, as nothing in PHP allows this support (save for HHVM, but you would know this if you were using it).
Since you say you do not have access to the code, I suggest notifying someone who does.

Related

Get trace call list of PHP eval or override it manually

I am investigating a still undiscovered zero day exploit in Revive Adserver. An attack happen on one location and the attacker was able to invoke an eval which was already in the development and production version of Revive Adserver code base.
I have investigated the access_logs and they indicate the user was doing a POST attack on delivery script fc.php but the payload of POST still remains unclear.
The code base of Revive Adserver is very mixed, old and weird at times. There are lots of points where an eval is called in the code, and one might find something like:
$values = eval(substr(file_get_contents(self::$file), 6));
Which is actually a Smarty template thing, but it looks really scary.
As mentioned, lots and lots of eval appearances are throughout the code and it would take a whole lot time to go through each one at this time.
Is there a possibility to override eval function in PHP to display some trace information, i.e. from which file it was called, on which line did it occur?
If not, is it possible to do this by modifying PHP's C/C++ source code and recompiling it altogether?
Or is there a PHP extension or some tool which can trace all eval callbacks throughout a script?
And if there's no such thing, it would be great if someone would develop it since it would speed up investigating malicious code containing eval's.
is there a possibility to override eval function in PHP to display some trace information, i.e. from which file it was called, on which line did it occur?
Sort of.
You can add eval to disable_functions in php.ini. Then when you call eval you'll get the fatal error function eval not found or such.
Then with a custom error handler.
set_error_handler(function($errno, $errstr, $errfile, $errline){
if(false !== strpos($errstr,'eval')){
throw new Exception();
}else{
return false; //see below
}
//debug_print_backtrace() - I prefer exceptions as they are easier to work with, but you can use this arcane thing too.
});
Or something like that (untested).
Unfortunately you cannot redefine eval as your own function. Eval is not really a function, its a language construct like isset, empty, include etc... For example function_exists('empty') is always false. Some are just more "function" like then others.
In any case you'll probably have to disable eval, I cant really think of a way around that.
Tip
Don't forget you can do this:
try{
throw new \Exception;
}catch(\Exception $e){
echo $e->getTraceAsString();
}
Which both suppresses the exception (so execution continues), and gives you a nice stacktrace.
Tip
http://php.net/manual/en/function.set-error-handler.php
It is important to remember that the standard PHP error handler is completely bypassed for the error types specified by error_types unless the callback function returns FALSE
So given the above, you can/should return false for all other errors. Then PHP will report them. I am not sure it really matters much in this case, as this isn't really meant to be in production code, but I felt it worth mentioning.
Hope it helps.

Count for non countable object PHP 7.2

I have been using PHP 5.4 until now in one of my old projects.
Recently I have decided to upgrade to PHP 7.2
I have around 800 files in my project and old developer team used count() hundreds of time in the project.
In PHP 5.4 even if the variable was not countable it worked as expected but in PHP 7.2 it throws an error.
I could go into each file and change the function but that could take a lot of time and the risk of not changing a couple of function here and there.
Can I extend count() function and write code to make it work as it used to in PHP 5.4?
Is there any other solution to this problem?
I am replacing all if conditions with count($var) functions to is_array($var) && count($var) for now.
Thank you
The difference between php 5.4 and php 7+ on that aspect is not that count changed it's behavior, is that php now turned errors into catchable exceptions.
Your code was failing silently on production with error_repoting turned off. That is extremely dangerous for the business, as the consistency of your app is unreliable, you should change that asap.
Having that said. Since all errors cannot be turned off anymore by setting your error_reporting to 0 (thats because now they are exceptions rather than errors, and that's really nice, php is no more a super unreliable language). You have to catch those exeptions, which are ErrorException.
Go to the index file of your application and add a try-catch block and swipe the dirty down the carpet again.
try {
//super buggy app bootstraping
} catch (ErrorException $ex) {
error_log($ex);
}
Well, i'd rather fix the counts anyway, they are toxic.
Note: is_array($var) && count($var) is not a goo idea, since arrays is not the only countable thing on php, ((is_array($var) || $var instanceof countable) && count($var)) would be a better, but not perfect solution. Also note a bug in php on any version - count(false) - returns 1
In other languages like Python you can use decorators to intercept an existing method without modifying the original method.
However PHP does not support decorators so you will need to manually change every location of that call.
You can use runkit to overload the count() function with your own implementation:
// to override internal functions, this system INI value needs truthy
assert((bool)ini_get('runkit.internal_override'));
// now you can define your own implementation
runkit_function_redefine('count', function ($var) {
return ((is_array($var) || $var instanceof \Countable) ? count($var) : 0);
});
However, please, consider using this only as a temporary stop gap to ease the pain of 5.4 to 7.2. Because this method is opaque, it'll become a maintenance liability at some point, a liability you probably don't want to linger.
Additionally, note that your conversion of is_array($v) && count($v) does not handle all countable things: i.e., \Generator and \Traversable. The runkit override example above does handle them, though.
Fix your code rather, and learn to type. Make sure your count method returns something countable

Why and how does Symfony fill undefined view variable with null values in prod environment?

Consider the following PHP template:
<?= var_dump($myVariable) ?>
This is rendered with the following controller code:
public function myAction() {
return $this->render("MyBundle::test.html.php");
}
Obiously, our variable $myVariable is not set in the template. When looking at this page in the dev environment we get an exception telling us that this variable is not defined. When looking at the page in the prod enviroment we get the output null.
I have debugged this for quite a bit but have not found the place where Symfony decides which variables need to be initialized with null. What happens behind the scenes is that Symfony calls export on the array of view variables (which is empty in our case) and then calls require on the view template itself. Funnily enough, when debugging the view, the variable is never defined, neither in dev nor in prod. But still there is different output for these two environments.
So my questions are:
How does Symfony do this?
Is this intended behavior?
Is this documented somewhere and I just missed it?
Is there a way to change this behavior so that it will also fail in prod and write an error to the log file?
I've only just started with PHP and Symfony, but it seems to me it goes something like this:
Symfony will call the require on the template
Your template starts executing
PHP tries to evaluate $myVariable
PHP will trigger E_NOTICE level error as described in PHP Manual
Here is where different environments come into play. Error level, which is defined under debug.error_handler.throw_at key in debug.xml is -1 (ALL) for dev and 0 (NONE) for prod. So for dev the ErrorHandler in Symfony will throw, for production it won't.

Cannot call a command with multiple method arguments

Every time I attempt to use one of the basic PHPUnit Selenium assertions, the tests errors out and displays this message:
Exception: You cannot call a command with multiple method arguments.
On http://phpunit.de/manual/3.7/en/selenium.html, it shows the usage to be:
void assertElementValueEquals(string $locator, string $text)
Be when I call it with
$this->assertElementValueEquals( 'id=date_1_formatted', '2013-01-01' );
the test produces the above error every time even though this same format seems to be working for others such as in the question Using PHPUnit with Selenium, how can I test that an element contains exactly something?
assertElementValueEquals is not implemented in Selenium2TestCase. On your link it mentioned for SeleniumTestCase (Selenium RC version).
Moreover, you used correct structure with $this->byXPath like here https://github.com/sebastianbergmann/phpunit-selenium/blob/master/Tests/Selenium2TestCaseTest.php
Also you can use $this->byId():
$element = $this->byId('date_1_formatted');
$this->assertEquals('2013-01-01', $element->value());
P. S.: If you are familiar with Selenium IDE, you can try this command line tool.
Walked into this one too, in my case it was my own custom method, so I thought it was a line off.
Turns out I was using a different "buffer" class between the test class and the phpunit than I thought. But because it uses __call() a Lot, it gave ↑ that error instead of "undefined method".

Can I get PHP to throw an error when I try to define a function that has already been defined?

I run into this problem periodically, and I'm trying to figure out if it's a configuration issue or a peculiarity with PHP.
Basically, there will be a function foo() defined somewhere in my code, and then elsewhere I will accidentally define a function again with the same name foo(). I would expect an error of some kind to be thrown, e.g. "Fatal Error: Function already defined line 234, bar.php blah blah".
But I get nothing. I just get a blank screen. Debugging can take an eternity as I try to pinpoint exactly where the function is being accidentally redefined, without help from an error message.
My config settings for reporting errors are set to E_ALL, and I see all other kinds of errors without a hitch.
Any insights? Is there anything I can do to cause PHP to report these errors (so that I can either rename the offending function or wrap it in an if function_exists() clause)?
Edit: To be clear, I understand the many strategies to avoid namespace collisions in PHP. I'm talking about instances where for whatever reason I accidentally name a function a name that already exists during development (or for example I import a file where a function has already been defined). I would expect an error message when this occurs, which would help me debug, but I do not get one.
You can wrap function definitions in a conditional function_exists():
if (!function_exists("file_get_contents"))
{
function file_get_contents(....)
....
}
works for me.
You could wrap a function_exists check around each function and have it throw an error message if it already exists. The better way however would be finding out why fatal errors don't appear on screen. Can you check phpinfo() for the error_reporting and related settings?
This is what you should be getting when trying to redefine a function:
Fatal error: Cannot redeclare file_get_contents() in D:\xyz\htdocs\test.php on line 5
From my phpinfo():
display_errors On
error_reporting 22519 (equals... well, I don't know but it shows errors :)
In some hosting environments when error reporting was turned off completely, I have found defining a custom error handler very helpful: set_error_handler()
Are you hosting the script on your local server? I know a UK based hosting company that prevent any PHP errors from being returned at all, even if you've set E_ALL.
If this is the case consider testing the app locally with error reporting turned on.
As an alternative, you can actually check whether a function is already there by using function_exists.
if (function_exists('foo'))
{
echo 'foo function exists !!';
}
else
{
echo 'foo function does not exists !!';
}
Some suggestions to make your life easier:
I typically will create a generic functions file, where I store all my global functions that I'll be using throughout my app.
I'll also try and remain as object-oriented as possible. PHP will give errors if you're creating an object that already exists, and by encapsulating your functions into logical objects you'll have an easier time maintaining it.
It seems you're not organizing your code properly, try using a common library that you include in your files, and define functions there. Perhaps you just need to start uses classes, then you won't have collisions when you have two functions with the same name.

Categories