This document contains the preferred coding style regardless of language. The examples below will be in PHP, but the accompanying rules work for Javascript or Ruby as well.
At its most basic, we generally prefer to not cram everything together. Use whitespace around parentheses, variables, and operators. Example:
class Garp_FiddleStick {
public function __construct($foo, $bar = null) {
if ($foo >= 10000) {
doSomething();
}
$this->_property = $foo;
if ($bar) {
$this->_id = 1000 + $bar;
}
}
public function loopDeLoop() {
for ($i = 0; $i < count($this->_data); ++$i) {
dump($this->_data[$i]);
}
}
}
We use spaces for indentation.
- 2 spaces for Javascript
- 4 spaces for the rest
Set your editor to replace your tabs with spaces before you're saving it in a Garp project. An example for why we use spaces instead of tabs is for instance this docblock:
/**
* @param array $data Content of new record
* @param bool $overwrite Whether to overwrite existing records.
*/
Will be all screwed up if aligned using tabs. Different contexts render tabs differently, so do not rely on them.
Same goes for aligning things like variable declarations:
$blue = 'Leonardo';
$red = 'Rafael';
$orange = 'Michaelangelo';
$purple = 'Donatello';
Just use spaces. (a good Vim plugin to help with this is Tabular)
Try to keep your lines readable. We stick to a line limit of 100 characters, but we do not enforce it - in case of urls and such.
Use Husky for hooks whenever possible.
In Vim you can enforce this/make this more visible using:
set textwidth=100
set colorcolumn=+1
In Atom you can make this visible by setting Preferred Line Length
to 100
under Settings > Editor
.
Prefix private or protected methods and properties with an underscore:
protected $_data = array();
protected function _doSomething() {
}
Also, generally we prefer protected
over private
.
Try not to use more than three (3) arguments in a function or method definition:
// BAD
function fetchFiltered($page, $limit, $color, $brand, $group, $something, $else, $etc) {
// ...
}
Convert to a parameter object (or array):
// GOOD
function fetchFiltered(array $params) {
$page = $params['page'];
// et cetera
}
See also "Introduce Parameter Object".
When calling function and spreading arguments over multiple lines, you should place all arguments on a separate line, not some. Especially not when the first argument is a closure or array and the second a wee scalar value.
// NOT LIKE THIS:
doTheThing(function ($foo) {
return $this->manipulateTheVar($foo);
}, $poo);
// LIKE THIS:
doTheThing(
function ($foo) {
return $this->manipulateTheVar($foo);
},
$poo
);
Try to write array/object/dictionary properties on new lines:
$abc = [
'pork' => 'butts',
'beef' => 'cake',
'fruit' => 'loop'
];
If you have to use a switch
, align the case
and break
statements:
switch ($breakfast) {
case 'cereal':
// ...
break;
case 'bacon':
// ...
break;
default:
// ...
break;
}
Also, remember that cool kids use Replace Conditional With Polymorphism.
Even though it's allowed to omit curly braces for one-liners, we generally prefer to always use them.
if (!array_key_exists('Leonardo', $ninjaTurtles)) array_push($ninjaTurtles, 'Leonardo');
The reasoning for this is that it's easier to add lines in the future:
if (!array_key_exists('Leonardo', $ninjaTurtles)) {
array_push($ninjaTurtles, 'Leonardo');
debug('Leonardo missing');
}
We prefer CamelCase
for identifiers, but in Ruby you are allowed to use the underscored notation.
In general, prefer longer variable and method names over short variable names.
This is not algebra, we're not trying to find x
.
Usually there's a proper semantic name for whatever value you're working with.
Comments are added with the best of intentions, obviously. But take a moment to consider whether you can make the code self-explanatory before adding a comment. Maybe your variable names are ambiguous? Maybe your function parameters can be made a little bit more clear?
If you absolutely must, write a clear comment, use proper capitalisation and punctuation. We're not bloody animals.
No hard and fast rules here, but try to stick to the following:
- Prefer maps/filters/reductions over looping
- Prefer immutable data
At GRRR, we practice Object Calisthenics. Generally, the most important bits are:
Most commonly violated in loops:
public function listTurtles() {
foreach ($turtles as $turtle) {
if ($turtle->hasWeapons()) {
foreach ($turtle->getWeapons() as $weapon) {
echo "{$turtle->name} uses weapon: {$weapon->name}";
}
}
}
}
Can be fixed using something like:
public function listTurtles() {
$output = '';
foreach ($turtles as $turtle) {
$output .= $this->getOutput($turtle);
}
echo $output;
}
public function getOutputForTurtle($turtle) {
if (!$turtle->hasWeapons()) {
return;
}
$output = '';
foreach ($turtle->getWeapons() as $weapon) {
$output .= "{$turtle->name} uses weapon: {$weapon->name}";
}
return $output;
}
Don't use this:
function getBreakfast() {
if ($this->guest->isVegetarian()) {
$bowl = new CerealBowl();
$bowl->add($nuts);
$bowl->add($berries);
$bowl->add($milk);
$breakfast = $bowl;
} else {
$breakfast = new Bacon();
}
return $breakfast;
}
Use an early return:
function getBreakfast() {
if (!$this->guest->isVegetarian()) {
return new Bacon();
}
$bowl = new CerealBowl();
$bowl->add($nuts);
$bowl->add($berries);
$bowl->add($milk);
return $bowl;
}
By extension, use a ternary operator when assigning a variable to one or another value:
const foo = yepNope ? 42 : 84;
It's immediately clear this is a single statement, being the assignment, whereas an if
/ else
statement requires you to inspect the contents of the different blocks to see what's going on.