Skip to content

Commit 6db59af

Browse files
committed
Merge remote-tracking branch 'origin/8.x' into 8.x
2 parents 5067ccf + 52e2376 commit 6db59af

File tree

2 files changed

+230
-1
lines changed

2 files changed

+230
-1
lines changed

src/Illuminate/Foundation/Bootstrap/HandleExceptions.php

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Exception;
77
use Illuminate\Contracts\Debug\ExceptionHandler;
88
use Illuminate\Contracts\Foundation\Application;
9+
use Illuminate\Log\LogManager;
910
use Symfony\Component\Console\Output\ConsoleOutput;
1011
use Symfony\Component\ErrorHandler\Error\FatalError;
1112
use Throwable;
@@ -52,7 +53,7 @@ public function bootstrap(Application $app)
5253
}
5354

5455
/**
55-
* Convert PHP errors to ErrorException instances.
56+
* Report PHP deprecations, or convert PHP errors to ErrorException instances.
5657
*
5758
* @param int $level
5859
* @param string $message
@@ -66,10 +67,57 @@ public function bootstrap(Application $app)
6667
public function handleError($level, $message, $file = '', $line = 0, $context = [])
6768
{
6869
if (error_reporting() & $level) {
70+
if ($this->isDeprecation($level)) {
71+
return $this->handleDeprecation($message, $file, $line);
72+
}
73+
6974
throw new ErrorException($message, 0, $level, $file, $line);
7075
}
7176
}
7277

78+
/**
79+
* Reports a deprecation to the "deprecations" logger.
80+
*
81+
* @param string $message
82+
* @param string $file
83+
* @param int $line
84+
* @return void
85+
*/
86+
public function handleDeprecation($message, $file, $line)
87+
{
88+
try {
89+
$logger = $this->app->make(LogManager::class);
90+
} catch (Exception $e) {
91+
return;
92+
}
93+
94+
$this->ensureDeprecationLoggerIsConfigured();
95+
96+
with($logger->channel('deprecations'), function ($log) use ($message, $file, $line) {
97+
$log->warning(sprintf('%s in %s on line %s',
98+
$message, $file, $line
99+
));
100+
});
101+
}
102+
103+
/**
104+
* Ensure the "deprecations" logger is configured.
105+
*
106+
* @return void
107+
*/
108+
protected function ensureDeprecationLoggerIsConfigured()
109+
{
110+
with($this->app['config'], function ($config) {
111+
if ($config->get('logging.channels.deprecations')) {
112+
return;
113+
}
114+
115+
$driver = $config->get('logging.deprecations') ?? 'null';
116+
117+
$config->set('logging.channels.deprecations', $config->get("logging.channels.{$driver}"));
118+
});
119+
}
120+
73121
/**
74122
* Handle an uncaught exception from the application.
75123
*
@@ -143,6 +191,17 @@ protected function fatalErrorFromPhpError(array $error, $traceOffset = null)
143191
return new FatalError($error['message'], 0, $error, $traceOffset);
144192
}
145193

194+
/**
195+
* Determine if the error level is a deprecation.
196+
*
197+
* @param int $level
198+
* @return bool
199+
*/
200+
protected function isDeprecation($level)
201+
{
202+
return in_array($level, [E_DEPRECATED, E_USER_DEPRECATED]);
203+
}
204+
146205
/**
147206
* Determine if the error type is fatal.
148207
*
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Foundation\Bootstrap;
4+
5+
use ErrorException;
6+
use Illuminate\Config\Repository as Config;
7+
use Illuminate\Container\Container;
8+
use Illuminate\Foundation\Bootstrap\HandleExceptions;
9+
use Illuminate\Log\LogManager;
10+
use Mockery as m;
11+
use PHPUnit\Framework\TestCase;
12+
use ReflectionClass;
13+
14+
class HandleExceptionsTest extends TestCase
15+
{
16+
protected function setUp(): void
17+
{
18+
$this->container = Container::setInstance(new Container);
19+
20+
$this->config = new Config();
21+
22+
$this->container->singleton('config', function () {
23+
return $this->config;
24+
});
25+
26+
$this->handleExceptions = new HandleExceptions();
27+
28+
with(new ReflectionClass($this->handleExceptions), function ($reflection) {
29+
$property = tap($reflection->getProperty('app'))->setAccessible(true);
30+
31+
$property->setValue(
32+
$this->handleExceptions,
33+
$this->container
34+
);
35+
});
36+
}
37+
38+
protected function tearDown(): void
39+
{
40+
Container::setInstance(null);
41+
}
42+
43+
public function testPhpDeprecations()
44+
{
45+
$logger = m::mock(LogManager::class);
46+
$this->container->instance(LogManager::class, $logger);
47+
$logger->shouldReceive('channel')->with('deprecations')->andReturnSelf();
48+
$logger->shouldReceive('warning')->with(sprintf('%s in %s on line %s',
49+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
50+
'/home/user/laravel/routes/web.php',
51+
17
52+
));
53+
54+
$this->handleExceptions->handleError(
55+
E_DEPRECATED,
56+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
57+
'/home/user/laravel/routes/web.php',
58+
17
59+
);
60+
}
61+
62+
public function testUserDeprecations()
63+
{
64+
$logger = m::mock(LogManager::class);
65+
$this->container->instance(LogManager::class, $logger);
66+
$logger->shouldReceive('channel')->with('deprecations')->andReturnSelf();
67+
$logger->shouldReceive('warning')->with(sprintf('%s in %s on line %s',
68+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
69+
'/home/user/laravel/routes/web.php',
70+
17
71+
));
72+
73+
$this->handleExceptions->handleError(
74+
E_USER_DEPRECATED,
75+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
76+
'/home/user/laravel/routes/web.php',
77+
17
78+
);
79+
}
80+
81+
public function testErrors()
82+
{
83+
$logger = m::mock(LogManager::class);
84+
$this->container->instance(LogManager::class, $logger);
85+
$logger->shouldNotReceive('channel');
86+
$logger->shouldNotReceive('warning');
87+
88+
$this->expectException(ErrorException::class);
89+
$this->expectExceptionMessage('Something went wrong');
90+
91+
$this->handleExceptions->handleError(
92+
E_ERROR,
93+
'Something went wrong',
94+
'/home/user/laravel/src/Providers/AppServiceProvider.php',
95+
17
96+
);
97+
}
98+
99+
public function testEnsuresDeprecationsDriver()
100+
{
101+
$logger = m::mock(LogManager::class);
102+
$this->container->instance(LogManager::class, $logger);
103+
$logger->shouldReceive('channel')->andReturnSelf();
104+
$logger->shouldReceive('warning');
105+
106+
$this->config->set('logging.channels.stack', [
107+
'driver' => 'stack',
108+
'channels' => ['single'],
109+
'ignore_exceptions' => false,
110+
]);
111+
$this->config->set('logging.deprecations', 'stack');
112+
113+
$this->handleExceptions->handleError(
114+
E_USER_DEPRECATED,
115+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
116+
'/home/user/laravel/routes/web.php',
117+
17
118+
);
119+
120+
$this->assertEquals(
121+
[
122+
'driver' => 'stack',
123+
'channels' => ['single'],
124+
'ignore_exceptions' => false,
125+
],
126+
$this->config->get('logging.channels.deprecations')
127+
);
128+
}
129+
130+
public function testEnsuresNullDeprecationsDriver()
131+
{
132+
$logger = m::mock(LogManager::class);
133+
$this->container->instance(LogManager::class, $logger);
134+
$logger->shouldReceive('channel')->andReturnSelf();
135+
$logger->shouldReceive('warning');
136+
137+
$this->config->set('logging.channels.null', [
138+
'driver' => 'monolog',
139+
'handler' => NullHandler::class,
140+
]);
141+
142+
$this->handleExceptions->handleError(
143+
E_USER_DEPRECATED,
144+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
145+
'/home/user/laravel/routes/web.php',
146+
17
147+
);
148+
149+
$this->assertEquals(
150+
NullHandler::class,
151+
$this->config->get('logging.channels.deprecations.handler')
152+
);
153+
}
154+
155+
public function testNoDeprecationsDriverIfNoDeprecationsHereSend()
156+
{
157+
$this->assertEquals(null, $this->config->get('logging.deprecations'));
158+
$this->assertEquals(null, $this->config->get('logging.channels.deprecations'));
159+
}
160+
161+
public function testIgnoreDeprecationIfLoggerUnresolvable()
162+
{
163+
$this->handleExceptions->handleError(
164+
E_DEPRECATED,
165+
'str_contains(): Passing null to parameter #2 ($needle) of type string is deprecated',
166+
'/home/user/laravel/routes/web.php',
167+
17
168+
);
169+
}
170+
}

0 commit comments

Comments
 (0)