This is the Aucor way of writing and formatting code. By following these guidelines you will write better code that will be easy to read. We have history in both WordPress and Drupal so our ways are relaxed combination of both.
When there is discipline in writing and formatting code, our freedom to operate will increase. To create code that other people can take and extend will give you freedom from your old code. To understand your peers' code will give you freedom to participate.
Discipline equals freedom. – Jocko Willink
There's a few of tools to do this job as there is no one-size-fits-all solution for different languages.
You are mainly resposible of keeping your code clean and easy to read. There are bunch of automations to help you, but they can only detect some things.
First time setup: Install PHPCS and plugin to text editor
Editorconfig automatically configures your code editor to project's settings. For example aucor-starter includes this file so when you have the plugin installed in your text editor you are good to go.
This is the content of .editorconfig
file:
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
First time setup: Install PHPCS and plugin to text editor
PHPCS (PHP_CodeSniffer) is a command line tool / text editor plugin that will check syntax and formatting of PHP code either on every save or once for whole project, directory or file. PHPCS has also tools to automatically fix some errors. PHPCS rules live on project's phpcs.xml
file that lists which rules it includes.
Instruction how to install PHPCS can be found on chapter 2 and PHP guidelines on chapter 3.
First time setup: -
Styles are written in SASS that extends CSS capabilities. SASS files are compiled back to CSS (Gulp does this). During this compilation you will get a tiny syntax check that will fail if your SASS is not valid or files do not exist.
There's not really many good tools for checking the formatting so you'll have to follow guidelines from chapter 4 yourself.
First time setup: -
Javascript is part of Gulp's build process so the syntax check happens when Gulp compiles files. The tool for the job is JSHint and its rules live in .jscsrc
. JSHint has also a few other files. You can ignore some files from syntax check by adding it to .jshintignore
. JSHint configuration lives in .jshintrc
.
JSHint gives warnings for potentially bad syntax in command line when Gulp is running and you save JS files.
Find JS guidelines in chapter 6.
Install a plugin to your text editor. That's it.
First you need to install PHPCS with Homebrew. If you are missing Homebrew, here's the instructions for installing it. (If you are not using MacOS, there are other ways).
brew tap homebrew/dupes
brew tap josegonzalez/homebrew-php
brew install PHP71 (or other version of your choice)
brew install php-code-sniffer
brew install phpcs
PHPCS will use ruleset from project and it will go up the directory as long as it will find a ruleset. Some text editor plugins will fallback to a premade standard if ruleset is not found. For this reason, we can create an empty ruleset for your development root to disable this. So if the project has no standard, it won't check the formatting.
Create phpcs.xml
for your development directory (for example /htdocs/
):
<?xml version="1.0"?>
<ruleset name="No Standards">
<description>Skip</description>
</ruleset>
Open Package Control
and install package Phpcs
.
Go to "Sublime Text" > Preferences > Package Settings > PHP Code Sniffer > Settings - User
Add following settings:
{
// path to the php executable.
"phpcs_php_path": "/usr/local/bin/phpcs",
// project based PHPCS
"phpcs_additional_args": {
"-n": ""
},
}
Open some PHP file from project. Open package manager with Cmd + Shift + P
and select Php Code Sniffer: Turn Execute On Save On
.
Now the code is checked every time you save. If project has phpcs.xml
, Sublime Text will automatically use it.
There is atom-phpcs. Check it out and tell how it works.
PHPCS can be used in command line without any fuss. PHPCS list of commands.
Rule: Use English always if possible.
Enforced: -
✅ aucor_get_svg();
✅ $svg_title;
❌ aucor_hae_ikoni();
❌ $ikoni_otsikko;
Rule: Use lowercase letters and underscores in variable and function names.
Enforced: -
✅ aucor_get_svg();
✅ $svg_title;
❌ aucorGetSvg();
❌ AUCORGETSVG();
❌ $svgtitle;
❌ $SvgTitle;
Rule: Prefix your functions to avoid collitions with WordPress or plugins.
Enforced: -
✅ function aucor_get_svg();
❌ function get_svg();
Rule: Constants should be in all upper-case with underscores separating words.
Enforced: PHPCS (Generic.NamingConventions.UpperCaseConstantName)
✅ define('SITE_LANGUAGE', 'fi');
❌ define('SiteLanguage', 'fi');
❌ define('site_langauge', 'fi');
Rule: Use lowercase for default PHP constants.
Enforced: PHPCS (Generic.PHP.LowerCaseConstant)
✅ true
✅ false
✅ null
❌ TRUE
❌ FALSE
❌ NULL
Rule: Function keywords should be lowercase.
Enforced: PHPCS (Squiz.Functions.LowercaseFunctionKeywords)
✅ public function aucor_get_svg()
❌ PUBLIC FUNCTION aucor_get_svg()
Rule: Built-in PHP functions should be lowercase.
Enforced: PHPCS (Squiz.PHP.LowercasePHPFunctions)
✅ echo '🍏';
✅ empty($arr);
❌ ECHO '🍏';
❌ EMPTY($arr);
Rule: Every function should have at least small description.
Enforced: -
✅
/**
* Get HTML SVG markup from theme's SVG sprite
*/
function aucor_get_svg($id, $args)
❌
function mystery_function($secret)
Rule: Documention is grouped with title, description and @groups.
Enforced: PHPCS (Generic.Commenting.DocComment)
✅
/**
* Get HTML SVG markup from theme's SVG sprite
*/
function aucor_get_svg($id, $args)
✅
/**
* Lorem ipsum
*
* Dolor sit amet integer omit nomen donec.
*
* @example lorem_ipsum('amor fati', $post_id)
*
* @param string $lorem acta non verba
* @param int $donec dolor sit amet
*
* @return int lorem ipsum dolor
*/
function lorem_ipsum($lorem, $donec)
✅
/**
* Lorem ipsum
*
* @return int lorem ipsum dolor
*/
function lorem_ipsum()
❌
/**
* Lorem ipsum
* Dolor sit amet integer omit nomen donec.
* @example lorem_ipsum('amor fati', $post_id)
* @param string $lorem acta non verba
* @param int $donec dolor sit amet
* @return int lorem ipsum dolor
*/
function lorem_ipsum($lorem, $donec)
❌
/** Get HTML SVG markup from theme's SVG sprite */
function aucor_get_svg($id, $args)
Rule: Line comments should start with lowercase letter and be just one sentence. Use code blocks for longer comments.
Enforced: -
✅
// this is a short notice
✅
/**
* Sometimes you have to explain things longer. You can make line breaks when
* it seems natural as text editor won't do it for you.
*/
❌
//I like to capitalize things.
❌
// I need to explain this very thorough. I'm going on and on. I should have used code block but here I still go. This comment is so long that it will break to another line or give you nice old scroll bar for your text editor.
Rule: Make asterisks (*) aligned and leave 1 space before content.
Enforced: PHPCS (Squiz.Commenting.DocCommentAlignment)
✅
/**
* Get HTML SVG markup from theme's SVG sprite
*/
❌
/**
* Get HTML SVG markup from theme's SVG sprite
*/
❌
/**
*Get HTML SVG markup from theme's SVG sprite
*/
Rule: No extra spaces everywhere aka WordPress spacing.
Enforced: -
✅ if (!empty($str))
✅ function lorem_ipsum($acta, $non, $verba)
✅ add_action('login_head', 'aucor_starter_favicons');
❌ if( !empty( $str ) )
❌ function lorem_ipsum( $acta, $non, $verba )
❌ add_action( 'login_head', 'aucor_starter_favicons' );
Rule: Indent with spaces, 2 at a time.
Enforced: PHPCS (Generic.WhiteSpace.ScopeIndent) + .editorconfig (indent_style + indent_size)
✅ $example; // 2 spaces
✅ $example; // 4 spaces (you can go over if it's clearer that way)
❌ $example; // 1 tab
Too little indentation is error as well!
Rule: No whitespace at the end of each line of code.
Enforced: PHPCS (Squiz.WhiteSpace.SuperfluousWhitespace) + .editorconfig (trim_trailing_whitespace)
✅ echo '🍏';
❌ echo '🍏';
Rule: All files should end with a new line.
Enforced: PHPCS (Generic.Files.EndFileNewline) + .editorconfig (insert_final_newline)
Rule: No whitespace before semicolon.
Enforced: PHPCS (Squiz.WhiteSpace.SemicolonSpacing)
✅ echo '🍏';
❌ echo '🍏' ;
Rule: Separate function arguments with space.
Enforced: PHPCS (Generic.Functions.FunctionCallArgumentSpacing)
✅ function lorem_ipsum($acta, $non, $verba)
❌ function lorem_ipsum($acta,$non,$verba)
Rule: When embedding multi-line PHP snippets within a HTML block, the PHP open and close tags must be on a line by themselves.
Enforced: PHPCS (Squiz.PHP.EmbeddedPhp)
✅
<?php
$url = get_permalink();
$title = get_the_title();
?>
❌
<?php $url = get_permalink();
$title = get_the_title(); ?>
❌
<?php }
else {
?>
Rule: Avoid echoing HTML markup if possible. It's very hard to read.
Enforced: -
✅
<article>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<a href="<?php the_permalink(); ?>"><?php pll_e('Lue lisää'); ?></a>
</article>
❌
<?php echo '<article><h1>'.get_the_title().'</h1>'.get_the_content().'<a href="'.get_permalink().'">.pll__('Lue lisää').'</a></article>'; ?>
Rule: Never use shorthand PHP start tags. Always use full PHP tags.
Enforced: PHPCS (Generic.PHP.DisallowShortOpenTag + Generic.PHP.DisallowAlternativePHPTags)
✅ <?php $url = get_permalink(); ?>
❌ <?= $url = get_permalink(); ?>
Rule: Don't put closing PHP tag at the end of a file, leave it open.
Enforced: PHPCS (PSR3.Files.ClosingTag)
File should never end with ?>
.
Rule: Use single and double quotes when appropriate. If you're not evaluating anything in the string, use single quotes.
Enforced: PHPCS (Squiz.Strings.DoubleQuoteUsage)
✅ echo 'lorem ipsum';
✅ echo "lorem ipsum \n"; // escaping requires double quotes
✅ echo "name: $name"; // variables require double quotes but please don't do it like this
❌ echo "lorem ipsum"; // no need to use double quotes
Rule: Class opening braces should be on the same line as the statement.
Enforced: PHPCS (Squiz.ControlStructures.ControlSignature)
✅
class My_Class() {
✅
function my_function() {
❌
class My_Class()
{
❌
function my_function()
{
Rule: Braces shall be used for all blocks, even when they are not required.
Enforced: PHPCS (Generic.Classes.OpeningBraceSameLine)
✅
if ($has_link) {
echo $link;
}
❌
if ($has_link)
echo $link;
Rule: Add 1 space after conditional keywords.
Enforced: PHPCS (Generic.Functions.FunctionCallArgumentSpacing)
✅
if () {
} else {
}
❌
if() {
}else{
}
Rule: Use elseif, not else if.
Enforced: PHPCS (PSR3.ControlStructures.ElseIfDeclaration)
✅ } elseif () {
❌ } else if () {
Here's a few longer examples to help you get hang of it.
✅ Example A: aucor-starter hide-users.php
<?php
/**
* Hide users' identities
*
* @package aucor_starter
*/
/**
* Rename users to sitename (only front-end)
*
* @param string $name the name of the author
*
* @return string name of the author
*/
function aucor_starter_rename_authors($name) {
if (is_admin()) {
return $name;
}
return get_bloginfo('name');
}
add_filter('the_author', 'aucor_starter_rename_authors', 100);
add_filter('the_modified_author', 'aucor_starter_rename_authors', 100);
/**
* Link user to front page
*
* @param string $url link to author archive
*
* @return string link to site url
*/
function aucor_starter_author_link_to_front_page($url) {
return get_site_url();
}
add_filter('get_the_author_link', 'aucor_starter_author_link_to_front_page', 100);
/**
* Disable users from REST API
*
* @param array $endpoints registered routes
*
* @return array registered routes
*/
function aucor_starter_disable_user_endpoints($endpoints) {
// disable list of users
if (isset($endpoints['/wp/v2/users'])) {
unset($endpoints['/wp/v2/users']);
}
// disable single user
if (isset($endpoints['/wp/v2/users/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/users/(?P<id>[\d]+)']);
}
return $endpoints;
}
add_filter('rest_endpoints', 'aucor_starter_disable_user_endpoints', 1000);
✅ Example B: aucor-lead-paragraph
/**
* Class: Aucor_Lead_Paragraph
*/
class Aucor_Lead_Paragraph {
// internal vars
private $meta_key = 'lead';
private $nonce_key = 'aucor_lead_paragraph_nonce';
// configurable vars
private $use_soft_limit;
private $use_in_excerpts;
private $char_limit;
private $auto_prepend_lead_paragraph;
/**
* Constructor
*/
public function __construct() {
// configure character limit (default: 280)
$this->char_limit = apply_filters('aucor_lead_pargraph_character_limit', 280);
// use soft or hard character limit
$this->use_soft_limit = apply_filters('aucor_lead_pargraph_use_soft_character_limit', true);
// load textdomain
if (is_admin()) {
load_plugin_textdomain('aucor-lead-paragraph', false, basename(dirname(__FILE__)) . '/languages');
}
// add meta box when needed
add_action('add_meta_boxes', array($this, 'plugin_add_meta_boxes'));
// print meta box below title
add_action('edit_form_after_title', array($this, 'plugin_edit_form_after_title'));
// handle value save
add_action('save_post', array($this, 'plugin_meta_save'));
// maybe add the lead paragraph before content
$this->auto_prepend_lead_paragraph = apply_filters('aucor_lead_paragraph_auto_prepend', true);
if ($this->auto_prepend_lead_paragraph) {
add_filter('the_content', array($this, 'plugin_the_content'));
}
// maybe use lead paragraph as excerpt if it makes sense
$this->use_in_excerpts = apply_filters('aucor_lead_paragraph_use_in_excerpts', true);
if ($this->use_in_excerpts) {
add_filter('get_the_excerpt', array($this, 'plugin_get_the_excerpt'));
}
// include lead to REST API
add_action('rest_api_init', array($this, 'append_lead_to_rest_api'));
}
/**
* Get lead paragraph
*
* @param int $post_id the ID of post
*
* @return string plain lead paragraph content without HTML markup
*/
public function get_lead_paragraph($post_id = null) {
if (empty($post_id)) {
$post_id = get_the_ID();
}
return get_post_meta($post_id, $this->meta_key, true);
}
/**
* Get lead paragraph with HTML markup
*
* @param int $post_id the ID of post
*
* @return string lead paragraph with HTML markup
*/
public function get_lead_paragraph_html($post_id = null) {
$stored_lead_paragraph = $this->get_lead_paragraph($post_id);
if (empty($stored_lead_paragraph)) {
return '';
}
return '<p class="lead" itemprop="description">' . esc_html($stored_lead_paragraph) . '</p>';
}
...
Rule: Generally avoid styling bare elements – it's very risky.
Don't apply styles to basic HTML elements if you don't know what you are doing.
✅
.primary-menu li {
display: flex;
}
❌
li {
display: flex;
}
Rule: Be only as specific as you need.
We get it, nesting things is fun. But it also leads to overly specific rules that may need !important
later on and the code will be hard to re-use.
✅
.person {
.contact-info {
}
.phone {
}
.phone-icon {
}
}
❌
.person {
.contact-info {
.phone {
.phone-icon {
}
}
}
}
Rule: Avoid !important
if you can.
The !important
is just a shortcut to be more specific but you can often get over this by making the selector more specific (longer). Using !important
is a short lived ease as there is no !importanter
.
Styles don't require tons of documentation. There are a few cases when you want to add some documentation:
Rule: Start file with name.
✅
/* ==========================================================================
Templates - Page
========================================================================== */
Rule: No magic numbers – explain weird values.
✅ width: 120%; // stretch to outer container (1200px / 1000px)
❌ width: 120%;
Rule: Avoid repeating colors and values – use variables.
✅ color: $pink;
❌ color: #C82986;
Rule: Create mixins for repeating snippets.
✅
.button-menu {
@include button-reset;
}
❌
.button-menu {
border: 0;
background: none;
border-radius: 0;
... // resetting button styles in many places
}
Rule: Avoid @extend
if you can.
The @extend
command is risky command if you don't know what you are doing. Read more on why @extend is bad.
Rule: Size of elements should be multiply of 0.5 rem.
In 8-point grid you try to size everything by multiply of 8 points like 8px, 16px, 24px... So there generally shouldn't be elements that are 7px or 10px. With rem units this is done with 0.5rem, 1rem, 1.5rem... This adds harmony and consistency to UI. Read more about 8-point grid.
✅ padding: 1rem;
✅ font-size 1.5rem;
❌ padding: 12px; // correct if the element will be in grid
❌ font-size: .9735rem; // correct if the element will be in grid
Rule: Use relative units as often as possible.
✅ padding: 1rem;
✅ line-height: 1.5;
✅ width: 50%;
❌ padding: 16px;
❌ line-height: 24px;
❌ width: 500px;
Rule: Braces go to same line as selector.
✅
.person {
❌
.person
{
✅ Example A: Simple nested class.
.menu-social-items {
@include list-reset;
li {
display: inline-block;
}
svg {
width: 1rem;
height: 1rem;
margin-right: .5rem;
}
.menu-item-label {
@include visuallyhidden; // show titles only to screen-readers
}
}
Rule: Use kebab-case for class and attribute names.
✅ <span class="phone-number">
❌ <span class="phoneNumber">
❌ <span class="phone_number">
Rule: Name values from least specific to most specific.
✅ <span class="event-date-start">
✅ <span class="event-start-date">
❌ <span class="start-date-event">
❌ <span class="date-event">
Rule: HTML comments should be usually avoided.
HTML comments are send with the document unless server/WordPress minifies HTML. This is unnecessary info and comments can include some embarassing text.
Exceptions are marking end of large containers where these comments can be helpful but not any way necessary.
✅ (none)
✅ <!-- .post -->
❌ <!-- Is this working? -->
Rule: If it's a link, it should be <a>
.
✅ <a href="/news">Click me</a>
❌ <span data-href="/news">Click me</span> <!-- JS magic -->
Rule: If it's a button and not a link, it should be <button>
.
✅ <button class="next">Next</button>
❌ <span class="next">Next</span> <!-- JS magic -->
Rule: Study WCAG standards for accessibility.
WCAG 2.0 has the best practices for accessibility. Apply where you can.
Rule: Always have alt attribute for images, even an empty one.
✅ <img alt="Description" src="image.png" />
✅ <img alt="" src="image.png" />
❌ <img src="image.png" />
Rule: No buttons without text. Use at least invisible text.
✅ <button>Open menu</button>
✅ <button><img alt="" src="hamburger.svg" /><span class="screen-reader-text">Open menu</span></button>
❌ <button><img alt="" src="hamburger.svg" /></button>
✅ Example A: Simple article markup with HTML comments.
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
<header class="entry-header">
<?php the_title('<h1 class="entry-title">', '</h1>'); ?>
</header><!-- .entry-header -->
<div class="entry-content">
<?php the_content(); ?>
</div><!-- .entry-content -->
<footer class="entry-footer">
</footer><!-- .entry-footer -->
</article><!-- #post-## -->
JS guidelines are work in progress.