PHPコードでassertを使用する必要がありますか?


87

同僚が、ライブラリ内のifステートメントを使用して例外をスローした場所にassertコマンドを数回追加しました。(私はこれまでアサートについて聞いたことがありませんでした。)これは彼がそれをどのように使用したかの例です:

assert('isset($this->records); /* Records must be set before this is called. */');

私はしただろう:

if (!isset($this->records)) {
    throw new Exception('Records must be set before this is called');
}

assertに関するPHPのドキュメントを読むと、assertを使用する前に、assertがアクティブであることを確認し、ハンドラーを追加することをお勧めします。彼がこれをした場所が見つかりません。

だから、私の質問は、上記のことを考えると、assertを使用するのは良い考えですか?ifや例外の代わりにもっと頻繁に使用する必要がありますか?

また、これらのライブラリは、私たちが参加していない可能性のあるプロジェクトも含め、さまざまなプロジェクトやサーバーで使用することを計画しています(ライブラリはオープンソースです)。これはassertの使用に違いはありますか?


それは本当に'isset(のコード行assert)ですか?だけではありませんisset(一重引用符なしで')?
PeterMortensen19年

回答:


79

ほとんどの言語(私が漠然と知っていることすべて)に適用できる経験則はassert、条件が常に真であることを主張するために使用されますが、if失敗することがあると考えられる場合は適切です。

この場合、与えられたメソッドが呼び出される前に常に設定する必要あるassertため、(状況についての私の弱い理解に基づいて)それは適切であると言えます。したがって、レコードの設定に失敗すると、実行時の状態ではなく、プログラムのバグになります。ここで、は、(適切なテストを行って)保護されているコードが設定されずに呼び出される原因となる可能性のあるプログラム実行パスがないことを確認するのに役立ちます。recordsassertassertrecords

assertifは対照的に使用する利点は、assert通常、本番コードでオフにできるため、オーバーヘッドが削減されることです。最適に処理される種類の状況はif、本番システムの実行時に発生する可能性があるため、オフにできないことによって失われることはありません。


4
これに加えて、本番コードでアサーションを無効にしたくない場合があります。アサーションは、これらの「これは決して起こらない」条件がそのまま維持されるようにするのに役立つからです。存在してはならない実行パスに沿ってユーザーを続行させるよりも、アプリケーションをアサートから停止させる方がよい場合があります。
derekerdmann 2010

2
@derekerdmann:本当です。一部のアサーションでは、(本番環境で)ログに記録するか、(開発環境で)警告を出力するだけで十分な場合があります。ただし、多くの場合、assertはセキュリティ関連のコードも保護するため、を有効にすることをお勧めしますassert_options(ASSERT_BAIL)。とにかく、手動のif / throw回避策よりも高速です。
マリオ2010

4
@derekerdmann私はこれに同意しません(phpでassert()を使用する場合)。assert()はすべての文字列引数をPHPコードとして扱うため、これは大きな脆弱性のギャップです。したがって、(理論的には)任意のコードを挿入して実行することが可能です。IMHO、アサーションは本番
環境で

2
@VitaliyLebedevインジェクションの影響を受けたくない場合は、アサートする文字列を渡さないでください。
Damon Snyder

9
パーティーに遅れましたが、PHP.netは次のように述べています。「アサーションはデバッグ機能としてのみ使用する必要があります。」
公園。

25

アサーションを「パワーコメント」と考えてください。次のようなコメントではなく:

// Note to developers: the parameter "a" should always be a number!!!

使用する:

assert('is_numeric(a) /* The parameter "a" should always be a number. */');

意味はまったく同じで、まったく同じ聴衆を対象としていますが、最初のコメントは(感嘆符がいくつあっても)簡単に忘れられたり無視されたりしますが、「パワーコメント」は人間が読んで理解できるだけではありません。また、開発中に常にマシンテストが行​​われ、コードや作業習慣で適切なアサート処理を設定しても無視されません。

このように見ると、アサーションはif(error)...や例外とはまったく異なる概念であり、共存できます。

