• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • assert()

    (PHP 4, PHP 5, PHP 7)

    检查一个断言是否为FALSE

    说明

    PHP 5

    assert(mixed $assertion[,string $description]): bool

    PHP 7

    assert(mixed $assertion[,Throwable$exception]): bool

    assert()会检查指定的$assertion并在结果为FALSE时采取适当的行动。

    Traditional assertions (PHP 5 and 7)

    如果$assertion是字符串,它将会被assert()当做 PHP 代码来执行。$assertion是字符串的优势是当禁用断言时它的开销会更小,并且在断言失败时消息会包含$assertion表达式。这意味着如果你传入了 boolean 的条件作为$assertion,这个条件将不会显示为断言函数的参数;在调用你定义的assert_options()处理函数时,条件会转换为字符串,而布尔值FALSE会被转换成空字符串。

    断言这个功能应该只被用来调试。你应该用于完整性检查时测试条件是否始终应该为TRUE,来指示某些程序错误,或者检查具体功能的存在(类似扩展函数或特定的系统限制和功能)。

    断言不应该用于普通运行时操作,类似输入参数的检查。作为一个经验法则,在断言禁用时你的代码也应该能够正确地运行。

    assert()的行为可以通过assert_options()来配置,或者手册页面上描述的.ini 设置。

    assert_options()ASSERT_CALLBACK配置指令允许设置回调函数来处理失败的断言。

    assert()回调函数在构建自动测试套件的时候尤其有用,因为它们允许你简易地捕获传入断言的代码,并包含断言的位置信息。当信息能够被其他方法捕获,使用断言可以让它更快更方便!

    回调函数应该接受三个参数。第一个参数包括了断言失败所在的文件。第二个参数包含了断言失败所在的行号,第三个参数包含了失败的表达式(如有任意—字面值例如 1 或者"two"将不会传递到这个参数)。 PHP 5.4.8 及更高版本的用户也可以提供第四个可选参数,如果设置了,用于将$description指定到assert()

    Expectations (PHP 7 only)

    assert()is a language construct in PHP 7, allowing for the definition of expectations: assertions that take effect in development and testing environments, but are optimised away to have zero cost in production.

    Whileassert_options()can still be used to control behaviour as described above for backward compatibility reasons, PHP 7 only code should use the two new configuration directives to control the behaviour ofassert()and not callassert_options().

    PHP 7 configuration directives forassert()
    DirectiveDefault valuePossible values
    zend.assertions1
    • 1: generate and execute code (development mode)
    • 0: generate code but jump around it at runtime
    • -1: do not generate code (production mode)
    assert.exception0
    • 1: throw when the assertion fails, either by throwing the object provided as the$exceptionor by throwing a newAssertionErrorobject if$exceptionwasn't provided
    • 0: use or generate aThrowableas described above, but only generate a warning based on that object rather than throwing it (compatible with PHP 5 behaviour)

    参数

    $assertion

    断言。In PHP 5, this must be either astringto be evaluated or abooleanto be tested. In PHP 7, this may also be any expression that returns a value, which will be executed and the result used to indicate whether the assertion succeeded or failed.

    $description

    如果$assertion失败了,选项 description 将会包括在失败信息里。

    $exception

    In PHP 7, the second parameter can be aThrowableobject instead of a descriptivestring, in which case this is the object that will be thrown if the assertion fails and theassert.exceptionconfiguration directive is enabled.

    返回值

    assertion 是 false 则返回FALSE,否则是TRUE

    更新日志

    版本说明
    7.0.0assert()is now a language construct and not a function.assertion()can now be an expression. The second parameter is now interpreted either as an$exception(if aThrowableobject is given), or as the$descriptionsupported from PHP 5.4.8 onwards.
    5.4.8增加了参数$description$description现在也作为第四个参数提供给ASSERT_CALLBACK模式里的回调函数。

    范例

    Traditional assertions (PHP 5 and 7)

    使用自定义处理程序处理失败的断言

    <?php
    // 激活断言,并设置它为 quiet
    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');
    // 让一则断言失败
    assert('mysql_query("")');
    ?>
    

    使用自定义处理器打印描述信息

    <?php
    // 激活断言,并设置它为 quiet
    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');
    // Make an assertion that should fail
    assert('2 < 1');
    assert('2 < 1', 'Two is less than one');
    ?>
    

    以上例程会输出:

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

    Expectations (PHP 7 only)

    Expectations without a custom exception

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

    Withzend.assertionsset to 0, the above example will output:

    Hi!
    

    Withzend.assertionsset to 1 andassert.exceptionset to 0, the above example will output:

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

    Withzend.assertionsset to 1 andassert.exceptionset to 1, the above example will output:

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

    Expectations with a custom exception

    <?php
    class CustomError extends AssertionError {}
    assert(true == false, new CustomError('True is not false!'));
    echo 'Hi!';
    ?>
    

    Withzend.assertionsset to 0, the above example will output:

    Hi!
    

    Withzend.assertionsset to 1 andassert.exceptionset to 0, the above example will output:

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

    Withzend.assertionsset to 1 andassert.exceptionset to 1, the above example will output:

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

    参见

    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.
    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);
    ?>
    
    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.
    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...)
    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)
    if there was no 'warning' message when assertion failed (FALSE), try reset the error handler:
    <?php
    set_error_handler ( null );
    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.
    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);
    ?>