Coding Conventions
[DEPRECATED] Style guide & coding conventions for AlumnForce projects
#Deprecated
This repository is deprecated since we follow PSR 1 and PSR 2 recommandations
#AlumnForce Coding Conventions
#Table of Contents
#MySQL
#Schemas
-
Naming:
- table names in the singular
- both table and column names in
snake_case - PK: do not repeat the table's name, and typically call it simply
id. - FK column's name MUST be named
<referenced-table>_<referenced-column>, example:user_id. - Add
_idsuffix for int columns * Add_atsuffix for timestamp columns, example:created_at - Add
_ext_idsuffix for external references columns, example:degree_ext_id - Boolean:
is_x TINYINT
-
No need of CONSTRAINT clause when defining a foreign key:
FOREIGN KEY user_id_fk (user_id) REFERENCES user (id) ON DELETE RESTRICT ON UPDATE CASCADE
-
To name an FK or an index (KEY), prefer this naming:
<column-name>_[idx|fk]. Examples:user_id_fkorcol1_col2_idx. -
By default, always set FK with
ON UPDATE CASCADE ON DELETE RESTRICT. -
Avoid
DEFAULTvalues, unless you have a good reason for it. -
Don't forget
UNSIGNEDtypes:user_id UNSIGNED INT. -
Stop specifying a never used display width with integers' declaration:
INT(1)allows to store exactly the same integers asINT(11). So prefer:user_id UNSIGNED INT. -
Add as many integrity constraints as you can:
UNIQUE,FK,NOT NULL… -
About text columns:
- use
CHAR(x)for fixed-length strings, andVARCHAR(x)for strings of length at mostx - if there is no product justification, write
VARCHAR(255)instead of any value < 255 - prefer
VARCHAR(255)toVARCHAR()because by default the latter is equivalent toVARCHAR(65535)
- use
-
Use COMMENT on columns and keep them up to date.
-
Don't forget our 2 “system” columns managed by MySQL itself:
created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
These 2 columns should never be used in writing by application code!
#Queries
-
All keywords in CAPITALS.
-
Do not put backticks
`around a table or column name if not necessary, this complicates reading. -
Always specify which type of join:
LEFT JOIN,INNER JOIN, other? -
Use table aliases as soon as there is more than one table in the query, and prefix each column name with them. Example:
SELECT U.id, D.name FROM user U INNER JOIN degree D ON D.id = U.degree_id WHERE ...
- Any SELECT must have a
LIMITclause, with the possible exception of exports.
#Patches
Because IF EXISTS doesn't exist for many DDL statements
we need to encapsulate some SQL instructions in a stored procedure.
Systematically make sure to remove the possible existing in order to reconstruct and guarantee the expected result:
for example make a DROP INDEX before the CREATE INDEX because perhaps it does not target the same columns.
Approach adopted after vote:
DROP PROCEDURE IF EXISTS clean; DELIMITER // CREATE PROCEDURE clean() BEGIN SET @exists = ( SELECT count(*) FROM information_schema.statistics WHERE table_name = 'bounce' AND index_name = 'campaign_id' AND table_schema = database() ); IF @exists > 0 THEN DROP INDEX campaign_id ON bounces; END IF; END // DELIMITER ; CALL clean(); DROP PROCEDURE IF EXISTS clean; -- True payload: CREATE INDEX campaign_id ON bounce (campaign_id);
#PHP
#General
We follow the standards defined in the PSR-0, PSR-1 and PSR-2 documents.
#Composer
- Allways specify tag/version for each dependency into
composer.json. - The
composer.lockmust be committed. - The format
Alumnforce\PackageNameshould be used as namespace.
#Notation
- Use camelCase. Acronyms have only their first letter in capital:
getPsrLogger(),$mySqlQuery. - Always use
[]notation for arrays. - Place unary operators adjacent to the affected variable,
with the excpetion of negation operator and casting:
$a++,$a--,if (! $expr) {…},$foo = (int) $bar; - Add a single space around binary operators,
including concatenation operator:
$a && $b,$a . $b - We voted against Yoda conditions.
#OOP
- By default every class property and every instance property must be private.
- Do not use magical methods (or prove that there was no alternative).
- All function calls must have parenthesis, even constructor:
new MyClass().
#PHPDoc
- Always document structure of array parameters and array returns.
- Avoid
@param array, be as accurate as possible. Examples:@param int[] <description> <structure>,@param ThisClass[] <description> <structure>. - These annotations are forbidden because without any added value:
@abstract@access public@access protected@access private@internal@static
#Practical cases
#On-the-fly assignment
On-the-fly assignment must be avoided because not very readable:
if (($myId = $obj->getById())) {...}.
Write:
$myId = $obj->getById(); if ($myId !== null) { // ... }
#Implicit if
Implicit if must be avoided because not very readable:
<expression> and <statement>;<expression> && <statement>;
Write:
if (<expression>) {
<statement>
}
#Approximate test
Let: if (<expression> === true) {...},
if <expression> is of boolean type, then simply write: if (<expression>) {…}.
If <expression> is not a boolean then be specific, e.g. if (preg_match(...) > 0) {...}
#Switch hack
To avoid: switch (true) {...}
If case expressions are not deductible from the same variable, then use if/elseif.
#Remote return
Instead of:
if ($expression) { return $foo; } // many lines... return $bar;
Write:
if ($expression) { return $foo; } else { // many lines... return $bar; }
Or better, only one return if possible:
if ($expression) { $value = $foo; } else { // many lines... $value = $bar; } return $value;
#Mixed return types
For a given function, do not use more than one data type for return value.
Bad, because of the mixture between array and int:
/** * @return array|int */ function (…) { if (…) { return [1, 2, 3]; } else { return 5; } }
Rewrite as follows:
/** * @return array */ function (…) { if (…) { return [1, 2, 3]; } else { return [5]; } }
- Same for the
string|nullcase. - The empty object being
null, it's ok withMyClass|nullcase. - The management of unexpected errors must be done via exceptions.
#Emptyness
Redundant: if (! isset($params['foo']) || empty($params['foo'])) {…}.
This is equivalent to: if (empty($params['foo'])) {…}.
#Variable name differentiation
Not very readable: foreach ($items as $item) {…}.
For example, improve it this way: foreach ($allItems as $item) {…}.