PHP code style in WordPress — standards

To ensure that WordPress code is formatted in the same style everywhere and is easy to read in core, plugins, and themes, it’s recommended to follow code-writing standards that have been adopted by WordPress developers. These standards are very similar to the PEAR standard, however there are also major differences. I recommend reviewing them and, when creating plugins or themes, following them whenever possible.

Addition to what’s written here (updates for PHP 5.6 version): Coding Standards Updates for PHP 5.6

Single and double quotes

If a string contains no variables, use single quotes; otherwise, use double quotes. There is no need to escape quotes in the string, and if they are present, it is recommended to alternate them:

echo '<a href="https://wp-kama.ru/static/link" title="Yeah yeah!">Link name</a>';
echo "<a href='$link' title='$linktitle'>$linkname</a>";

The second string in this example does not escape the output variables, and that’s necessary for security purposes. Therefore, for such a notation, the variables must be escaped in advance. In general, such a notation can be considered unacceptable! See the handbook section safe output.

Indentation

Indentation should always reflect the logical structure of the code. Use tabs (Tab key), not spaces — this provides more flexibility. Spaces should be used when you need to align something within a line.

Rule: tabs must be used at the beginning of the line for indentation, while spaces can be used in the middle of the line for alignment.

if ( condition ) {
	$foo     = 'somevalue';
	$foo2    = 'somevalue2';
	$foo_bar = 'somevalue3';
	$foo5    = 'somevalue4';
}

And this is how the code looks if you show the invisible tab and space characters:

if ( condition ) {
———$foo.....= 'somevalue';
———$foo2....= 'somevalue2';
———$foo_bar.= 'somevalue3';
———$foo5....= 'somevalue4';
}

For associative arrays, values must start on a new line. It’s recommended to put the “last” comma when listing array elements — that makes it easier to add new elements...

$my_array = array(
———'foo'...=> 'somevalue',
———'foo2'..=> 'somevalue2',
———'foo3'..=> 'somevalue3',
———'foo34'.=> 'somevalue3',
);

Brace style

Braces must be used for all blocks in the style shown below:

if ( condition ) {
	action1();
	action2();
} elseif ( condition2 && condition3 ) {
	action3();
	action4();
} else {
	defaultaction();
}

If there’s a long block, if possible, break it into two or more shorter blocks or functions. If such a long block is necessary, add a short comment at the end so it’s clear what the closing brace belongs to. This approach should logically be applied to blocks of 35 lines or more.

Any code that is not intuitively clear should be commented.

Always use braces even if they are not required.

if ( condition ) {
	action0();
}

if ( condition ) {
	action1();
} elseif ( condition2 ) {
	action2a();
	action2b();
}

foreach ( $items as $item ) {
	process_item( $item );
}

Note that the requirement to always use braces means that single-line style constructs are prohibited.

You can use alternative syntax for control structures: if / endif, while / endwhile — especially relevant for templates where PHP code is embedded in HTML:

<?php if ( have_posts() ) : ?>
	<div class="hfeed">
		<?php while ( have_posts() ) : the_post(); ?>
			<article id="post-<?php the_ID() ?>" class="<?php post_class() ?>">
				<!-- ... -->
			</article>
		<?php endwhile; ?>
	</div>
<?php endif; ?>

Use elseif, not else if

elseif and else if will work the same only when braces are used. If you use the colon syntax for defining conditions, you must use elseif; otherwise, we will get a fatal PHP error.

// Incorrect way:
if ( $a > $b ) {
	echo "$a is greater than $b";
} else if ( $a == $b ) {
	echo "The string above triggers a fatal error.";
}

// Correct way:
if ( $a > $b ) {
	echo "$a is greater than $b";
} elseif ( $a == $b ) {
	echo "$a equals $b";
} else {
	echo "$a is not greater than and not equal to $b";
}

Multi-line function calls

When splitting function parameters across multiple lines, each parameter must be on a separate line. This way, comments about parameters can have their own line.

