diff --git a/wordpress-coding-standards/php.md b/wordpress-coding-standards/php.md index 7e3c92f..e92ef8f 100644 --- a/wordpress-coding-standards/php.md +++ b/wordpress-coding-standards/php.md @@ -2,7 +2,7 @@ These PHP coding standards are intended for the WordPress community as a whole. They are mandatory for WordPress Core and we encourage you to use them for your themes and plugins as well. -While themes and plugins may choose to follow a different _coding style_, these **_coding standards_** are not just about _code style_, but also encompass established best practices regarding interoperability, translatability, and security in the WordPress ecosystem, so, even when using a different _code style_, we recommend you still adhere to the WordPress Coding Standards with regards to these best practices. +While themes and plugins may choose to follow a different _coding style_, these **_coding standards_** are not just about _code style_, but also encompass established best practices regarding interoperability, translatability, and security in the WordPress ecosystem, so, even when using a different _code style_, we recommend you still adhere to the WordPress Coding Standards in regard to these best practices. While not all code may fully comply with these standards (yet), all newly committed and/or updated code should fully comply with these coding standards. @@ -10,240 +10,134 @@ Also see the [PHP Inline Documentation Standards](https://developer.wordpress.or If you want to automatically check your code against this standard, you can use the official [WordPress Coding Standards](https://github.com/WordPress/WordPress-Coding-Standards) tooling, which is run using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer/). -## Single and Double Quotes +## General -Use single and double quotes when appropriate. If you're not evaluating anything in the string, use single quotes. You should almost never have to escape quotes in a string, because you can just alternate your quoting style, like so: - -```php -echo 'Link name'; -echo "text with a ' single quote"; -``` - -Text that goes into HTML or XML attributes should be escaped so that single or double quotes do not end the attribute value and invalidate the HTML, causing a security issue. See [Data Validation](https://developer.wordpress.org/plugins/security/data-validation/) in the Plugin Handbook for further details. - -## Indentation - -Your indentation should always reflect logical structure. Use **real tabs**, **not spaces**, as this allows the most flexibility across clients. - -Exception: if you have a block of code that would be more readable if things are aligned, use spaces: +### Opening and Closing PHP Tags -```php -[tab]$foo = 'somevalue'; -[tab]$foo2 = 'somevalue2'; -[tab]$foo34 = 'somevalue3'; -[tab]$foo5 = 'somevalue4'; -``` - -For associative arrays, _each item_ should start on a new line when the array contains more than one item: +When embedding multi-line PHP snippets within an HTML block, the PHP open and close tags must be on a line by themselves. -```php -$query = new WP_Query( array( 'ID' => 123 ) ); -``` +Correct (Multiline): ```php -$args = array( -[tab]'post_type' => 'page', -[tab]'post_author' => 123, -[tab]'post_status' => 'publish', -); - -$query = new WP_Query( $args ); +function foo() { + ?> +
+ +
+ 'somevalue', -[tab]'foo2' => 'somevalue2', -[tab]'foo3' => 'somevalue3', -[tab]'foo34' => 'somevalue3', -); + ``` -For `switch` control structures, `case` statements should be indented one tab from the `switch` statement and the contents of the `case` should be indented one tab from the `case` condition statement. +Incorrect: ```php -switch ( $type ) { -[tab]case 'foo': -[tab][tab]some_function(); -[tab][tab]break; -[tab]case 'bar': -[tab][tab]some_function(); -[tab][tab]break; -} +if ( $a === $b ) { ?> + + + ``` -If you have a really long block, consider whether it can be broken into two or more shorter blocks, functions, or methods, to reduce complexity, improve ease of testing, and increase readability. - -Braces should always be used, even when they are not required: +Incorrect: ```php -if ( condition ) { - action0(); -} - -if ( condition ) { - action1(); -} elseif ( condition2 ) { - action2a(); - action2b(); -} - -foreach ( $items as $item ) { - process_item( $item ); -} + + ``` -Note that requiring the use of braces means that _single-statement inline control structures_ are prohibited. You are free to use the [alternative syntax for control structures](https://www.php.net/manual/en/control-structures.alternative-syntax.php) (e.g. `if`/`endif`, `while`/`endwhile`)—especially in templates where PHP code is embedded within HTML, for instance: +### Single and Double Quotes + +Use single and double quotes when appropriate. If you're not evaluating anything in the string, use single quotes. You should almost never have to escape quotes in a string, because you can just alternate your quoting style, like so: ```php - -
- -
- -
- -
- +echo 'Link name'; +echo "text with a ' single quote"; ``` -## Use `elseif`, not `else if` - -`else if` is not compatible with the colon syntax for `if|elseif` blocks. For this reason, use `elseif` for conditionals. - -## Declaring Arrays - -Using long array syntax ( `array( 1, 2, 3 )` ) for declaring arrays is generally more readable than short array syntax ( `[ 1, 2, 3 ]` ), particularly for those with vision difficulties. Additionally, it's much more descriptive for beginners. +Text that goes into HTML or XML attributes should be escaped so that single or double quotes do not end the attribute value and invalidate the HTML, causing a security issue. See [Data Validation](https://developer.wordpress.org/plugins/security/data-validation/) in the Plugin Handbook for further details. -Arrays must be declared using long array syntax. +## Naming -## Closures (Anonymous Functions) +### Naming Conventions -Where appropriate, closures may be used as an alternative to creating new functions to pass as callbacks. For example: +Use lowercase letters in variable, action/filter, and function names (never `camelCase`). Separate words via underscores. Don't abbreviate variable names unnecessarily; let the code be unambiguous and self-documenting. ```php -$caption = preg_replace_callback( - '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', - function ( $matches ) { - return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] ); - }, - $caption -); +function some_name( $some_variable ) { [...] } ``` -Closures should not be passed as filter or action callbacks, as removing these via `remove_action()` / `remove_filter()` is complex (at this time) (see [#46635](https://core.trac.wordpress.org/ticket/46635 "Improve identifying of non–trivial callbacks in hooks") for a proposal to address this) - -## Multiline Function Calls - -When splitting a function call over multiple lines, each parameter must be on a separate line. Single line inline comments can take up their own line. - -Each parameter must take up no more than a single line. Multi-line parameter values must be assigned to a variable and then that variable should be passed to the function call. +Class names should use capitalized words separated by underscores. Any acronyms should be all upper case. ```php -$bar = array( - 'use_this' => true, - 'meta_key' => 'field_name', -); -$baz = sprintf( - /* translators: %s: Friend's name */ - __( 'Hello, %s!', 'yourtextdomain' ), - $friend_name -); - -$a = foo( - $bar, - $baz, - /* translators: %s: cat */ - sprintf( __( 'The best pet is a %s.' ), 'cat' ) -); +class Walker_Category extends Walker { [...] } +class WP_HTTP { [...] } ``` -## Regular Expressions - -Perl compatible regular expressions ([PCRE](https://www.php.net/pcre), `preg_` functions) should be used in preference to their POSIX counterparts. Never use the `/e` switch, use `preg_replace_callback` instead. - -It's most convenient to use single-quoted strings for regular expressions since, contrary to double-quoted strings, they have only two metasequences which need escaping: `\'` and `\\`. - -## Opening and Closing PHP Tags - -When embedding multi-line PHP snippets within an HTML block, the PHP open and close tags must be on a line by themselves. - -Correct (Multiline): +Constants should be in all upper-case with underscores separating words: ```php -function foo() { - ?> -
- -
- " /> +my-plugin-name.php ``` -Incorrect: +Class file names should be based on the class name with `class-` prepended and the underscores in the class name replaced with hyphens, for example, `WP_Error` becomes: ```php -if ( $a === $b ) { ?> - - - +general-template.php ``` -Incorrect: +### Interpolation for Naming Dynamic Hooks + +Dynamic hooks should be named using interpolation rather than concatenation for readability and discoverability purposes. + +Dynamic hooks are hooks that include dynamic values in their tag name, e.g. `{$new_status}_{$post->post_type}` (publish_post). + +Variables used in hook tags should be wrapped in curly braces `{` and `}`, with the complete outer tag name wrapped in double quotes. This is to ensure PHP can correctly parse the given variables' types within the interpolated string. ```php - - +do_action( "{$new_status}_{$post->post_type}", $post->ID, $post ); ``` -## Remove Trailing Spaces +Where possible, dynamic values in tag names should also be as succinct and to the point as possible. `$user_id` is much more self-documenting than, say, `$this->id`. -Remove trailing whitespace at the end of each line. Omitting the closing PHP tag at the end of a file is preferred. If you use the tag, make sure you remove trailing whitespace. +## Whitespace -## Space Usage +### Space Usage Always put spaces after commas, and on both sides of logical, comparison, string and assignment operators. @@ -308,8 +202,8 @@ In a `switch` block, there must be no space between the `case` condition and the ```php switch ( $foo ) { - case 'bar': // Correct. - case 'ba' : // Incorrect. + case 'bar': // Correct. + case 'ba' : // Incorrect. } ``` @@ -317,11 +211,11 @@ Similarly, there should be no space before the colon on return type declarations ```php function sum( $a, $b ): float { - return $a + $b; + return $a + $b; } ``` -Unless otherwise specified, parentheses should have spaces inside of them. +Unless otherwise specified, parentheses should have spaces inside them. ```php if ( $foo && ( $bar || $baz ) ) { ... @@ -329,73 +223,152 @@ if ( $foo && ( $bar || $baz ) ) { ... my_function( ( $x - 1 ) * 5, $y ); ``` -## Formatting SQL statements +### Indentation -When formatting SQL statements you may break them into several lines and indent if it is sufficiently complex to warrant it. Most statements work well as one line though. Always capitalize the SQL parts of the statement like `UPDATE` or `WHERE`. +Your indentation should always reflect logical structure. Use **real tabs**, **not spaces**, as this allows the most flexibility across clients. -Functions that update the database should expect their parameters to lack SQL slash escaping when passed. Escaping should be done as close to the time of the query as possible, preferably by using `$wpdb->prepare()` +Exception: if you have a block of code that would be more readable if things are aligned, use spaces: -`$wpdb->prepare()` is a method that handles escaping, quoting, and int-casting for SQL queries. It uses a subset of the `sprintf()` style of formatting. Example : +```php +[tab]$foo = 'somevalue'; +[tab]$foo2 = 'somevalue2'; +[tab]$foo34 = 'somevalue3'; +[tab]$foo5 = 'somevalue4'; +``` + +For associative arrays, _each item_ should start on a new line when the array contains more than one item: ```php -$var = "dangerous'"; // Raw data that may or may not need to be escaped. -$id = some_foo_number(); // Data we expect to be an integer, but we're not certain. +$query = new WP_Query( array( 'ID' => 123 ) ); +``` -$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) ); +```php +$args = array( +[tab]'post_type' => 'page', +[tab]'post_author' => 123, +[tab]'post_status' => 'publish', +); + +$query = new WP_Query( $args ); ``` -`%s` is used for string placeholders and `%d` is used for integer placeholders. Note that they are not 'quoted'! `$wpdb->prepare()` will take care of escaping and quoting for us. The benefit of this is that it is easy to see at a glance whether something has been escaped or not because it happens right when the query happens. +Note the comma after the last array item: this is recommended because it makes it easier to change the order of the array, and makes for cleaner diffs when new items are added. -See [Data Validation](https://developer.wordpress.org/plugins/security/data-validation/) in the Plugin Handbook for further details. +```php +$my_array = array( +[tab]'foo' => 'somevalue', +[tab]'foo2' => 'somevalue2', +[tab]'foo3' => 'somevalue3', +[tab]'foo34' => 'somevalue3', +); +``` -## Database Queries +For `switch` control structures, `case` statements should be indented one tab from the `switch` statement and the contents of the `case` should be indented one tab from the `case` condition statement. -Avoid touching the database directly. If there is a defined function that can get the data you need, use it. Database abstraction (using functions instead of queries) helps keep your code forward-compatible and, in cases where results are cached in memory, it can be many times faster. +```php +switch ( $type ) { +[tab]case 'foo': +[tab][tab]some_function(); +[tab][tab]break; +[tab]case 'bar': +[tab][tab]some_function(); +[tab][tab]break; +} +``` -If you must touch the database, consider creating a [Trac](https://core.trac.wordpress.org/) ticket. There you can discuss the possibility of adding a new function to cover the functionality you wanted, for a future version of WordPress. +**Rule of thumb:** Tabs should be used at the beginning of the line for indentation, while spaces can be used mid-line for alignment. -## Naming Conventions +### Remove Trailing Spaces -Use lowercase letters in variable, action/filter, and function names (never `camelCase`). Separate words via underscores. Don't abbreviate variable names unnecessarily; let the code be unambiguous and self-documenting. +Remove trailing whitespace at the end of each line. Omitting the closing PHP tag at the end of a file is preferred. If you use the tag, make sure you remove trailing whitespace. -```php -function some_name( $some_variable ) { [...] } -``` +## Formatting -Class names should use capitalized words separated by underscores. Any acronyms should be all upper case. +### Brace Style + +Braces shall be used for all blocks in the style shown here: ```php -class Walker_Category extends Walker { [...] } -class WP_HTTP { [...] } +if ( condition ) { + action1(); + action2(); +} elseif ( condition2 && condition3 ) { + action3(); + action4(); +} else { + defaultaction(); +} ``` -Constants should be in all upper-case with underscores separating words: +If you have a really long block, consider whether it can be broken into two or more shorter blocks, functions, or methods, to reduce complexity, improve ease of testing, and increase readability. + +Braces should always be used, even when they are not required: ```php -define( 'DOING_AJAX', true ); -``` +if ( condition ) { + action0(); +} -Files should be named descriptively using lowercase letters. Hyphens should separate words. +if ( condition ) { + action1(); +} elseif ( condition2 ) { + action2a(); + action2b(); +} -```php -my-plugin-name.php +foreach ( $items as $item ) { + process_item( $item ); +} ``` -Class file names should be based on the class name with `class-` prepended and the underscores in the class name replaced with hyphens, for example, `WP_Error` becomes: +Note that requiring the use of braces means that _single-statement inline control structures_ are prohibited. You are free to use the [alternative syntax for control structures](https://www.php.net/manual/en/control-structures.alternative-syntax.php) (e.g. `if`/`endif`, `while`/`endwhile`)—especially in templates where PHP code is embedded within HTML, for instance: ```php -class-wp-error.php + +
+ +
+ +
+ +
+ ``` -This file-naming standard is for all current and new files with classes. There is one exception to this rule for three legacy files: `class.wp-dependencies.php`, `class.wp-scripts.php`, `class.wp-styles.php`. Those files are prepended with `class.`, a dot after the word class instead of a hyphen. +### Declaring Arrays -Files containing template tags in the `wp-includes` directory should have `-template` appended to the end of the name so that they are obvious. +Using long array syntax ( `array( 1, 2, 3 )` ) for declaring arrays is generally more readable than short array syntax ( `[ 1, 2, 3 ]` ), particularly for those with vision difficulties. Additionally, it's much more descriptive for beginners. + +Arrays must be declared using long array syntax. + +### Multiline Function Calls + +When splitting a function call over multiple lines, each parameter must be on a separate line. Single line inline comments can take up their own line. + +Each parameter must take up no more than a single line. Multi-line parameter values must be assigned to a variable and then that variable should be passed to the function call. ```php -general-template.php +$bar = array( + 'use_this' => true, + 'meta_key' => 'field_name', +); +$baz = sprintf( + /* translators: %s: Friend's name */ + __( 'Hello, %s!', 'yourtextdomain' ), + $friend_name +); + +$a = foo( + $bar, + $baz, + /* translators: %s: cat */ + sprintf( __( 'The best pet is a %s.' ), 'cat' ) +); ``` -## Only one object structure (class/interface/trait) per file +## Object-Oriented Programming + +### Only One Object Structure (Class/Interface/Trait) per File For instance, if we have a file called `class-example-class.php` it can only contain one class in that file. @@ -418,7 +391,84 @@ class Example_Class { [...] } class Example_Class_Extended { [...] } ``` -## Self-Explanatory Flag Values for Function Arguments +## Control Structures + +### Use `elseif`, not `else if` + +`else if` is not compatible with the colon syntax for `if|elseif` blocks. For this reason, use `elseif` for conditionals. + +### Yoda Conditions + +```php +if ( true === $the_force ) { + $victorious = you_will( $be ); +} +``` + +When doing logical comparisons involving variables, always put the variable on the right side and put constants, literals, or function calls on the left side. If neither side is a variable, the order is not important. (In [computer science terms](https://en.wikipedia.org/wiki/Value_(computer_science)#Assignment:_l-values_and_r-values), in comparisons always try to put l-values on the right and r-values on the left.) + +In the above example, if you omit an equals sign (admit it, it happens even to the most seasoned of us), you'll get a parse error, because you can't assign to a constant like `true`. If the statement were the other way around `( $the_force = true )`, the assignment would be perfectly valid, returning `1`, causing the if statement to evaluate to `true`, and you could be chasing that bug for a while. + +A little bizarre, it is, to read. Get used to it, you will. + +This applies to `==`, `!=`, `===`, and `!==`. Yoda conditions for `<`, `>`, `<=` or `>=` are significantly more difficult to read and are best avoided. + +## Operators + +### Ternary Operator + +[Ternary operators](https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary) are fine, but always have them test if the statement is true, not false. Otherwise, it just gets confusing. (An exception would be using `! empty()`, as testing for false here is generally more intuitive.) + +The short ternary operator must not be used. + +For example: + +```php +// (if statement is true) ? (do this) : (else, do this); +$musictype = ( 'jazz' === $music ) ? 'cool' : 'blah'; +// (if field is not empty ) ? (do this) : (else, do this); +``` + +### Error Control Operator `@` + +As noted in the [PHP docs](https://www.php.net/manual/en/language.operators.errorcontrol.php): + +> PHP supports one error control operator: the at sign (@). When prepended to an expression in PHP, any diagnostic error that might be generated by that expression will be suppressed. + +While this operator does exist in Core, it is often used lazily instead of doing proper error checking. Its use is highly discouraged, as even the PHP docs also state: + +> Warning: Prior to PHP 8.0.0, it was possible for the @ operator to disable critical errors that will terminate script execution. For example, prepending @ to a call of a function that did not exist, by being unavailable or mistyped, would cause the script to terminate with no indication as to why. + +## Database + +### Database Queries + +Avoid touching the database directly. If there is a defined function that can get the data you need, use it. Database abstraction (using functions instead of queries) helps keep your code forward-compatible and, in cases where results are cached in memory, it can be many times faster. + +If you must touch the database, consider creating a [Trac](https://core.trac.wordpress.org/) ticket. There you can discuss the possibility of adding a new function to cover the functionality you wanted, for a future version of WordPress. + +### Formatting SQL statements + +When formatting SQL statements you may break them into several lines and indent if it is sufficiently complex to warrant it. Most statements work well as one line though. Always capitalize the SQL parts of the statement like `UPDATE` or `WHERE`. + +Functions that update the database should expect their parameters to lack SQL slash escaping when passed. Escaping should be done as close to the time of the query as possible, preferably by using `$wpdb->prepare()` + +`$wpdb->prepare()` is a method that handles escaping, quoting, and int-casting for SQL queries. It uses a subset of the `sprintf()` style of formatting. Example : + +```php +$var = "dangerous'"; // Raw data that may or may not need to be escaped. +$id = some_foo_number(); // Data we expect to be an integer, but we're not certain. + +$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) ); +``` + +`%s` is used for string placeholders and `%d` is used for integer placeholders. Note that they are not 'quoted'! `$wpdb->prepare()` will take care of escaping and quoting for us. The benefit of this is that it is easy to see at a glance whether something has been escaped or not because it happens right when the query happens. + +See [Data Validation](https://developer.wordpress.org/plugins/security/data-validation/) in the Plugin Handbook for further details. + +## Recommendations + +### Self-Explanatory Flag Values for Function Arguments Prefer string values to just `true` and `false` when calling functions. @@ -456,51 +506,7 @@ eat ( 'noodles', array( 'speed' => 'moderate' ) ); Be careful when using this pattern, as it can lead to "Undefined array index" notices if input isn't validated before use. Use this pattern only where it makes sense (i.e. multiple possible arguments), not just for the sake of it. -## Interpolation for Naming Dynamic Hooks - -Dynamic hooks should be named using interpolation rather than concatenation for readability and discoverability purposes. - -Dynamic hooks are hooks that include dynamic values in their tag name, e.g. `{$new_status}_{$post->post_type}` (publish_post). - -Variables used in hook tags should be wrapped in curly braces `{` and `}`, with the complete outer tag name wrapped in double quotes. This is to ensure PHP can correctly parse the given variables' types within the interpolated string. - -```php -do_action( "{$new_status}_{$post->post_type}", $post->ID, $post ); -``` - -Where possible, dynamic values in tag names should also be as succinct and to the point as possible. `$user_id` is much more self-documenting than, say, `$this->id`. - -## Ternary Operator - -[Ternary operators](https://www.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary) are fine, but always have them test if the statement is true, not false. Otherwise, it just gets confusing. (An exception would be using `! empty()`, as testing for false here is generally more intuitive.) - -The short ternary operator must not be used. - -For example: - -```php -// (if statement is true) ? (do this) : (else, do this); -$musictype = ( 'jazz' === $music ) ? 'cool' : 'blah'; -// (if field is not empty ) ? (do this) : (else, do this); -``` - -## Yoda Conditions - -```php -if ( true === $the_force ) { - $victorious = you_will( $be ); -} -``` - -When doing logical comparisons involving variables, always put the variable on the right side and put constants, literals, or function calls on the left side. If neither side is a variable, the order is not important. (In [computer science terms](https://en.wikipedia.org/wiki/Value_(computer_science)#Assignment:_l-values_and_r-values), in comparisons always try to put l-values on the right and r-values on the left.) - -In the above example, if you omit an equals sign (admit it, it happens even to the most seasoned of us), you'll get a parse error, because you can't assign to a constant like `true`. If the statement were the other way around `( $the_force = true )`, the assignment would be perfectly valid, returning `1`, causing the if statement to evaluate to `true`, and you could be chasing that bug for a while. - -A little bizarre, it is, to read. Get used to it, you will. - -This applies to `==`, `!=`, `===`, and `!==`. Yoda conditions for `<`, `>`, `<=` or `>=` are significantly more difficult to read and are best avoided. - -## Clever Code +### Clever Code In general, readability is more important than cleverness or brevity. @@ -512,7 +518,7 @@ Although the above line is clever, it takes a while to grok if you're not famili ```php if ( ! isset( $var ) ) { - $var = some_function(); + $var = some_function(); } ``` @@ -522,7 +528,7 @@ Correct: ```php if ( 0 === strpos( $text, 'WordPress' ) ) { - echo esc_html__( 'Yay WordPress!', 'textdomain' ); + echo esc_html__( 'Yay WordPress!', 'textdomain' ); } ``` @@ -530,7 +536,7 @@ Incorrect: ```php if ( 0 == strpos( 'WordPress', 'foo' ) ) { - echo esc_html__( 'Yay WordPress!', 'textdomain' ); + echo esc_html__( 'Yay WordPress!', 'textdomain' ); } ``` @@ -557,18 +563,18 @@ In a `switch` statement, it's okay to have multiple empty cases fall through to ```php switch ( $foo ) { - case 'bar': // Correct, an empty case can fall through without comment. - case 'baz': - echo esc_html( $foo ); // Incorrect, a case with a block must break, return, or have a comment. - case 'cat': - echo 'mouse'; - break; // Correct, a case with a break does not require a comment. - case 'dog': - echo 'horse'; - // no break // Correct, a case can have a comment to explicitly mention the fall through. - case 'fish': - echo 'bird'; - break; + case 'bar': // Correct, an empty case can fall through without comment. + case 'baz': + echo esc_html( $foo ); // Incorrect, a case with a block must break, return, or have a comment. + case 'cat': + echo 'mouse'; + break; // Correct, a case with a break does not require a comment. + case 'dog': + echo 'horse'; + // no break // Correct, a case can have a comment to explicitly mention the fall through. + case 'fish': + echo 'bird'; + break; } ``` @@ -576,17 +582,29 @@ The `goto` statement must never be used. The `eval()` construct is _very dangerous_ and is impossible to secure. Additionally, the `create_function()` function, which internally performs an `eval()`, is deprecated since PHP 7.2 and has been removed in PHP 8.0. Neither of these must be used. -## Error Control Operator `@` +### Closures (Anonymous Functions) -As noted in the [PHP docs](https://www.php.net/manual/en/language.operators.errorcontrol.php): +Where appropriate, closures may be used as an alternative to creating new functions to pass as callbacks. For example: -> PHP supports one error control operator: the at sign (@). When prepended to an expression in PHP, any diagnostic error that might be generated by that expression will be suppressed. +```php +$caption = preg_replace_callback( + '/<[a-zA-Z0-9]+(?: [^<>]+>)*/', + function ( $matches ) { + return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] ); + }, + $caption +); +``` -While this operator does exist in Core, it is often used lazily instead of doing proper error checking. Its use is highly discouraged, as even the PHP docs also state: +Closures should not be passed as filter or action callbacks, as removing these via `remove_action()` / `remove_filter()` is complex (at this time) (see [#46635](https://core.trac.wordpress.org/ticket/46635 "Improve identifying of non–trivial callbacks in hooks") for a proposal to address this). -> Warning: Prior to PHP 8.0.0, it was possible for the @ operator to disable critical errors that will terminate script execution. For example, prepending @ to a call of a function that did not exist, by being unavailable or mistyped, would cause the script to terminate with no indication as to why. +### Regular Expressions + +Perl compatible regular expressions ([PCRE](https://www.php.net/pcre), `preg_` functions) should be used in preference to their POSIX counterparts. Never use the `/e` switch, use `preg_replace_callback` instead. + +It's most convenient to use single-quoted strings for regular expressions since, contrary to double-quoted strings, they have only two metasequences which need escaping: `\'` and `\\`. -## Don't `extract()` +### Don't `extract()` Per [#22400](https://core.trac.wordpress.org/ticket/22400 "Remove all, or at least most, uses of extract() within WordPress"):