rollbar / rollbar-php-wordpress

Official WordPress plugin from Rollbar, Inc.
https://rollbar.com/
GNU General Public License v2.0
15 stars 20 forks source link

Unable to use closures in PHP settings #89

Open squatto opened 5 years ago

squatto commented 5 years ago

I am using the rollbar_plugin_settings filter to set check_ignore and 'person_fn', which I set to closures. However, this causes the library to throw a fatal error while it is building the PHP config. It's easily replicated by doing this:

add_filter('rollbar_plugin_settings', 'adjust_rollbar_php_settings');

function adjust_rollbar_php_settings(array $settings)
{
    $settings['check_ignore'] = function () {
        //
    };

    return $settings;
}

This results in this error:

Fatal error: Uncaught Error: Call to undefined method Closure::__set_state() in /rollbar/src/Plugin.php(326) : eval()'d code on line 1

This is the code block that's causing the error: (it is in \Rollbar\Wordpress\Plugin::buildPHPConfig())

foreach (UI::settingsOfType(UI::SETTING_INPUT_TYPE_PHP) as $setting) {

    if (isset($config[$setting])) {

        $code = is_string($config[$setting]) ?: 'return ' . var_export($config[$setting], true) . ';';

        $config[$setting] = eval($code);
    }
}

The problem is being caused by the usage of var_export() because of the way that it handles closures:

$closure = function () {};
var_export($closure);
// output:
// Closure::__set_state(array( ))

This is fixed by checking if $config[$setting] is NOT a Closure:

foreach (UI::settingsOfType(UI::SETTING_INPUT_TYPE_PHP) as $setting) {

    if (isset($config[$setting]) && ! ($config[$setting] instanceof \Closure)) {

        $code = is_string($config[$setting]) ?: 'return ' . var_export($config[$setting], true) . ';';

        $config[$setting] = eval($code);
    }
}

I confirmed that this fixes the issue by doing the following:

add_filter('rollbar_plugin_settings', 'adjust_rollbar_php_settings');

function adjust_rollbar_php_settings(array $settings)
{
    $settings['check_ignore'] = function ($isUncaught, $toLog, $payload) {
        dd($isUncaught, $toLog, $payload);
    };

    return $settings;
}

I then triggered an error and the closure was called:

check_ignore_dd

I will submit a PR with the change for you to review. Thanks!