Each parameter must take no more than one line. Multi-line parameters should be wrapped into a variable, and that variable should be passed into the function call.

$bar = array(
	'use_this' => true,
	'meta_key' => 'field_name',
);
$baz = sprintf(
	// translators: %s: Friend's name
	esc_html__( 'Hello, %s!', 'yourtextdomain' ),
	$friend_name
);

$a = foo(
	$bar,
	$baz,
	// translators: %s: cat
	sprintf( __( 'The best pet is a %s.' ), 'cat' )
);

Regular expressions

Use strings in single quotes for regular expressions, because unlike double quotes, they have only two characters that need escaping: \' and \\.

Opening and closing PHP tags

When adding multi-line PHP code to an HTML block, the opening and closing PHP tags must be placed on separate lines.

Correct (multi-line):

<?php
function foo() {
	?>
		<div>
		<?php echo bar( $baz, $bat ); ?>
		</div>
	<?php
}

Correct (single-line):

<input name="<?php echo esc_attr( $name ); ?>" />

Incorrect:

<?php if ( $a === $b ) { ?>
<some html>
<?php }

Do not use short PHP tags

Never use short PHP tags for public projects (e.g., plugins), because on some servers their processing may be disabled and your code will not work...

Correct:

<?php ... ?>
<?php echo $var; ?>

Incorrect:

<? ... ?>
<?= $var ?>

Remove trailing spaces

Remove trailing spaces at the end of each line.

Omit the closing PHP tag at the end of the file. If the closing PHP tag is used anyway, make sure there are no spaces or line breaks after it.

Don’t write it like this:

<?php
$foo = 'string';space
?>

End of file

Write it like this:

<?php
$foo = 'string';

End of file

Using spaces

Always put spaces after commas, and on both sides of logical operators (! && ||), comparison operators (==), concatenation operators (.), and assignment operators (.=).

x == 23
foo && bar
! foo
array( 1, 2, 3 )
$baz . '-5'
$term .= 'X'

Put spaces on both sides of opening and closing parentheses for blocks if, elseif, foreach, switch.

