Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining the run-time source of the PHP.INI values that were used to evaluate the pre-processing?

Is there a way to determine, via enhanced error log message or output variable, the value of the CORE PHP DIRECTIVES' values used at run-time? This would be identical to a phpinfo() output for each error.

A basic example is the error_reporting variable. If the /etc/php.ini value is set at x, and the phpinfo() states that that php.ini file is in-fact the intended source of the ini variables. But if a user implements a run-time adjustment to that variable in their code, are we able to output the error_reporting variable at the time when an error is thrown.

Such as when the php.ini file has E_ALL & ~E_STRICT but E_STRICT errors are being reported in the log files, it would be great to have a way to enhance the output to the logfile that shows the "stacktrace" of all the system variables active, or at least modified, and their source for each error reported, perhaps via an error-id added to the httpd log that corresponds to an optional file that shows those run-time settings at the time of the error.

I do realize that a try/catch could be used for this, but I don't believe that could be implemented in practicality for a large deployment of mixed 3rd party code.

Does such a mechanism exist in PHP, or has anyone developed one (google says not likely)? This could be vary valuable for many other cases of errors, by helping expedite the merging of code-bases through relativity of variable settings to error message.

I hope that I'm just looking at this wrong and there is an easy fix I'm missing.

Thanks!

like image 269
Lance Avatar asked May 31 '15 18:05

Lance


People also ask

What is INI_ set() in PHP?

The ini_set() function allows you to change system attributes that affect the way your script is executed. Changes only affect the current script, and will revert back when the script ends. To use ini_set() , pass it the value you want to change as its first parameter, and the new value to use as its second parameter.

How do I run Phpinfo?

The phpinfo() function can be used to output a large amount of information about your PHP installation and can be used to identify installation and configuration problems. To run the function, just create a new file called test. php and place it into the root directory of your web server.

How do I find Phpinfo on Windows?

Check PHP Version by Running PHP Codephp echo 'PHP version: ' . phpversion(); Create the file using a text editor like gedit or Notepad, and upload it to your website's document root directory. Note: While phpinfo() is useful for debugging, the page features sensitive information about your system.

Where is Phpinfo PHP located?

As we mentioned in Step 2, because your phpinfo. php file was placed in your public_html folder, it's now publically available.


4 Answers

