This repository is deprecated since we follow PSR 1 and PSR 2 recommandations
-
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
_id
suffix for int columns * Add_at
suffix for timestamp columns, example:created_at
- Add
_ext_id
suffix 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_fk
orcol1_col2_idx
. -
By default, always set FK with
ON UPDATE CASCADE ON DELETE RESTRICT
. -
Avoid
DEFAULT
values, unless you have a good reason for it. -
Don't forget
UNSIGNED
types: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!
-
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
LIMIT
clause, with the possible exception of exports.
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);
We follow the standards defined in the PSR-0, PSR-1 and PSR-2 documents.
- Allways specify tag/version for each dependency into
composer.json
. - The
composer.lock
must be committed. - The format
Alumnforce\PackageName
should be used as namespace.
- 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.
- 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()
.
- 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
On-the-fly assignment must be avoided because not very readable:
if (($myId = $obj->getById())) {...}.
Write:
$myId = $obj->getById();
if ($myId !== null) {
// ...
}
Implicit if
must be avoided because not very readable:
<expression> and <statement>;<expression> && <statement>;
Write:
if (<expression>) {
<statement>
}
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) {...}
To avoid: switch (true) {...}
If case
expressions are not deductible from the same variable, then use if/elseif
.
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;
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|null
case. - The empty object being
null
, it's ok withMyClass|null
case. - The management of unexpected errors must be done via exceptions.
Redundant: if (! isset($params['foo']) || empty($params['foo'])) {…}
.
This is equivalent to: if (empty($params['foo'])) {…}
.
Not very readable: foreach ($items as $item) {…}
.
For example, improve it this way: foreach ($allItems as $item) {…}
.