if ( $foo ) { ...

foreach ( $foo as $bar ) { ...

When defining a function, use spaces like this:

function my_function( $param1 = 'foo', $param2 = 'bar' ) { ...

When calling a function, like this:

my_function( $param1, func_param( $param2 ) );

When performing logical comparisons, like this:

if ( ! $foo ) { ...

When casting types, like this:

foreach ( (array) $foo as $bar ) { ...

$foo = (boolean) $bar;

When accessing array elements, add spaces around the index only if it’s a variable:

$x = $foo['bar'];   // correct
$x = $foo[ 'bar' ]; // incorrect

$x = $foo[0];       // correct
$x = $foo[ 0 ];     // incorrect

$x = $foo[ $bar ];  // correct
$x = $foo[$bar];    // incorrect

In a switch block, there must be no space before the case colon.

switch ( $foo ) {
	case 'bar': // correct
	case 'ba' : // incorrect
}

Similarly, there must be no space before the colon in return type declarations.

function sum( $a, $b ): float {
	return $a + $b;
}

Parentheses inside parentheses must have spaces:

if ( $foo && ( $bar || $baz ) ) { ...

my_function( ( $x - 1 ) * 5, $y );

Formatting SQL constructs

When formatting an SQL query, if the query is complex, it can be split into multiple lines and indented where needed. Although most constructs are written on one line, always write in uppercase parts of SQL constructs such as: UPDATE, WHERE, FROM, JOIN.

The query cleaning/escaping should be done as late as possible. For query protection, it’s recommended to use $wpdb->prepare() rather than esc_sql().

$var = "dangerous'";      // unprocessed data that may or may not be escaped
$id  = some_foo_number(); // data is expected as an integer, but we’re not sure

$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) );

%s is used for strings and %d for integers. Note that they are not 'in quotes'! $wpdb->prepare() itself escapes strings and adds quotes when needed. The advantage of prepare() is that you don’t have to remember to manually use esc_sql(), and also the query string with placeholders is more readable than if variables wrapped in esc_sql() were used there.

See the description of the $wpdb->prepare() method.

Database queries

Try not to write direct database queries. If there is a suitable function—there are many in WP—that can get the required data, use it.

Using functions instead of queries helps preserve future compatibility of the code. Also, many functions work with cache, which can significantly speed up the code.

Class, function, file, constant, variable names

Function, variable, hook names

Use lowercase letters a-z in variables, hooks, and function names, and never use CamelCase. Separate individual words with a lowercase underscore _. Don’t abbreviate variable names unnecessarily; let the code be unambiguous and self-documenting.

function some_name( $some_variable ) { [...] }
Class names

Need to use words with Uppercase_Words, separated by underscores. Any abbreviations (acronyms, short forms) must be in UPPERCASE.

class Walker_Category extends Walker { [...] }
class WP_HTTP { [...] }

Constants must be words in UPPER_CASE, separated by underscores:

define( 'DOING_AJAX', true );
File names

Must be understandable and must also contain only lowercase letters, and words must be separated by a hyphen -.

my-plugin-name.php
Class file names

They must be based on the class name with the prefix class-; underscores in the class name are replaced with hyphens, for example WP_Error becomes:

class-wp-error.php

This file naming standard applies to all existing and new files with classes. However, there are exception files: class.wp-dependencies.php, class.wp-scripts.php, class.wp-styles.php. These files use the class. prefix, with a dot after the class word instead of a hyphen.

Clear variable values in function parameters

For boolean values, prefer string values. I.e., instead of true/false when calling functions, it’s better to use some explanatory string that describes the meaning of the parameter.

Bad code:

function eat( $what, $slowly = true ) {
	...
}
eat( 'mushrooms' );
eat( 'mushrooms', true ); // what does true mean?
eat( 'dogfood', false );  // what does false mean, the opposite of true?

Because PHP doesn’t support named arguments, flag values are meaningless, and every time we encounter a function call, as in the examples above, we need to look at the function documentation. Code can be made more readable with descriptive string values instead of boolean values.

Good code:

function eat( $what, $speed = 'slowly' ) {
	...
}
eat( 'mushrooms' );
eat( 'mushrooms', 'slowly' );
eat( 'dogfood', 'quickly' );

When you need more function parameters, use the $args array. It’s even better!

Very good code:

function eat( $what, $args ) {
	...
}
eat( 'noodles', array( 'speed' => 'moderate' ) );

Interpolation for dynamic hook names

For readability and to make hooks easier to discover, hooks with variables in the name must be interpolated (enclosed in curly braces { and }), and must not be concatenated:

Braces are needed so that PHP can correctly analyze the variable data types in the interpolated string.

// correct
do_action( "{$new_status}_{$post->post_type}", $post->ID, $post );

// incorrect
do_action( $new_status .'_'. $post->post_type, $post->ID, $post );

Where possible, dynamic values in tag names should also be as short and precise as possible. $user_id is much clearer than, say, $this->id.

The ternary operator

Ternary operators are good, but it’s recommended to always check the truthy condition, not the falsy one. Otherwise, it simply misleads due to double negation. The exception is using ! empty(), because otherwise sometimes it’s just difficult to write.

How it should be checked:

// (if the condition is true) ? (do this) : (otherwise do this);
$music_type = ( 'jazz' == $music ) ? 'cool' : 'blah';
// (if the value is not empty - ! empty ) ? (do this) : (otherwise do this);

How it shouldn’t be written:

// (if the condition is not true != true) ? (do this) : (otherwise do this);
$music_type = ( 'jazz' != $music ) ? 'blah' : 'cool';

Ternary operator (short syntax)

Often when assigning a variable, the code is written like this:

$a = $b ? $b : $c;

Short syntax makes the code easier to read:

$a = $b ?: $c;

It’s recommended to use short syntax where possible.

Don’t confuse this operator with the null-coalescing operator — ??, which appeared in PHP 7. Here, if $b is not defined, php will output a notice.

Yoda conditions

When performing logical comparisons, always put constants or literals on the left, and the variable on the right.

if ( true == $the_force ) {
	$victorious = you_will( $be );
}

If you skip the second = sign in the example above (admittedly, this even happens to the most experienced of us), we will get a PHP error and we’ll see it immediately because the code won’t work. But if the construct were reversed—$the_force = true—the condition would always be true and we wouldn’t see any error, and we could miss such a serious bug, which is also sometimes difficult to catch!

You just need to get used to this “reversed” writing.

This also applies to ==, !=, ===, and !==. “Yoda conditions” for <, >, <=, or >= are significantly harder to read and it’s better not to use them.

Smart code

In short, code readability should be the top priority; it is more important than brevity or some not-obvious but convenient abbreviations.

isset( $var ) || $var = some_function();
// or
! isset( $var ) && $var = some_function();

Yes — that’s a great construct; you can see that an experienced programmer wrote it. But for any other developer, and often even for the author, to understand such a construct, they need to dig in a bit and spend extra seconds or minutes. This is not an obvious and not understandable construct, and it should be avoided. It’s better to write it longer but clearer:

if ( ! isset( $var ) ) {
	$var = some_function();
}

Error suppression operator @

From PHP documentation:

PHP supports one error control operator: the @ sign. If it precedes any expression in PHP code, any error messages generated by that expression will be ignored.

While this operator exists in core, it is often used because it’s lazy to properly handle the variable. Using it is strongly not recommended, because even the PHP documentation states:

Warning: As of today, the “@” operator suppresses output of messages even about critical errors that interrupt script execution. In addition, this means that if you used “@” to suppress errors occurring when a function runs, and if that function is unavailable or written incorrectly, further script execution will be stopped without any notifications.

Don’t use the extract() function

Based on ticket #22400. extract() is a terrible function that greatly complicates code debugging, and also makes code less readable and harder to understand. Therefore, never use extract(), except in cases where it’s impossible to avoid it — i.e., never!

Joseph Scott (English) explains why extract() is so terrible: I Don’t Like PHP’s extract() Function.

Anonymous functions

Anonymous functions are useful when you need to write a short, coherent logical construct using a PHP function. For example, a call to preg_replace_callback() can be written like this:

$caption = preg_replace_callback(
	'/<[a-zA-Z0-9]+(?: [^<>]+>)*/',
	function ( $matches ) {
		return preg_replace( '/[\r\n\t]+/', ' ', $matches[0] );
	},
	$caption
);

Such code improves readability because the developer doesn’t need to “jump” to the function code just to see what’s happening there.

Where the developer considers it appropriate, anonymous functions can be used as an alternative to creating new callback functions.

However, anonymous functions must not be used in hooks in the WordPress core as callback functions, because in this case they cannot be removed via remove_action() or remove_filter(). Outside of core, developers can pass anonymous functions to hooks; in that case, you should consider that you might need to remove the hook, and then it’s better NOT to use an anonymous function.

Namespaces

Namespaces are a simple way to encapsulate (separate, isolate) functionality. However, as it was found out, adding namespaces to the WordPress core is not a simple task that requires a well-thought-out architecture.

At this time, introducing namespaces into the WordPress core is not expected. Therefore, namespaces should not be used in the WordPress core.

Array declaration — array()

Using the long array syntax ( array( 1, 2, 3 )) for declaring arrays is, as a rule, more readable than the short array syntax ( [ 1, 2, 3 ] ), especially for those with vision problems. It’s also much clearer for beginners.

Arrays should be declared using the long array syntax.

Defining a variable inside a condition

For normal code readability, it is strongly recommended to define variables on a separate line, rather than inside an if condition:

Correct:

$sticky_posts = get_option( 'sticky_posts' );
if ( $sticky_posts ) {
	// ...
}

Incorrect:

if ( $sticky_posts = get_option( 'sticky_posts' ) ) {
	// ...
}

--

Links: