Developer Coding Guidelines for WP Staging

If you want to participate on the developing of WP Staging please get familiar with the following coding guidelines and best practices.

  • Use PSR https://www.php-fig.org/psr/.
  • Use short Array syntax [].
  • Use camelCase for variable names.
  • Use StudlyCaps for class names.
  • Use underscores in file and folder names.
  • Prevent inline conditions.
  • Use abreviations rarely! Don’t use any abreviation thas not been approved by the team and don’t invent your own abreviations that are not commonly popular when naming properties or methods.
  • Names for classes, methods, properties should explain exactely its purpose.

Clean Code

  • The total cost of owning a mess compounds over time.
  • It’s very difficult to rebuild a legacy system from the ground up. Refactoring and incremental improvements are often the better path to take.
  • In messy codebases it can take days or weeks to accomplish tasks that should only take hours.
  • Take the time to go fast.
  • Clean code does one thing well. Bad code tries to do too much.
  • Clean code is well-tested.
  • When reading well-written code, every function does pretty much what you expected.
  • If you disagree with a principle that someone with decades of experience is teaching, you’d do well to at least consider their viewpoint before disregarding it.
  • Code is read far more often than it is written.
  • Code that is easier to read is easier to change.
  • Leave the codebase better than you found it (The Boy Scout Rule).

Comments and docBlocks

  • Comments can lie. They can be wrong to begin with, or they can be originally accurate and then become outdated over time as the related code changes. E.g. If you rename or refactor a method you always have to check if the comment is still valid.
  • Use comments to describe why something is written the way it is, not to explain what is happening.
  • Comments can often be avoided by using clearly named variables and extracting sections of code into clearly named functions.
  • Prefix your TODO comments in a consistent manner to make searching for them easier. Revisit and clean up your TODO comments periodically.
  • Don’t use docBlocks just for the sake of using them. Comments that describe what a method does, what arguments it takes, and what it returns are often redundant at best and misleading at worst.
  • Comments should include all the relevant info and context someone reading the comment will need. Don’t be lazy or vague when you write a comment.
  • Journal comments and file author comments are unnecessary due to version control and git blame.
  • Don’t comment out dead code. Just delete it. If you think you’ll need the code in the future, that’s what version control is for.
  • Comment as much as necessary, but as little as possible. If you write your method and you have the strong pressure to write a comment for it, most often your method name is not clear enough or the code does more than one thing. In that case, consider a refactoring before writing a comment.

Code Style

Binary operator spaces

Binary operators ‘=’ and ‘=>’ should be surrounded by space. There is an autoformat option in the IDE code style settings that allow you to align key values assignment like this:

$array = [
    $var    => 'foo',
    $varvar => 'foo'
];

$var     = 1;
$var100  = 100;
$var2000 = 2000;

This is a neat trick to make larger portions of similar code easier readable.

Yoda conditions

We have voted not to use Yoda conditions for ease of readability.

Not recommended:

if ( true === $var ) {}

Recommended:

if ( $var === true ) {}

Native function/constant invocation

Native invocation is a technique to add a leading backward slash (\) before function/constant invocation to speed up resolving. This way PHP doesn’t need to check whether the function/constant is from the namespace or native.

We have voted not to use the Native function/constant invocation technique for ease of readability and avoid error-prone on our code base.

But in some cases, it is allowed to use carefully and wisely.

Not recommended:

if ( \is_array($var) ) {}
if ( \PHP_SAPI === 'cli') {}

Recommended:

if ( is_array($var) ) {}
if ( PHP_SAPI === 'cli') {}

Namespaces and Class names

According to Object-Oriented Programming (OOP) principles, class names should be nouns, as they represent objects or concepts in the system. This helps to make the code more readable and understandable for other developers.

However, it is worth noting that some programming languages, including PHP, allow verbs to be used as class names. In these cases, the verb is usually used as a way to indicate that the class represents an action or behavior rather than an object.

For example, a class named “Mailer” could represent an object that sends emails, while a class named “SendMail” could represent a behavior that sends emails. While using verbs as class names are technically allowed, it can make the code more difficult to read and understand, and is generally discouraged in favor of using nouns.

  • Only the characters a-z, A-Z and 0-9 are allowed for namespace and class names.
  • Namespaces are usually written in UpperCamelCase but variations are allowed for well established names and abbreviations.
  • Class names are always written in UpperCamelCase.
  • The unqualified class name must be meant literally even without the namespace.
  • The main purpose of namespaces is categorization and ordering
  • Class names must be nouns, never adjectives.
  • The name of abstract classes must start with the word “Abstract”, class names of aspects must end with the word “Aspect”.

Incorrect naming of namespaces and classes:

Fully qualified class nameUnqualified nameRemarks
WPStaging\Framework\Session\PhpPhpThe class is not a representation of PHP
WPStaging\Framework\Cache\Backend\FileFileThe class doesn’t represent a file!
WPStaging\Framework\Session\InterfaceInterfaceNot allowed, “Interface” is a reserved keyword
WPStaging\Framework\Controller\DefaultDefaultNot allowed, “Default” is a reserved keyword
WPStaging\Framework\Objects\ManagerManagerJust “Manager” is too fuzzy

Correct naming of namespaces and classes:

Fully qualified class nameUnqualified nameRemarks
WPStaging\Framework\Util\FileSystemFileSystemThat’s the FileSystem
WPStaging\Framework\Cache\Backend\FileBackendFileBackendA File Backend
WPStaging\Framework\Session\SessionInterfaceSessionInterfaceInterface for a session
WPStaging\Framework\StandardControllerStandardControllerThe standard controller
WPStaging\Framework\ObjectManagerObjectManager

Edge cases in naming of namespaces and classes:

Fully qualified class nameUnqualified nameRemarks
WPStaging\Framework\Mvc\ControllerInterfaceControllerInterfaceConsequently the interface belongs to all the controllers in the Controller sub namespace
WPStaging\Framework\Mvc\Controller\ControllerInterface Better
WPStaging\Framework\Cache\AbstractBackendAbstractBackendSame here: In reality this class belongs to the backends
WPStaging\Framework\Cache\Backend\AbstractBackend Better

Methods

Method names should clearly describe what it does so it should begin with a verb like doSomethingmakeSomethingisSomethinghasSomething.

All method names are written in lowerCamelCase. In order to avoid problems with different filesystems, only the characters a-z, A-Z and 0-9 are allowed for method names – don’t use special characters.

Make method names descriptive, but keep them concise at the same time. Constructors must always be called __construct(), never use the class name as a method name.

myMethod()

someNiceMethodName()

betterWriteLongMethodNamesThanNamesNobodyUnderstands()

singYmcaLoudly()

__construct()

Properties & Variables

A variable should be a noun or adjective and never be a verb. It stores something and should explain what it contains.

Don’t use the keyword getSomething for a property. The get is reserved for getters and setters and for methods that explicily do something and where there is no better keyword than get.

Variable names are written in lowerCamelCase and should be:

  • Self-explanatory.
  • Not shortened beyond recognition, but rather longer if it makes their meaning clearer.

The following example shows two variables with the same meaning but different naming. You’ll surely agree the longer versions are better (don’t you …?).

Good naming of variables

$singletonObjectsRegistry

$argumentsArray

$aLotOfHtmlCode

renderHTMLtoScreen

Bad naming of variables

$sObjRgstry

$argArr

$cx

As a special exception you may use variable names like $i, $j and $k for numeric indexes in for loops if it’s clear what they mean on the first sight. But even then you should want to avoid them.

i18n, sprintf(), esc_html_* and gettext()

Always use gettext() functions first and wrap them inside sprintf() when there are parameters in a translateable string.

Wrong:

esc_html_e(sprintf('There are %1$d monkeys in the %2$s'), $number, $string), 'text-domain');

Correct:

// Correct (multi-line):

sprintf(
	/* translators: number of monkeys, location. */
	__( 'There are %1$d monkeys in the %2$s', 'text-domain' ),
	(int) $number,
	esc_html( $string )
);

// Correct (single-line):

/* translators: number of monkeys, location. */
printf( __( 'There are %1$d monkeys in the %2$s', 'text-domain' ), intval( $number ), esc_html( $string ) );

If using printf or sprintf, single quotes (‘) around the string are mandatory because double quotes (“) will tell php to interpret the $s as the s variable, which is not what we want.

To standardize with the wporg translation tool, it must include a translator’s placeholder.

/* translators: %s: name */
sprintf(esc_html__('string %s to translate', 'text-domain'), esc_html($string));

/* translators: %1$s = name1, %2$s = name2 */
sprintf(esc_html__('string %1$s to translate for %2$s', 'text-domain'), esc_html($string1), esc_html($string2));

Read more: https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#variables

If Else Conditions

Prevent inline conditions if they require extra brackets and if else statements or have more than multiple states to compare.

Otherwise it’s fine to write them inline or in separate early return conditions.

Bad:

return ((!$this->isWindowsOs() && is_executable($fileName))) && is_writable($fileName) || ($this->isWindowsOs()) && is_writable($fileName);

Good:

if (!$this->isWindowsOs && is_executeable && is_writeable()) {
     return true;
}

if ($this->isWindowsOs && is_writeable()) {
  return true;
}

return false;

Actions and Filters

  • Use constant to declare filter name, the constant name should respect the pattern FILTER_*.
  • Always begin a filter name with wpstg, do some namespacing with dots like wpstg.backups.listing_table where listing_table is the method name where that action is used.

For instance:

// Declare const for filter
const FILTER_CLONING_UPDATE_ACTIVE_PLUGINS_FILTER = 'wpstg.cloning.update.active_plugins'

and then

apply_filters(self::CLONING_UPDATE_ACTIVE_PLUGINS_FILTER, $activePlugins);

Storing Data in wp_options

  • Use constant to declare option name, the constant name should respect the pattern OPTION_*.
  • Always begin a table option name with wpstg, separate words with underscore like wpstg_backup_database_settings. Don’t forget to add the newly added option into uninstall.php

Bad:

    /**
     * The option that stores the staging sites
     */
    const STAGING_SITES_OPTION = 'wpstg_staging_sites';

Good:

    /**
     * The option that stores the staging sites
     */
    const OPTION_STAGING_SITES = 'wpstg_staging_sites';

Using WPStaging*\FileObject instead of SplFileObject

  • Use WPStaging\Framework\Filesystem\FileObject instead of SplFileObject for consistent seek() and fgets() behaviour across all PHP versions up to 8.0.1.
  • Use readAndMoveNext() instead of fgets() after fseek() for consistent behaviour across all PHP versions up to PHP 8.0.1.

Dealing with Paths

  • Always use one of the existing constants like WPSTG_PLUGIN_DIR or WPSTG_PLUGIN_URL when dealing with paths.
  • Always use the forward slash (/) and never DIRECTORY_SEPARATOR. Forward slash is compatible with Linux and Windows, while backward slash (\) works only on Windows. This will reduce complexity, makes code easier to read and prevents unexpected issues on mixed host environments or when migrating data from Linux to Windows system or vice versa or doing processing on paths like doing a search and replace of paths.
  • Use the wp_normalize_path function when needed to replace a backward slash (\) with a forward slash (/).

Updated on May 24, 2023