PHP 8.1.24 Released!

assert

(PHP 4, PHP 5, PHP 7, PHP 8)

assertアサーションの値を調べる

説明

assert(mixed $assertion, Throwable|string|null $description = null): bool

assert() は関数ではなく、言語構造です。 これは、expectation の定義を満たします。すなわち、 開発環境やテスト環境では有効であるが、 運用環境では除去されて、まったくコストのかからないアサーションということです。

アサーションは、デバッグ目的にのみ使用するべきです。 アサーションを常に true となる条件を調べる不具合診断に使用し、 true でない場合に何らかのプログラミングエラーを示したり、 extension 関数または特定のシステム制限や機能といった、 特定の機能の存在をチェックするために使用することが可能です。

アサーションは削除するように設定できるため、 入力パラメータのチェックのような通常の実行動作に使用するべきでは ありません。 一般的には、アサーションのチェックを無効にしてもそのコードが正常に動作しなければなりません。

assert()assertion で指定された expectation をチェックします。 それを満たさない場合、この関数の結果は false になり、 assert() の設定に応じて適切な動作をします。

assert() の振る舞いは、以下のINI設定によって制御できます:

Assert 設定オプション
名前 デフォルト 説明 変更履歴
zend.assertions 1
  • 1: コードを生成して実行する (開発モード)
  • 0: コードを生成するが、実行時には読み飛ばす
  • -1: コードを生成しない (運用モード)
 
assert.active true false を指定すると、 assert() は expectation をチェックせず、 無条件に true を返します。  
assert.callback null アサーションが失敗した時にコールされるユーザー定義の関数。 シグネチャは以下の形であるべきです:
assert_callback(
    string $file,
    int $line,
    null $assertion,
    string $description = ?
): void
PHP 8.0.0 より前のバージョンでは、 コールバックのシグネチャは以下の形であるべきでした:
assert_callback(
    string $file,
    int $line,
    string $assertion,
    string $description = ?
): void
assert.exception true true を指定すると、expectation を満たさない場合に AssertionError がスローされます。  
assert.bail false true を指定すると、expectation を満たさない場合に PHP スクリプトの実行を停止します。  
assert.warning true true を指定すると、expectation を満たさない場合に E_WARNING が発生します。 このINI設定は assert.exception が有効な場合は意味がありません。  
assert. ではじまるオプションは、 assert_options() でも設定できますが、おすすめできません。

パラメータ

assertion

値を返すあらゆる式を指定できます。 この式を実行した結果を用いて、アサーションに成功したか否かを判断します。

警告

PHP 8.0.0 より前のバージョンでは、 assertion に文字列を指定すると、 PHP コードとして解釈され、 eval() 経由で実行されていました。 この文字列はコールバックの3番目の引数として渡されていました。 この振る舞いは PHP 7.2.0 以降では 非推奨 になり、 PHP 8.0.0 で 削除されました

description

descriptionThrowable のインスタンスの場合、 assertion が実行され、 かつ失敗した場合にスローされます。

注意:

PHP 8.0.0 以降では、 定義済みのアサーションのコールバックがコールされる に上の処理は行われます。

注意:

PHP 8.0.0 以降では、 assert.exception の設定に関わらず、object がスローされます。

注意:

例外がスローされる場合、 PHP 8.0.0 以降では、 assert.bail は意味がありません。

description が文字列の場合、 例外や警告が発生した場合にここで指定したメッセージを使います。 assertion が失敗したときのオプションのメッセージを指定します。

description は省略できます。 デフォルトの値は、assert() を呼び出したソースコードと同じです。この値はコンパイル時に生成されます。

戻り値

assertionfalse の場合は false を返します。 そうでない場合は、true を返します。

変更履歴

