====== Guard statement ====== * Version: 0.8 * Date: 2020-05-16 * Author: Pavel Patapau, [[algerd.blr@gmail.com]] * Status: Withdrawn * Target Version: 8.0 * Implementation: https://github.com/php/php-src/pull/5578 ===== Introduction ===== Defensive programming is set of practices that widely used in software-engineering and also in PHP community. One of the techniques from it, guarded code or precondition, is mature and proven to be effective in structuring programs. This RFC introduces new statement that improves writing guarded code. More about the topic: * https://medium.com/@scadge/if-statements-design-guard-clauses-might-be-all-you-need-67219a1a981a * https://wiki.c2.com/?GuardClause * [[https://en.wikipedia.org/wiki/Guard_(computer_science)|Wikipedia]] ===== Proposal ===== Guard statement is control-flow type statement that executes code in a body only if expression equals false. Body of statement must contain code that changes control flow: **return**, **throw**, **goto**. Also, in a loop context, you can use **break** or **continue**. guard (condition) else { //this code executed only if condition equals false return|throw|goto; //or continue | break(in loop context only); } // in loop context - valid code while (condition){ guard (condition) else { break; //valid } } If compiler doesn't find any of those statements, it will search selection type statements(if with else|try/catch/finally|switch) and ensures that every element of these statements contains **return**, **throw**, **goto**. //correct guard (condition) else { if(another condition) { return; } else { throw new \LogicException(); } } //incorrect guard(condition) else { if(another condition) { return; } } //correct guard (condition) else { try { //some code return; } catch(\Throwable $e) { throw new \LogicException(); } } //correct guard (condition) else { try { //some code } catch(\Throwable $e) { throw new \LogicException(); } return; } Otherwise, it will cause **compile** error. Syntax guarantees that statement will change scope of execution. ===== Syntax and other languages ==== Syntax almost the same as from Swift https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_if-statement guard condition else { statements } **Else** keyword after expression helps to point that code will execute only if condition equals false. Similar statements: * Ruby - unless statement https://www.tutorialspoint.com/ruby/ruby_if_else.htm * Perl - unless statement https://www.perltutorial.org/perl-unless/ ===== Benefits ===== * Improving readability. It will become easier to distinguish precondition blocks from generic if blocks. * Error pruning. Currently, to write preconditions, it often needed to negate expression to make early return. It is easy to forget insert **!** before. And this kind of mistakes you cannot catch with static analyses tools. With guard statement it will become more natural and simpler to write this kind of code and make less mistakes. * Encourages [[https://en.wikipedia.org/wiki/Defensive_programming|Defensive programming]] design ===== Backward Incompatible Changes ===== **Guard** become reserved keyword, so it cannot be used in function/class/etc name. It will break userland code such as [[https://github.com/illuminate/contracts/blob/7.x/Auth/Guard.php|Laravel]] However, I think it is ok to make BC break for several reasons: * proposal for major version * there was similar precedence in PHP 7.0 with [[rfc:reserve_even_more_types_in_php_7|RFC]] that broke Yii https://github.com/yiisoft/yii2/issues/7936. It had broader scope, guard semantics has more narrow scope(mostly authentication), therefore lesser effect. * PHP ecosystem has plenty of tools such as https://github.com/rectorphp/rector that can make simple transition to new version.