Until May last year, contributions to WordPress Core Core is the set of software required to run WordPress. The Core Development Team builds WordPress. were bound to PHP The web scripting language in which WordPress is primarily architected. WordPress requires PHP 7.4 or higher 5.2 syntax and most plugins and themes stuck to the PHP 5.2 minimum requirement as well.
However, with the change to PHP 5.6 as the minimum PHP version for WordPress Core, new PHP features have become available for use in WP Core and with the outlook of a minimum version of PHP 7.x in the (near) future, even more interesting language features will soon become available for use in WordPress Core, plugins and themes.
With that in mind, we’d like to define coding standards for a number of these constructs and propose to implement automated checking for these in the WordPress Coding Standards The Accessibility, PHP, JavaScript, CSS, HTML, etc. coding standards as published in the WordPress Coding Standards Handbook.
May also refer to The collection of PHP_CodeSniffer rules (sniffs) used to format and validate PHP code developed for WordPress according to the PHP coding standards. tooling in the near future.
While it may still be a while before some of these features will actually be adopted for use in WordPress Core, defining the coding standards in advance will allow for a consistent code base when they do get adopted and will allow for plugins and themes, which are not necessarily bound to the PHP 5.6 minimum, to safeguard their code consistency when they start using more modern PHP already.
To be honest, none of these proposals are terribly exciting and some may not even seem worth mentioning. Most follow either prior art in WordPress Core or industry standards for the same, but in the spirit of openness, we’d like to verify our take on these before implementing them.
So without further ado, the following coding standards are proposed for implementation in the WordPress Coding Standards 3.0.0:
Rules for modern PHP constructs
Namespace declarations
Proposed standards:
- The
namespace
keyword should be lowercase. - There should be exactly one space between the
namespace
keyword and the start of the namespace name in a namespace declaration.
And to be extra clear: namespace names in a namespace declaration should not start with a leading backslash \
. This would be a parse error in most cases anyway. - Each “leaf” of a namespace name should be in
Camel_Caps
, i.e. each word should start with a capital and words should be separated by an underscore.
Consecutive caps, like for acronyms, will be allowed. This is in line with other WP naming conventions. - There should be no whitespace or comments within the name part of the namespace declaration.
- There should be exactly one blank line before a namespace declaration.
- There should be at least one blank line after a namespace declaration.
- There should be only one namespace declaration per file.
This automatically implies that the namespace declaration should be at the top of a file, with only the file docblock (phpdoc, xref, inline docs) and an optional declare
statement preceding it. - Namespace declaration using the curly brace block Block is the abstract term used to describe units of markup that, composed together, form the content or layout of a webpage using the WordPress editor. The idea combines concepts of what in the past may have achieved with shortcodes, custom HTML, and embed discovery into a single consistent API and user experience. syntax are not allowed.
- Namespace declarations without name (= explicit global namespace) are not allowed.
Related open WPCS issue: https://github.com/WordPress/WordPress-Coding-Standards/issues/1763
Valid code example
<?php
/**
* File docblock.
*/
namespace Prefix\Admin\Domain_URL\Sub_Domain\Event;
use ...
Invalid A resolution on the bug tracker (and generally common in software development, sometimes also notabug) that indicates the ticket is not a bug, is a support request, or is generally invalid. code examples
<?php
/**
* File docblock.
*/
namespace Prefix \
/* Here comes the real namespace */ Admin\DomainURL\ SubDomain\Event;
use ...
<?php
/**
* File docblock.
*/
namespace Foo {
// Code.
}
namespace {
// Code.
}
Usage in WordPress Core
As previously stated, the introduction of namespaces into WordPress Core will need to be a concerted effort with careful thought about the implications for the architecture as a whole.
There is currently no timeline for introducing namespaces to WordPress Core.
Usage in plugins and themes
The use of namespaces in plugins and themes is strongly encouraged. It is a great way to prefix a lot of your code to prevent naming conflicts with other plugins, themes and/or WordPress Core.
Please do make sure you use a unique and long enough namespace prefix to actually prevent conflicts. Generally speaking, using a namespace prefix along the lines of Vendor\Packagename
is a good idea.
And while not new, this may also be a good time to remind you that using wp
or WordPress
as a prefix is not allowed (regardless of case).
// Correct.
namespace My_Awesome_Plugin\Admin;
// Incorrect.
namespace MAP\Admin;
Also be aware that namespacing has no effect on variables declared outside of functions/classes, constants declared with define()
or non-PHP native constructs, like the hook names as used in WordPress.
Those still need to be prefixed individually.
Import use
statements
Proposed standards:
- Import
use
statements should be at the top of a file and should directly follow the (optional) namespace
declaration. - Import
use
statements should be ordered by type: first import use
statements for classes, interfaces and traits, next import use
statements for functions and lastly import use
statements for constants.
No rules are currently set for the ordering of import use
statements of the same type. This will be revisited at a later point in time. - There should be exactly one blank line before the first import
use
statement of each type and at least one blank line after the last import use
statement. - The
use
, function
, const
and as
keywords should be lowercase. - There should be exactly one space after each keyword.
- Names in an import
use
statement should not start with a leading backslash \
.
All names in import use
statements must be fully qualified by nature, so a \
at the start is redundant. - There should be no whitespace or comments within the name part of a use declaration.
- Alias names should comply with the existing WordPress naming conventions for class/function/constant names.
- Only set an alias when using a different name.
I.e. this is not allowed: use My\Project\ClassName as ClassName
.
Note: as these names are case-insensitive in PHP, aliasing to the same name in a different case falls under this rule and is forbidden. - When using group
use
statements:- There should be one statement for each type – OO constructs (classes/interfaces/traits), functions, constants. The various types should not be combined in one group
use
statement. - There should be no space between the namespace separator and the opening curly brace for the group use.
- The opening curly brace should be the last code element of the line.
- Each sub-statement must be one a line by itself and should be indented one tab in from the
use
statement keyword. - When possible (PHP 7.2+), the last statement should be followed by a trailing comma.
This will not be enforced until the minimum PHP requirement has been raised to PHP 7.2. - The closing curly brace should be on a line by itself with no blank line(s) before it.
- The closing curly brace must be indented the same as the
use
keyword.
At this time, no standards are defined (yet) on when an import use
statement should be used and when to use a fully qualified name inline. Guidelines to that effect may be added at a later point in time.
Valid code example
<?php
/**
* File doc block.
*/
namespace WordPress\subnamespace;
use Some\Namespace\Class_A;
use Some\Namespace\Class_C as Aliased_Class_C;
use Some\Namespace\{
Class_D,
Class_E as Aliased_Class_E,
}
use function Some\Namespace\function_a;
use function Some\Namespace\function_b as aliased_function;
use const Some\Namespace\CONSTANT_A;
use const Some\Namespace\CONSTANT_D as ALIASED_CONSTANT;
// Rest of the code
Invalid code example
<?php
/**
* File doc block.
*/
namespace WordPress\subnamespace;
use Some\Namespace \ /*comment*/ Class_A;
use const Some\Namespace\CONSTANT_A;
use function Some\Namespace\function_a;
use \Some\Namespace\Class_C as aliased_class_c;
use Some\Namespace\{Class_D, Class_E as Aliased_Class_E}
use Vendor\Package\{ function function_a, function function_b,
Class_C,
const CONSTANT_NAME};
class Foo {
// Code.
}
use const \Some\Namespace\CONSTANT_D as Aliased_constant;
use function Some\Namespace\function_b as Aliased_Function;
// Rest of the code
Usage in WordPress Core
While import use
statements can already be used in WordPress Core, it is, for the moment, strongly discouraged.
Import use
statements are most useful when combined with namespaces and a class autoloading implementation. As neither of these are currently in place for WordPress Core and discussions about this are ongoing, holding off on adding import use
statements to WordPress Core is the sane choice for now.
Important notes:
- Adding a
use
statement does not automatically load whatever is being imported. You still need to load the file containing the class/function/constant with a require/include
statement before the imported code is used. - Import
use
statements have no effect on dynamic class, function or constant names. - Group use statements can not be used in WordPress Core until the minimum PHP version has been raised to PHP 7.0.
- Trailing commas in group use statements can not be used in WordPress Core until the minimum PHP version has been raised to PHP 7.2.
Fully qualified names in inline code
Proposed standards:
- When using a fully or partially qualified name, no whitespace or comments are allowed within the name.
Code example
// Correct.
$foo = new \Vendor\Domain\Foo();
$result = namespace\function_name();
// Incorrect.
$foo = new \Vendor // Source: external dependency.
\Domain
\Foo();
$result = namespace \function_name();
Usage in WordPress Core
Until the introduction of namespaces, there is no need to introduce the use of fully qualified names in inline code into WordPress Core.
Traits and interfaces
Proposed standards for trait
/interface
declarations:
- Trait and interface names should use capitalized words separated by underscores, same as class names. Any acronyms should be all upper case.
The word “Trait” is not required in a trait name, but won’t be forbidden either.
The word “Interface” is not required in an interface name, but won’t be forbidden either. - Trait file names should be based on the
trait
name with trait-
prepended and the underscores in the trait name replaced with hyphens.
The same goes for interface file names where the name should be based on the interface
name with interface-
prepended and the underscores in the interface name replaced with hyphens.
Example: for a trait called Form_Field_Generator
, the file should be named trait-form-field-generator.php
.
For everything else, the same formatting rules as already in place for class declarations apply.
Proposed standards for the formatting of trait use
statements:
- Trait
use
statements should be at the top of a class. - Trait
use
statements should have exactly one blank line before the first use
statement and at least one blank line after, with the exception of a class only containing a trait use
statement in which case the blank line after may be omitted.
Comments/docblocks directly above a trait use
statement will be regarded as belonging with the statement and the blank line will be enforced above it. - The
use
, insteadof
and as
keywords must be in lowercase. - There must be exactly one space between the
use
keyword and the name of the trait. - When grouping multiple traits in one
use
statement, there should be no space between a trait name and the comma and exactly one space between the comma and the next trait name. - When using the
insteadof
or as
statements:- There must be exactly one space before and after each of these keywords.
- For single-line
use
statements, there must be exactly one space between the last trait name and the opening curly brace and exactly one space between the opening curly brace and start of the enclosed statement.
There must also be exactly one space between the semi-colon at the end of the enclosed statement and the closing curly brace and the closing curly brace should be the last code element on the line. - For multi-line
use
statements, there must be exactly one space between the last trait name and the opening curly brace and a new line after the opening curly brace.
The closing curly brace should be on a line by itself with no blank line(s) before it.
The closing brace must be indented the same as the use
keyword. - When using multiple
insteadof
or as
statements grouped within curly braces, the statement must become a multi-line use
statement and each of the insteadof
or as
statements must be on a line by itself, indented exactly one tab from the use
keyword.
Code example
// Correct.
class Foo {
// Comments above a use statement will be regarded as documentation for the statement.
use BarTrait;
use FooTrait, BazingaTrait {
BarTrait::method_name insteadof FooTrait;
BazingaTrait::method_name as bazinga_method;
}
use LoopyTrait { eat as protected; }
public $baz = true;
...
}
class Foo {
use BarTrait;
}
// Incorrect.
class Foo {
use BarTrait;
use FooTrait, BazingaTrait{BarTrait::method_name insteadof FooTrait; BazingaTrait::method_name
as bazinga_method;
};
public $baz = true;
...
}
Usage in WordPress Core
Interfaces could already previously be used in WordPress Core and are encouraged to be used to clarify the public interface of sets of related classes.
Traits may be used in WordPress Core, though it should be carefully evaluated in each individual instance whether using a trait is the right decision from an architectural point of view.
Type declarations
Proposed standards:
- Type declarations must have exactly one space before and after the type.
In case of a multi-line function declaration, a new line + appropriate indentation should be used before the type declaration. - The nullability operator is regarded as part of the type declaration and there should be no space between this operator and the actual type.
- Keyword-based type declarations should use lowercase.
- Class/interface name based type declarations should use the case of the class/interface name as declared.
And specifically for return type declarations:
- There should be no space between the closing parenthesis of the function declaration and the colon starting a return type.
These rules apply to all structures allowing for type declarations: functions, closures, catch
conditions as well as the PHP 7.4 arrow functions and typed properties.
Code example
// Correct.
function foo( Class_Name $param_a, ?string $param_b, callable $param_c ): ?\Class_Name {
// Do something.
}
function bar(
Interface_Name $param_a,
?int $param_b,
bool $param_c
): ?\Class_Name {
// Do something.
}
// Incorrect.
function baz(Class_Name $param_a, ? Float$param_b, CALLABLE $param_c ) : ? \Class_Name{
// Do something.
}
Usage in WordPress Core
While rare, array and class/interface name based type declarations already exist in WordPress Core.
Adding type declarations to existing WordPress Core functions should be done with extreme care.
The function signature of any function (method) which can be overloaded by plugins or themes should not be touched.
This includes all existing public
and protected
class methods and any conditionally declared (pluggable) functions in the global namespace.
This leaves, for now, only unconditionally declared functions in the global namespace, private class methods and code newly being introduced as candidates for adding type declarations.
For those, adding callable
, class and interface name based parameter type declarations where the parameter type only expects one type and it would allow for simplification of existing/new code is allowed and tentatively encouraged.
Using the array
keyword in type declarations is strongly discouraged for now, as most often, it would be better to use iterable
to allow for more flexibility in the implementation and that keyword is not yet available for use in WordPress Core until the minimum requirements are upped to PHP 7.1.
Note:
- The scalar
bool
, int
, float
and string
type declarations can not be used in WordPress Core until the minimum PHP version has been raised to PHP 7.0. - Return type declarations can not be used until the minimum PHP version has been raised to PHP 7.0.
- Nullable type declarations can not be used until the minimum PHP version has been raised to PHP 7.1.
- The
iterable
, void
and object
type declarations can not be used until the minimum PHP version has been raised to PHP 7.1 and 7.2 respectively. - Property type declarations can not be used until the minimum PHP version has been raised to PHP 7.4.
Declare statements / strict typing
Proposed standards:
- There should be exactly one blank line above the
declare
statement and at least one blank line below. - The keywords used –
declare
, strict_types
, ticks
and encoding
– should be lowercase. - There should be no space between the
declare
keyword and the open parenthesis. - There should be no space on the inside of the parentheses.
- There should be no space on either side of the
=
operator used within declare
statements. declare
statements using the curly brace block syntax are allowed for ticks
only. For declare
statements using the curly brace block syntax:- There should be exactly one space between the closing parenthesis and the opening curly brace and the opening curly should be the last code element on the line.
- The block contents should be indented one tab in from the indentation of the
declare
keyword. - And the closing curly brace should be on a line by itself with no blank line above it and at least one blank line below it.
Note: these standards are different from those used for function calls. Keep in mind that declare
is not a function.
Code example
// Correct.
<?php
/**
* File doc block.
*/
declare(strict_types=1);
use ...
declare(ticks=1) {
// Do something.
}
// Incorrect.
<?php
/**
* File doc block.
*/
declare ( strict_types = 1 );
use ...
Usage in WordPress Core
Strict typing should not be used in WordPress Core at this time.
It is a PHP 7.0+ feature, but even when WordPress Core would get a PHP 7.0 minimum requirement, implementing strict typing will need careful consideration as a lot of WordPress Core relies on the loose type nature of PHP, so this is not something to be undertaken lightly and is not on the agenda for the foreseeable future.
The other declare
statements, ticks
and encoding
, can be used in WordPress Core when appropriate.
The ::class
constant
Proposed standards:
- There should be no space between
::class
and the preceding class name it applies to. - There should be no space between the double colon and the
class
keyword. - The
class
keyword should be in lowercase.
Code example
// Correct.
add_action( 'action_name', array( My_Class::class, 'method_name' ) );
// Incorrect.
add_action( 'action_name', array( My_Class :: CLASS, 'method_name' ) );
Usage in WordPress Core
The ::class
constant can be freely used in WordPress Core.
Replacing existing uses of __CLASS__
with self::class
and get_called_class()
with static::class
, where appropriate, is encouraged.
Operators
Spread operator ...
Proposed standards:
- There should be one space, or a new line + appropriate indentation, before the spread operator.
- There should be no space between the spread operator and the variable/function call it applies to.
- When combining the spread operator with the reference operator, there should be no space between them.
Related open WPCS issue: https://github.com/WordPress/WordPress-Coding-Standards/issues/1762
Code example
// Correct.
function foo( &...$spread ) {
bar( ...$spread );
bar(
array( ...$foo ),
...array_values( $keyed_array )
);
}
// Incorrect.
function fool( & ... $spread ) {
bar(...
$spread );
bar(
[... $foo ],.../*comment*/array_values( $keyed_array )
);
}
Usage in WordPress Core
The spread operator for packing arguments in function declarations and unpacking them in function calls has been introduced in WordPress Core in WP 5.3 and can be freely used when appropriate.
If you want to learn more about the caveats of when the spread operator can/can not be used, reading the implementation notes in the associated Trac ticket is highly recommended.
The spread operator can not be used to unpack arrays until the minimum PHP version has been raised to PHP 7.4.
instanceof
, pow **
, pow equals **=
, spaceship <=>
, null coalesce ??
and null coalesce equals ??=
The already existing rules regarding operators apply:
Always put spaces on both sides of logical, comparison, string and assignment operators.
Equality operators in blocks of code should be aligned for readability.
Usage in WordPress Core
instanceof
could already be used in WordPress Core and it is encouraged to use this operator instead of a function call to is_a()
as it is more performant.
The pow **
and pow equals **=
operators can be freely used in WordPress Core and have been introduced in WP 5.3.
The spaceship <=>
, null coalesce ??
and null coalesce equals ??=
operators can not be used in WordPress Core until the minimum PHP version has been raised to PHP 7.0 (spaceship and null coalesce) or PHP 7.4 (null coalesce equals).
Additional new rules
One class-like definition per file
Proposed standards:
- There should only be one class-like definition (class/trait/interface) per file.
This has already been a best practice for a while and will now be formalized.
Related open WPCS PR: https://github.com/WordPress/WordPress-Coding-Standards/pull/1802
Visibility should always be declared
Proposed standards:
- For all constructs which allow it (properties, methods), visibility should be explicitly declared.
- Using the
var
keyword for property declarations is not allowed.
This has already been a best practice for a while and will now be formalized.
Code example
// Correct.
class Foo {
public $foo;
protected function bar() {}
}
// Incorrect.
class Foo {
var $foo;
function bar() {}
}
Usage in WordPress Core
If any visibility modifiers are missing in WordPress Core code, they should be set to public
so as not to break backward-compatibility.
Visibility for class constants can not be used in WordPress Core until the minimum PHP version has been raised to PHP 7.1 (and won’t be enforced until that time).
Property and method modifier order
Proposed standards:
- When using multiple modifiers for a property or method declaration, the order should be as follows:
- First the optional
abstract
or final
keyword. - Next a visibility keyword.
- Lastly, the optional
static
keyword.
This has already been a best practice for a while and will now be formalized.
Code example
// Correct.
abstract class Foo {
public static $foo;
abstract protected static function bar();
}
// Incorrect.
abstract class Foo {
static public $foo;
static abstract protected function bar();
}
Object instantiation
Proposed standards:
- When instantiating a new instance of an object, parenthesis must always be used, even when not strictly necessary.
- There should be no space between the name of the class being instantiated and the opening parenthesis.
- Assigning the return value of an object instantiation by reference is not allowed.
New by reference has not been supported by PHP for quite a long while now.
This has already been a best practice for a while and will now be formalized.
Code example
// Correct.
$foo = new Foo();
$anon = new class() { ... };
$instance = new static();
// Incorrect.
$foo = & new Foo;
$foo = new Foo ();
Function closing brace
Proposed standards:
- There should be no blank line between the content of a function and the function’s closing brace.
Code example
// Correct.
function foo() {
// Do something.
}
// Incorrect.
function foo() {
// Do something.
}
Method chaining
Proposed standards:
- When chaining method calls over multiple lines, subsequent lines should be indented at least one tab in from the start of the statement and have no more than one tab indent difference between the indentation of the current line and indentation of the previous line.
- The arrow should be placed on the same line as the subsequent method call.
Code example
// Correct.
$someObject
->doSomething()
->doSomethingElse();
$someObject
->startSomething()
->someOtherFunc(23, 42)
->endSomething();
// Incorrect.
$someObject
->startSomething()->
someOtherFunc(23, 42)
->endSomething();
Include/Require
Proposed standards:
- No parentheses shall be used for
include[_once]
and require[_once]
statements.
As include[_once]
and require[_once]
are language constructs, they do not need parentheses around the path. - There should be exactly one space between the
include[_once]
and require[_once]
keyword and the start of the path. - It is strongly recommended to use
require[_once]
for unconditional includes.
When using include[_once]
PHP will throw a warning
when the file is not found, but will continue execution which will almost certainly lead to other errors/warnings/notices being thrown if your application depends on the file being available, potentially leading to security leaks. For that reason, require[_once]
is generally the better choice as it will throw a Fatal Error
if the file cannot be found.
This has already been a best practice for a while and will now be formalized.
Related open WPCS PR: https://github.com/WordPress/WordPress-Coding-Standards/pull/1862
Code example
// Correct.
require_once ABSPATH . 'filename.php';
// Incorrect.
include_once ( ABSPATH . 'filename.php' );
Increment/decrement operators
Proposed standards:
- There should be no space between an increment/decrement operator and the variable it applies to.
- Pre-increment/decrement should be favoured over post-increment/decrement for stand-alone statements.
“Pre” will in/decrement and then return, “post” will return and then in/decrement.
Using the “pre” version is slightly more performant and can prevent future bugs when code gets moved around.
Related open WPCS issue: https://github.com/WordPress/WordPress-Coding-Standards/issues/1511
Code example
// Correct.
for ( $i = 0; $i < 10; $i++ ) {}
--$a;
// Incorrect.
for ( $i = 0; $i < 10; $i ++ ) {}
$a--;
Magic constants
Proposed standards:
- The PHP native
__...__
magic constants should be in uppercase when used.
Code example
// Correct.
add_action( 'action_name', array( __CLASS__, 'method_name' ) );
require_once __DIR__ . '/relative-path/file-name.php';
// Incorrect.
add_action( 'action_name', array( __class__, 'method_name' ) );
require_once __dIr__ . '/relative-path/file-name.php';
Shell commands
Proposed standards:
- Use of the backtick operator is not allowed.
This has already been a best practice for a while and will now be formalized.
Complex control structure conditions
A control structure statement with multiple conditions can make for very long lines making code harder to read.
It is encouraged (but not enforced) that these long conditions are split into multiple lines.
When a long condition statement is split over multiple lines, the following (new) rules are proposed in addition to the existing rules for control structure formatting:
- The first condition should be on the same line as the control structure keyword.
- The closing parenthesis for the control structure should be on a new line, indented the same as the control structure keyword and followed by a space and the opening curly brace of the control structure.
- When splitting the conditions into multiple lines, the boolean/logical operators separating the statements should always be placed at the start of the new line.
This makes for smaller change-sets and improves readability.
Note: even with conditions spread out over multiple lines, multiple conditions per line will still be allowed as long as the condition grouping makes sense.
Code example
// Single line is allowed.
if ( function_call( $a, $b, $c ) === false && ( isset( $d, $e, $f ) && function_call( $d, $e, $f ) === true ) && function_call( $g, $h, $i ) === false ) {
// Do something.
}
// Correct formatting when using multi-line:
if ( function_call( $a, $b, $c ) === false
&& ( isset( $d, $e, $f ) && function_call( $d, $e, $f ) === true )
&& function_call( $g, $h, $i ) === false
) {
// Do something.
}
// Incorrect formatting when using multi-line:
if (
function_call( $a, $b, $c ) === false &&
( isset( $d, $e, $f ) && function_call( $d, $e, $f ) === true )
&& function_call( $g, $h, $i ) === false ) {
// Do something.
}
Feedback
Feedback on the standards proposed here is welcome in the comments.
If there are other PHP constructs and language features for which you’d like to see coding standards defined, please open an issue in the WordPress Coding Standards GitHub repository.
Kind request: while I realize that the existing stance on disallowing short array syntax is a hot topic for some people, please do not pollute the discussion about the standards which are currently being proposed by re-opening that discussion in the comments.
Short array syntax will be revisited at a later time, but is not part of the current proposal. Thank you in advance for your understanding.
Previous posts on this subject:
- https://make.wordpress.org/core/2019/07/12/php-coding-standards-changes/
- https://make.wordpress.org/core/2019/03/26/coding-standards-updates-for-php-5-6/
Props: Graceful thanks go out to @dingo_d, @garyj, @netweb, @nielsdeblaauw, @pento, @schlessera, and @SergeyBiryukov for reviewing this post prior to publication.
#modernizewp, #codingstandards, #php, #wpcs