はい、コードにコメントを付ける必要があります。また、可能な限り「パワーコメント」(アサート)を使用する必要があります。


テスト時に開発時に常に良好な条件をアサーションに渡すが、本番環境ではアサーションがオフになっている場合はどうなりますか?一部のユーザーは、テスト時に考えていなかった別の条件を渡しますか?または、アサートを常にオンにしておく必要がありますが、それは独自のチェックを作成することと同じではありませんか?
Darius.V 2014

その後、プログラムは失敗します。言語と開発環境のifステートメントとエラー処理機能を使用して適切に修正してください。アサートは問題を明らかにすることができます、問題を修正するより良い方法があります。
DaveWalley 2014

PHP 7.2以降、評価のために文字列をassertに渡すことは非推奨になっていることに注意してください。とても便利に見えたので悲しい。
Jannie Theunissen

16

それは完全にあなたの開発戦略に依存します。ほとんどの開発者はassert()、ダウンストリームの単体テストを認識しておらず、使用しています。ただし、プロアクティブで組み込みのテストスキームが有利な場合があります。

assertは有効または無効にできるため、便利です。そのようなアサーションハンドラーが定義されていない場合、パフォーマンスは低下しません。同僚にはコードがないため、開発環境で一時的に有効にするコードを考案する必要があります(E_NOTICE / E_WARNINGがオンの場合は、アサーションハンドラーもオンにする必要があります)。コードが混合変数タイプを処理できない場合に時々使用します-通常、弱い型のPHPで厳密な型指定を行うことはありませんが、ランダムなユースケースがあります。

 function xyz($a, $b) {
     assert(is_string($a));
     assert(is_array($b));

たとえば、型指定子の不足を補うでしょうstring $a, array $b。PHP5.4はそれらをサポートしますが、チェックはしません。


「php5.4にはそれらがありますがチェックされません」とはどういう意味ですか?
kzqai 2012

1
PHP 5.4は、アサートをサポートし、チェックします。
DaveWalley 2014

7

アサートは、if開発中のデバッグにのみ使用されることを目的としているため、または例外のような通常のフロー制御の代わりにはなりません。


6

7より前のPHPでのassertに関する重要な注意事項。assert構造を持つ他の言語とは異なり、PHPはassertステートメントを完全にスローしません。PHPはそれを関数として扱います(アサーションによって呼び出される関数でdebug_backtrace()を実行します)。アサートをオフにすると、関数がエンジンで何もしないように配線されているように見えます。PHP 7は、通常の1(オン)または-1(オフ)の値ではなく、zend.assertionsを0に設定することで、この動作をエミュレートできることに注意してください。

この問題は、assertが任意の引数を取ることで発生しますが、引数が文字列でない場合、assertはassertがオンかオフかに関係なく式の結果を取得します。これは、次のコードブロックで確認できます。

<?php
  function foo($a) { 
    echo $a . "\n"; 
    return TRUE;
  }
  assert_options(ASSERT_ACTIVE, FALSE);

  assert( foo('You will see me.'));
  assert('foo(\'You will not see me.\')');

  assert_options(ASSERT_ACTIVE, TRUE);

  assert( foo('Now you will see'));
  assert('foo(\'both of us.\')');

アサートの意図を考えると、これはバグであり、アサートがPHP 4で導入されて以来、この言語で使用されてきたため、長年のバグです。

assertに渡された文字列は評価され、それに伴うすべてのパフォーマンスへの影響と危険性がありますが、これは、アサートステートメントをPHPで正常に機能させる唯一の方法です(この動作はPHP 7.2で非推奨になりました)。

編集:PHP 7および7.2での変更に注意するために、上記を変更しました


1
PHP 7ではzend.assertions、完全にオフにするini設定がありますassert()
Kontrollfreak

これは素晴らしいニュースですが、そこにあるドキュメントに基づくと、PHPUnitへのパッチが注文されているように見えます。PHP5.xでアサーションが失敗した場合にAssertionExceptionをスローするアサーションコールバックハンドラーが追加されています。このようにして、単体テストは、PHP 5.xまたは7のどちらで実行されているかに関係なく、アノテーション@expectedException AssertionExceptionを使用できます。–
Michael Morris

3

アサートはデバッグに役立つため、開発でのみ使用する必要があります。したがって、必要に応じてWebサイトの開発に使用できますが、ライブWebサイトには例外を使用する必要があります。


7
ただし、コードにはまだアサートが含まれています。これらは、実稼働環境ではアクティブになりません。
aaronasterling 2010

1
私はそれらを本番環境に保ち、代わりにそれに応じてエラーハンドラーを微調整します。
ダニエルW.

3

いいえ、同僚はそれを汎用エラーハンドラーとして使用するべきではありません。マニュアルによると:

アサーションは、デバッグ機能としてのみ使用する必要があります。それらをサニティチェックに使用して、常にTRUEである必要があり、そうでない場合はプログラミングエラーを示す条件をテストしたり、拡張機能や特定のシステム制限や機能などの特定の機能の存在をチェックしたりできます。

アサーションは、入力パラメータチェックなどの通常のランタイム操作には使用しないでください。経験則として、アサーションチェックがアクティブ化されていない場合、コードは常に正しく機能するはずです。

自動テストスイートに精通している場合、「assert」動詞は通常、何らかのメソッドまたは関数の出力を検証するために使用されます。例えば:

function add($a, $b) {
    return $a + $b;
}

assert(add(2,2) == 5, 'Two and two is four, dummy!');
assert(is_numeric(add(2,2)), 'Output of this function to only return numeric values.');

同僚は、これを汎用エラーハンドラーとして、この場合は入力チェックとして使用しないでください。ライブラリの一部のユーザーがレコードフィールドを設定できない可能性があるようです。


3

あなたの同僚は本当に契約によってデザインを適用しようとしています、Eiffel言語から、「オブジェクト指向ソフトウェア構築、第2版」という本に基づいて、(DbC)を。

アサーションは、彼が使用したように、ホーア論理またはホーアトリプルの{P}部分になります。{P} C {Q}、ここで{P}は前提条件assert(ion)であり、{Q}は事後条件assert(ion)s。

PHPのassert機能にバグがあることについてのアドバイスに批判的に注意します。バグのあるコードは使いたくないでしょう。本当に必要なのは、アサーションのバグを修正するPHPのメーカーです。それまでは、assertを使用できますが、現在のバグのある状態に注意して使用してください。

さらに、アサーション機能にバグがある場合は、本番コードでは使用しないことをお勧めします。それでも、必要に応じてコードの開発とテストに使用することをお勧めします。

最後に、契約による設計の研究を行う場合、オブジェクト指向の古典的な継承に照らしてブールアサーションを使用すると結果が生じることがわかります。つまり、前提条件を弱めたり、事後条件を弱めたりしてはなりません。そうすることは、相互作用する多形の子孫オブジェクトにとって危険である可能性があります。それが何を意味するのか理解するまで、私はそれを放っておこう!

さらに、PHPのメーカーは、契約による設計の包括的な調査を行い、それをPHPにできるだけ早く導入することを強くお勧めします。そうすれば、私たち全員がDbC対応のコンパイラ/インタプリタを持つことで恩恵を受けることができます。これは、回答(上記)に記載されている問題を処理します。

  1. 適切に実装された契約による設計を意識したコンパイラは、(現在のPHPアサーションとは異なり)バグがないことを願っています。
  2. 適切に実装された契約による設計を意識したコンパイラは、問題に頭を悩ませるのではなく、多形アサーションロジック管理の微妙な違いを処理します。

注:ifアサーション(前提条件)の代わりに-ステートメントを使用した場合でも、前提条件を強化したり、事後条件を弱めたりするために使用すると、悲惨な結果になります。それが何を意味するのかを理解するには、契約によってデザインを勉強して知る必要があります!:-)

幸せな勉強と学習。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.