バージョン 説明
8.0.0 assert() は、文字列の引数を評価しなくなりました。 代わりに、他の引数と同じ扱いをされるようになっています。 assert('$a == $b') ではなく、assert($a == $b) を使うべきです。 php.ini ディレクティブ assert.quiet_eval と定数 ASSERT_QUIET_EVAL も削除されており、 それらを使っても何も起きなくなっています。
8.0.0 descriptionThrowable のインスタンスの場合、 assertion が失敗した場合、 assert.exception の値に関わらず、object がスローされるようになりました。
8.0.0 descriptionThrowable のインスタンスの場合、 たとえ設定されていてもコールバックは呼び出されません。
8.0.0 名前空間の内部で、assert() という名前の関数を宣言することはできなくなりました。 宣言した場合、E_COMPILE_ERROR が発生します。
7.3.0 名前空間の内部で、assert() という名前の関数を宣言することは推奨されなくなりました。 宣言した場合、 E_DEPRECATED が発生するようになっています。
7.2.0 assertionstring を使うことは推奨されなくなりました。 assert.activezend.assertions が両方 1 に設定されると E_DEPRECATED レベルの警告が発生するようになりました。

Expectation

<?php
assert
(true == false);
echo
'Hi!';
?>

zend.assertions が 0 の場合は、 上の例の結果は次のようになります:

Hi!

zend.assertions が 1、 かつ assert.exception が 0 の場合は、上の例の結果は次のようになります:

Warning: assert(): assert(true == false) failed in - on line 2
Hi!

zend.assertions が 1、 かつ assert.exception が 1 の場合は、上の例の結果は次のようになります:

Fatal error: Uncaught AssertionError: assert(true == false) in -:2
Stack trace:
#0 -(2): assert(false, 'assert(true == ...')
#1 {main}
  thrown in - on line 2

例1 自作の例外を用いた expectation

<?php
class CustomError extends AssertionError {}

assert(true == false, new CustomError('True is not false!'));
echo
'Hi!';
?>

zend.assertions が 0 の場合は、 上の例の結果は次のようになります:

Hi!

zend.assertions が 1、 かつ assert.exception が 0 の場合は、上の例の結果は次のようになります:

Warning: assert(): CustomError: True is not false! in -:4
Stack trace:
#0 {main} failed in - on line 4
Hi!

zend.assertions が 1、 かつ assert.exception が 1 の場合は、上の例の結果は次のようになります:

Fatal error: Uncaught CustomError: True is not false! in -:4
Stack trace:
#0 {main}
  thrown in - on line 4

評価済みのコードによるアサーション (PHP 7 のみ)

評価済みのコードを使って アサーションをチェックする場合、 assert() のコールバックは特に有用です。 なぜなら、アサーションに使うコードが、 アサーションのチェックが終わったときの情報と一緒にコールバックに渡されるからです。

例2 失敗した アサーションをカスタムハンドラで処理する

<?php
// assertを有効にし、出力を抑制する
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_QUIET_EVAL, 1);

// ハンドラ関数を作成する
function my_assert_handler($file, $line, $code)
{
echo
"<hr>Assertion Failed:
File '
$file'<br />
Line '
$line'<br />
Code '
$code'<br /><hr />";
}

// コールバックを設定する
assert_options(ASSERT_CALLBACK, 'my_assert_handler');

// 失敗するアサーションを作成
$array = [];
assert('count($array);');
?>

上の例の PHP 7.2 での出力は、このようになります。

Deprecated: assert(): Calling assert() with a string argument is deprecated in test.php on line 21
<hr>Assertion Failed:
        File 'test.php'<br />
        Line '21'<br />
        Code 'count($array);'<br /><hr />

上の例の PHP 7.1 での出力は、このようになります。

<hr>Assertion Failed:
        File 'test.php'<br />
        Line '21'<br />
        Code 'count($array);'<br /><hr />

例3 カスタムハンドラを使った説明の表示

<?php
// assertを有効にし、出力を抑制する
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_QUIET_EVAL, 1);

// ハンドラ関数を作成する
function my_assert_handler($file, $line, $code, $desc = null)
{
echo
"Assertion failed at $file:$line: $code";
if (
$desc) {
echo
": $desc";
}
echo
"\n";
}

// コールバックを設定する
assert_options(ASSERT_CALLBACK, 'my_assert_handler');

// 失敗するアサーションを作成
assert('2 < 1');
assert('2 < 1', 'Two is less than one');
?>

