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) {…}.

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.
