LaravelはAssertionErrorを直接あつかうことができない。
ちょっとした小ネタです。
PHP7の地味だけど、意外と重要な新機能としてassert()
が関数から言語構造に変更されました。
Expectation (PHP 7 のみ)
assert() は PHP 7 で言語構造となり、expectation の定義を満たすようになりました。 すなわち、開発環境やテスト環境では有効であるが、運用環境では除去されて、まったくコストのかからないアサーションということです。
今まではassert()
を無効にしていても(assert_options(ASSERT_ACTIVE, 0)
)、assert()
自体の呼び出しのオーバヘッドが発生してしまい、また、評価する式を文字列で記述しなければならないというイマイチ使いづらい関数でした。
zend.assertions
を-1
にすることで、コンパイル時のassert()
のopcode自体が生成されないため、オーバヘッドが0になりますし、文字列で評価式を記述する必要もなくなります。
またassert()
が失敗した場合、今まではASSERT_CALLBACK
で指定したコールバック関数が実行されて、そこで独自の例外に変換してthrowする処理を入れてたりしてたわけですが、assert.exception
を1
に指定すると何もしなくてもAssertionError
という例外オブジェクトをthrowしてくれます。
さて、ここからが本題。
PHP 7.1上のLumen(5.4)上で組んだシステムでこの挙動を確認しようと、こんなコードを書いてみました。
$app->get('/assert', function(){
assert(false);
});
あれ? AssertionError
ではなくてFatalThrowableError
になってる。
なぜだろうとソースを調べること小一時間。FatalThrowableError
はPHPの組み込みの例外ではなくて、Symfonyで独自定義されている例外クラスであることがわかりました。
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug\Exception;
/**
* Fatal Throwable Error.
*
* @author Nicolas Grekas <[email protected]>
*/
class FatalThrowableError extends FatalErrorException
{
public function __construct(\Throwable $e)
{
if ($e instanceof \ParseError) {
$message = 'Parse error: '.$e->getMessage();
$severity = E_PARSE;
} elseif ($e instanceof \TypeError) {
$message = 'Type error: '.$e->getMessage();
$severity = E_RECOVERABLE_ERROR;
} else {
$message = $e->getMessage();
$severity = E_ERROR;
}
\ErrorException::__construct(
$message,
$e->getCode(),
$severity,
$e->getFile(),
$e->getLine()
);
$this->setTrace($e->getTrace());
}
}
ということは、どこかでFatalThrowableError
が生成されてthrowされたということです。さらに調べること小一時間。今度はLumen側のvendor/laravel/lumen-framework/src/Routing/Pipeline.php
でThrowable (AssertionError)
をcatchしてFatalThrowableError
に変換していることがわかりました。
/**
* This extended pipeline catches any exceptions that occur during each slice.
*
* The exceptions are converted to HTTP responses for proper middleware handling.
*/
class Pipeline extends BasePipeline
{
(snip)
/**
* Get the initial slice to begin the stack call.
*
* @param \Closure $destination
* @return \Closure
*/
protected function prepareDestination(BaseClosure $destination)
{
return function ($passable) use ($destination) {
try {
return call_user_func($destination, $passable);
} catch (Exception $e) {
return $this->handleException($passable, $e);
} catch (Throwable $e) {
return $this->handleException($passable, new FatalThrowableError($e));
}
};
}
LumenコンポーネントのソースをThrowable
でgrepしてみると、その他の例外処理の部分で同じような処理をしていることがわかります。
/**
* Get the next job from the queue connection.
*
* @param \Illuminate\Contracts\Queue\Queue $connection
* @param string $queue
* @return \Illuminate\Contracts\Queue\Job|null
*/
protected function getNextJob($connection, $queue)
{
try {
foreach (explode(',', $queue) as $queue) {
if (! is_null($job = $connection->pop($queue))) {
return $job;
}
}
} catch (Exception $e) {
$this->exceptions->report($e);
} catch (Throwable $e) {
$this->exceptions->report(new FatalThrowableError($e));
}
}
Lumen(Laravel) 5.4はPHP 7だけではなく、PHP 5.6をサポートしているので、(Throwable
が扱えない)PHP 5.6のためにこのような変換をしているのでしょう。ちょっと残念な結果でした。
次のLumen(Laravel) 5.5は、PHP5系を切り捨てて、PHP7.0以上のサポートとなるのでThrowable
を直接扱えるようになってると良いなと思います。
Author And Source
この問題について(LaravelはAssertionErrorを直接あつかうことができない。), 我々は、より多くの情報をここで見つけました https://qiita.com/hirohero/items/d9ce1ae432979dcc5e27著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .