Skip to content

Commit

Permalink
feat: Opt-in logging of deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
paulbalandan committed Oct 20, 2022
1 parent ee3e329 commit 68e1ff6
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
25 changes: 25 additions & 0 deletions app/Config/Exceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Config;

use CodeIgniter\Config\BaseConfig;
use Psr\Log\LogLevel;

/**
* Setup how the exception handler works.
Expand Down Expand Up @@ -49,4 +50,28 @@ class Exceptions extends BaseConfig
* ex. ['server', 'setup/password', 'secret_token']
*/
public array $sensitiveDataInTrace = [];

/**
* --------------------------------------------------------------------------
* LOG DEPRECATIONS INSTEAD OF THROWING?
* --------------------------------------------------------------------------
* By default, CodeIgniter converts deprecations into exceptions. Also,
* starting in PHP 8.1 will cause a lot of deprecated usage warnings.
* Use this option to temporarily cease the warnings and instead log those.
* This option also works for user deprecations.
*/
public bool $logDeprecationsOnly = false;

/**
* --------------------------------------------------------------------------
* LOG LEVEL THRESHOLD FOR DEPRECATIONS
* --------------------------------------------------------------------------
* If `$logDeprecationsOnly` is set to `true`, this sets the log level
* to which the deprecation will be logged. This should be one of the log
* levels recognized by PSR-3.
*
* The related `Config\Logger::$threshold` should be adjusted, if needed,
* to capture logging the deprecations.
*/
public string $deprecationLogLevel = LogLevel::WARNING;
}
38 changes: 38 additions & 0 deletions system/Debug/Exceptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Config\Exceptions as ExceptionsConfig;
use Config\Paths;
use ErrorException;
use Psr\Log\LogLevel;
use Throwable;

/**
Expand Down Expand Up @@ -82,6 +83,10 @@ public function __construct(ExceptionsConfig $config, $request, ResponseInterfac
if (! isset($this->config->sensitiveDataInTrace)) {
$this->config->sensitiveDataInTrace = [];
}
if (! isset($this->config->logDeprecationsOnly, $this->config->deprecationLogLevel)) {
$this->config->logDeprecationsOnly = false;
$this->config->deprecationLogLevel = LogLevel::WARNING;
}
}

/**
Expand Down Expand Up @@ -155,6 +160,10 @@ public function exceptionHandler(Throwable $exception)
*/
public function errorHandler(int $severity, string $message, ?string $file = null, ?int $line = null)
{
if ($this->isDeprecationError($severity) && $this->config->logDeprecationsOnly) {
return $this->handleDeprecationError($message, $file, $line);
}

if (! (error_reporting() & $severity)) {
return;
}
Expand Down Expand Up @@ -328,6 +337,35 @@ protected function determineCodes(Throwable $exception): array
return [$statusCode, $exitStatus];
}

private function isDeprecationError(int $error): bool
{
$deprecations = E_DEPRECATED | E_USER_DEPRECATED;

return ($error & $deprecations) !== 0;
}

/**
* @return true
*/
private function handleDeprecationError(string $message, ?string $file = null, ?int $line = null): bool
{
// Remove the trace of the error handler.
$trace = array_slice(debug_backtrace(), 2);

log_message(
$this->config->deprecationLogLevel,
"[DEPRECATED] {message} in {errFile} on line {errLine}.\n{trace}",
[
'message' => $message,
'errFile' => clean_path($file ?? ''),
'errLine' => $line ?? 0,
'trace' => self::renderBacktrace($trace),
]
);

return true;
}

// --------------------------------------------------------------------
// Display Methods
// --------------------------------------------------------------------
Expand Down
47 changes: 47 additions & 0 deletions tests/system/Debug/ExceptionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use CodeIgniter\Test\ReflectionHelper;
use Config\Exceptions as ExceptionsConfig;
use Config\Services;
use ErrorException;
use RuntimeException;

/**
Expand All @@ -32,9 +33,55 @@ final class ExceptionsTest extends CIUnitTestCase

protected function setUp(): void
{
parent::setUp();

$this->exception = new Exceptions(new ExceptionsConfig(), Services::request(), Services::response());
}

/**
* @requires PHP >= 8.1
*/
public function testDeprecationsDoNotThrow(): void
{
$config = new ExceptionsConfig();

$config->logDeprecationsOnly = true;
$config->deprecationLogLevel = 'error';

$this->exception = new Exceptions($config, Services::request(), Services::response());
$this->exception->initialize();

// this is only needed for IDEs not to complain that strlen does not accept explicit null
$maybeNull = PHP_VERSION_ID >= 80100 ? null : 'random string';

try {
strlen($maybeNull);
$this->assertLogContains('error', '[DEPRECATED] strlen(): ');
} catch (ErrorException $e) {
$this->fail('The catch block should not be reached.');
} finally {
restore_error_handler();
restore_exception_handler();
}
}

public function testSuppressedDeprecationsDoNotThrow(): void
{
$config = new ExceptionsConfig();

$config->logDeprecationsOnly = true;
$config->deprecationLogLevel = 'error';

$this->exception = new Exceptions($config, Services::request(), Services::response());
$this->exception->initialize();

@trigger_error('Hello! I am a deprecation!', E_USER_DEPRECATED);
$this->assertLogContains('error', '[DEPRECATED] Hello! I am a deprecation!', false);

restore_error_handler();
restore_exception_handler();
}

public function testDetermineViews(): void
{
$determineView = $this->getPrivateMethodInvoker($this->exception, 'determineView');
Expand Down

0 comments on commit 68e1ff6

Please sign in to comment.