上の例の PHP 7.2 での出力は、このようになります。

Deprecated: assert(): Calling assert() with a string argument is deprecated in test.php on line 21
Assertion failed at test.php:21: 2 < 1

Deprecated: assert(): Calling assert() with a string argument is deprecated in test.php on line 22
Assertion failed at test.php:22: 2 < 1: Two is less than one

上の例の PHP 7.1 での出力は、このようになります。

Assertion failed at test.php:21: 2 < 1
Assertion failed at test.php:22: 2 < 1: Two is less than one

参考

add a note

User Contributed Notes 9 notes

up
26
hodgman at ali dot com dot au
15 years ago
As noted on Wikipedia - "assertions are primarily a development tool, they are often disabled when a program is released to the public." and "Assertions should be used to document logically impossible situations and discover programming errors— if the 'impossible' occurs, then something fundamental is clearly wrong. This is distinct from error handling: most error conditions are possible, although some may be extremely unlikely to occur in practice. Using assertions as a general-purpose error handling mechanism is usually unwise: assertions do not allow for graceful recovery from errors, and an assertion failure will often halt the program's execution abruptly. Assertions also do not display a user-friendly error message."

This means that the advice given by "gk at proliberty dot com" to force assertions to be enabled, even when they have been disabled manually, goes against best practices of only using them as a development tool.
up
4
Tom
3 years ago
When migrating older code to PHP 7.2+, you may get E_DEPRECATED warnings for every call to assert() you ever wrote, urging you to not pass the assertion as a string.

It may be tempting to just run a regular expression across your files to convert all strings within "assert(...)" to statements. But, before you do that, be aware of the following caveat!

For example, this code simply asserts that $input is not empty.

assert('$input;');

This works, because the string passed to assert() is evaluated as a PHP statement and the result cast to Boolean.

If you want to have an equivalent statement that doesn't pass the first parameter as a string, your regular expression should rewrite this statement as:

assert((bool) ($input));

However, this looks a bit bulky and it is tempting to instead opt for the more direct approach to convert the above line to this:

assert($input);

But! This new statement will do one of three things:

1) Looks as if it worked as intended because $input just happens to be Boolean to begin with
2) Throw a parse error if $input is a string (best case)
3) Allow an attacker on a poorly configured server to execute arbitrary PHP-Code (worst case)

The reason is that, even though on PHP 7.2+ a E_DEPRECATED warning is raised, if assert() finds the first parameter to be a string, it will still execute it as PHP-Code, just as if it was called with a string to begin with.

If an attacker finds a way to manipulate the contents of $input, you might end up with a remote code execution vulnerability. So just be extra careful when migrating assertions.
up
6
mail<at>aaron-mueller.de
17 years ago
Here is a simple demonstration of Design By Contract with PHP

<?php

assert_options
(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_BAIL, 1);
assert_options(ASSERT_CALLBACK, 'dcb_callback');

function
dcb_callback($script, $line, $message) {
echo
"<h1>Condition failed!</h1><br />
Script: <strong>
$script</strong><br />
Line: <strong>
$line</strong><br />
Condition: <br /><pre>
$message</pre>";
}

// Parameters
$a = 5;
$b = 'Simple DCB with PHP';

// Pre-Condition
assert('
is_integer($a) &&
($a > 0) &&
($a < 20) &&

is_string($b) &&
(strlen($b) > 5);
'
);

// Function
function combine($a, $b) {
return
"Kombined: " . $b . $a;
}

$result = combine($a, $b);

// Post-Condition
assert('
is_string($result) &&
(strlen($result) > 0);
'
);

// All right, the Function works fine
var_dump($result);

?>
up
4
Krzysztof &#39;ChanibaL&#39; Bociurko
16 years ago
Note that func_get_args() should be used carefully and never in a string! For example:

<?php
function asserted_normal($a, $b) {
assert(var_dump(func_get_args()));
}
function
asserted_string($a, $b) {
assert('var_dump(func_get_args())');
}
?>

<?php asserted_normal(1,2) ?> prints
array(2) {
[0]=>
int(1)
[1]=>
int(2)
}