I finally have a fully working example with "pecl runkit" as I had trouble installing apd on a fresh distrib (it's an old library).

You can install runkit it with PECL:

 pecl install runkit

and add the following lines in your php.ini:

 extension=runkit.so
 runkit.internal_override=1

I forgot to mention that error_reporting can be defined in different way using error_reporting() function or ini_set. So we have to care about each function. First we copy the old ini_set using runkit_function_copy and then we redefine it using runkit_function_redefine

I use debug_bactrace(1) to get the calling file and line number.

And finally to catch both non-fatal and fatal error, we have to use set_error_handler and register_shutdown_function.

The following code will output, after an error, where ini_set was called and the filename/line.

Error (type=2): Division by zero in file /var/www/html/test.php line 47

INI SET stack

  • error_reporting was defined here (in order):
    • /var/www/html/test.php, line 16, value: 0
    • /var/www/html/test.php, line 17, value: 2
    • /var/www/html/test.php, line 18, value: 1
    • /var/www/html/test.php, line 19, value: 32767

Code:

<?php
runkit_function_copy("ini_set", "old_ini_set");

runkit_function_redefine("ini_set", '$key,$value', '
    global $iniset;
    $trace=debug_backtrace(1);
    $iniset[$key][]=$trace[0];
    old_ini_set($key, $value);');

runkit_function_redefine("error_reporting", '$value', '
    global $iniset;
    $trace=debug_backtrace(1);
    $iniset["error_reporting"][]=$trace[0];
    old_ini_set($key, $value);');

// let test now
ini_set("error_reporting", 0);
ini_set("error_reporting", E_WARNING);
error_reporting(E_ERROR);
ini_set("error_reporting", E_ALL);

set_error_handler("custom_error_handler");
register_shutdown_function("custom_error_handler");

function custom_error_handler($errno=NULL, $errstr=NULL) {
    global $iniset;
    if (!($error=func_get_args())) {
        $error=error_get_last();
        if ($error!=E_ERROR) $error=NULL;
    }
    if ($error) {
        echo "Error (type=$error[0]): $error[1]\n
            in file $error[2] line $error[3]<br />\n";
        echo "INI SET stack<br />\n";
        echo "<ul>";
        foreach ($iniset as $key=>$val) {
            echo "<li>$key was defined here (in order):<ul>";
            foreach ($val as $def) {
                echo "<li>{$def['file']}, line {$def['line']},
                      value: ".array_pop($def['args'])."</li>";
            }
            echo "</li></ul>";
        }

        echo "</table>";
    }
}

// division by 0
12/0;

Previous post:

You can get the value of all local and global configuration options with the ini_get_all() function.

To retrieve the values of both local and global value, you can set the $details parameter to true:

ini_get_all(NULL, true);

You can get the value for individual options with the ini_get() (for runtime value) and get_cfg_var() (for global php.ini value) functions.

$global_value=get_cfg_var("error_reporting");
$local_value=ini_get("error_reporting");

echo "Error reporting (Local value: $local_value, global value: $global_value)\n"

In order to see those results when an error occurs, you have to catch the errors.

For non fatal errors, you can use set_error_handler()

For fatal errors, you have to define a shutdown_function(), see How do I catch a PHP Fatal Error

like image 113
Adam Avatar answered Oct 19 '22 21:10

Adam


You can use error_reporting() without a parameter to return the current error reporting status. To test for particular features, do something like error_reporting() & (E_STRICT | E_COMPILE_WARNING), which would test if E_STRICT and E_COMPILE_WARNING are enabled.

If however, you really want that phpinfo() detail, you can always capture the phpinfo output and log it to the error log (or somewhere else), complete with html formatting. For example:

ob_start();
phpinfo();
error_log(ob_get_clean());

Note that this will only write the phpinfo() to the log, and not output it in the response.

Also, you may want to consider using xdebug. If you're looking for a better stack trace, that may help.

like image 35
James K. Avatar answered Oct 19 '22 21:10

James K.


The scope of your question is a bit unclear, so I write what I know about PHP's error handling and give the task making sense out of it back to you.

Detecting code changing php.ini settings

Part of your question suggest that you are running code that unexpectedly changes the settings from their default values, and you get error types that should have been disabled.

The solution for this would be to not change the settings in the code, and to find those places, there is a tool: PHP Code Sniffer. It has a rule "ForbiddenFunction" that can be customized to find calls to ini_set, error_reporting and others.

You'd scan the code for calls to these functions and then analyze why they are there. Simply removing them likely isn't the correct solution, because if code changes error settings, there is a reason. Maybe the code incorrectly sets the value back to the original.

Getting at the current values of settings at runtime

You can register a callback that is executed during regular code execution with register_tick_function. It will be called every number of "ticks", which is the number of low level statements. Effectively, you can make the registered function jump in almost after every regular script statement - note that this will affect run time.

The function itself can try to analyze whatever you like, and react to changes.

Getting at the current values in case of errors/exceptions

You can register your own error and exception handler. set_error_handler accepts a callback that will be called with all information about any error that was triggered (not including fatal errors - if the PHP engine fails e.g. with syntax errors, nothing can be called anymore).

bool handler ( 
    int $errno , 
    string $errstr 
    [, string $errfile 
     [, int $errline 
      [, array $errcontext 
    ]]] 
)

The function will be called regardless of which setting of error_reporting() was chosen, but it can also read the setting and ignore calls with disabled errors. So in your case, it could read the global php.ini setting (or get it hardcoded) and ignore all other errors. Or report these errors in a special log file. Returning false from this function will call the regular error handling, returning true will go on as if nothing happened.

Access to global variables are possible via the $GLOBALS array.

Thrown exceptions that are not being catched will be handled by a callback registered with set_exception_handler. The callback will get the exception object, which has everything from message to stack trace.

And now?

What about repairing the code? The strict errors are there for a reason: Your code is likely to break when updating PHP. You are not giving details, so this has to be a generic suggestion, but I wonder what is the background of your question. I have a feeling of you asking the wrong question or trying to wipe code problems under the carpet. You can do that, but it will hurt eventually - and if the current situation already hurts, it will hurt even more later. Go for my first suggestion: Find the problems in the code and fix them appropriately. Everything else is not fixing code, but reducing it's error log footprint.

like image 21
Sven Avatar answered Oct 19 '22 21:10

Sven


I too encountered this issue recently, so phpinfo() was showing it had loaded the correct php.ini but no matter what I do, it just won't use the values I set for the php directives it (PHP) just won't use it.

PHP phpinfo() directory

So I just searched the additional directory /etc/php/7.4/apache2/ for the string key of the directive (memory_limit in my case) as follows

grep -r 'memory_limit' /etc/php/7.4/apache2/

I found that one of the "Additional .ini files parsed" was overriding my php.ini for that directive. Once I got rid of that override, my changes in php.ini started reflecting correctly.

like image 1
Mohd Abdul Mujib Avatar answered Oct 19 '22 21:10

Mohd Abdul Mujib