but <?php asserted_string(3,4) ?> prints
array(1) {
[0]=>
string(25) "var_dump(func_get_args())"
}

This is because of that the string passed to assert() is being evaled inside assert, and not your function. Also, note that this works correctly, because of the eval scope:

<?php
function asserted_evaled_string($a, $b) {
assert(eval('var_dump(func_get_args())'));
}
asserted_evaled_string(5,6);
?>
array(2) {
[0]=>
int(5)
[1]=>
int(6)
}

(oh, and for simplicity's sake the evaled code doesn't return true, so don't worry that it fails assertion...)
up
1
Julien MOREAU aka PixEye
1 year ago
In order to change zend.assertions or assert.exception values, try with the ini_set() function but be aware that it may fail.

Example:
<?php
$ret
= @ini_set('zend.assertions', '1');
if (
$ret === false) echo 'ini_set() failed before line ', __LINE__, PHP_EOL;
up
-2
jason at jaypha dot com
5 years ago
You can take advantage of how assert is handled to use it for crude conditional compilation.

For example

<?php
assert
(print("Some debug message\n"));
assert(($val = "dev") || true);
?>

Since print() always returns 1, the topmost assertion will pass. For others, you may need to add a || true. Always enclose the expression in ().

In a development environment where zend.assertions=1, the above code will execute. In production environments where zend.assertions=-1, it wont even compile, thus not burdening performance.

Another, more real world, example.

<?php
$cssSrc
= 'https://code.jquery.com/jquery-3.2.1.min.js';
assert(($cssSrc = 'http://dev.local/jquery-3.2.1.js') || true);
echo
"<link rel='stylesheet' type='text/css' href='$cssSrc'/>\n";
?>

In a production environment, The website will use the minified version from the CDN. In a development environment, a development version, sourced locally, will be used instead.

Note: This will not work for everything. Only code that can be embedded in an expression will work.
up
-2
Ben
7 years ago
if there was no 'warning' message when assertion failed (FALSE), try reset the error handler:
<?php
set_error_handler
( null );
up
-5
uramihsayibok, gmail, com
13 years ago
There's a nice advantage to giving assert() some code to execute, as a string, rather than a simple true/false value: commenting.

<?php

assert
('is_int($int) /* $int parameter must be an int, not just numeric */');

// and my personal favorite
assert('false /* not yet implemented */');

?>

The comment will show up in the output (or in your assertion handler) and doesn't require someone debugging to go through your code trying to figure out why the assertion happened. That's no excuse to not comment your code, of course.

You need to use a block comment (/*...*/) because a line comment (//...) creates an "unexpected $end" parse error in the evaluated code. Bug? Could be.
(You can get around it with "false // not yet implemented\n" but that screws up the message)
up
-8
office dot stojmenovic at gmail dot com
10 years ago
Example from Ikac Framework how they use assert()

<?php

/**
* Set Assertion Debug
*
* This method will check the given assertion and take appropriate -
* action if its result is FALSE.
*
* This file is part of Ikac Framework.
*
* @package Ikac Framework
* @author Ivan Stojmenovic Ikac <contact.@stojmenovic.info>
*
* @param mixed $assertion The assertion.
* @param mixed $callback Callback to call on failed assertions
* @param array $options Set the various control options or just query their current settings.
* @param string $description An optional description that will be included in the failure message if the assertion fails.
*/
public function setAssertionDebug($assertion, $callback, array $options, $description = null)
{
if (
is_array($options)) {
foreach (
$options AS $option => $value) {
assert_options($option, $value);
}
}
if (
$callback) {
assert_options(ASSERT_CALLBACK, $callback);
}

return
assert($assertion, $description);
}
?>

How to use:

<?php
use Ikac\Component\SystemBehaviour\OptionsInfo;

$system = new OptionsInfo();

$option = array(ASSERT_ACTIVE => 1,ASSERT_WARNING => 0,ASSERT_QUIET_EVAL => 1);

$system->setAssertionDebug('2<1', function(){
echo
"Assertion failed";
},
$option);

?>
To Top