A repository containing dotfiles, code style guides, best practices used in our company related to PHP.
Predefined dotfiles used for standard configuration. Can be added on per-project basis and adjusted accordingly.
Based on php-cs-fixer Tool for automatically fix PHP coding standard issues.
Quote from editorconfig.org
EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs.
The EditorConfig project consists of a file format for defining coding styles and a collection of text editor plugins that enable editors to read the file format and adhere to defined styles.
EditorConfig files are easily readable and they work nicely with version control systems.
We basically follow the coding style related PSR standards issued by the PHP Framework Interoperability Group:
// bad
array('test', 'array');
// good
['test', 'array'];
The string concatenation operator MUST be preceded and followed by a space.
// bad
$string = 'Hello '.$name;
// good
$string = 'Hello ' . $name;
About the names of identifiers like classes, functions, methods, variables:
- A name SHOULD be descriptive, distinctive, precise and readable.
- A name SHOULD NOT be abbreviated.
- A name SHOULD NOT be pre- or postfixed with Interface, Trait or Abstract.
Abbreviation exceptions :
- The
id
field - Abbreviations common in the domain such as
VAT
A long name is not a problem, since our IDE has auto completion.
Consecutive assignments or declarations SHOULD NOT be aligned. Adding one item may require you to re-align the other items, resulting in unnecessary changes in your commit.
// bad
$short = 1;
$veryLongItem = 2;
// good
$short = 1;
$veryLongItem = 2;
Every property and method MUST be preceded by a DocBlock comment.
Property DocBlocks:
- It MUST contain a @var tag indicating the type of the property.
- It MAY contain documentation about the property.
Method DocBlocks:
- It MUST contain
@param
tags for every parameter of the method. - It MUST contain a
@return
tag if the method returns something. - It SHOULD contain a
@throws
tag if the method throws an Exception. - It MAY contain documentation about the method.
The description should provide more context than the method signature itself. Use full sentences for descriptions, including a period at the end.
Use a dockblocker plugin from your favorite editor to automate this process.
/**
* Search the Unsplash api for images.
* Should provide the search query and has optional pagination parameters.
*
* @param string $query
* @param int $page
* @param int $perPage
*
* @throws \Exception
*
* @return MediaSourceItemCollection
*/
public function search(string $query, int $page = 1, int $perPage = 10)
Comments should be avoided as much as possible by writing expressive code. If you do need to use a comment format it like this:
// There should be space before a single line comment.
/*
* If you need to explain a lot you can use a comment block. Notice the
* single * on the first line. Comment blocks don't need to be three
* lines long or three characters shorter than the previous line.
*/
PHP is a dynamically typed language. Careless type juggling can lead to unpredictable code behaviour and hard-to-find bugs.
Be as strict as possible:
- Use type hints
- Use strict comparisons (=== and !==)
- Use value objects that check their arguments
- Use constants for values that are used more than once
If your editor can complete your code, you'll likely make less mistakes. And your editor can refactor your code later on.
- Use constants instead of string values
- Use value objects instead of associative arrays
- Use exceptions instead of return types
You write code only once, but it is read (and changed) many times. Write your code so that other developers can easily understand it.
Quoting Martin Fowler:
Any fool can write code that a computer can understand.
Good programmers write code that humans can understand.
- Prefer simple solutions over complex solutions. No over-engineering. No premature optimization. No premature abstraction.
- Be consistent in approach, naming and structure with the rest of the codebase. Do not reinvent the wheel.
- Write documentation explaining why the code exists, not what it does. Don't state the obvious.
- Use object calisthenics.
Not only on the scale of a method should your code be readable. Let the methods of a class tell the story of the class.
Bad code is hard to change, unstable, fragile and non-reusable. A small change can have unexpected and undetected effects anywhere.
Design your code to minimize the ripple effect of a change.
The SOLID principles help to achieve this on the level of classes; The lesser known RCC ASS principles do the same for packages, components or even entire systems.
Do not assume that:
- input will be sane
- all users are benevolent
- other developers don't make mistakes
- external services are always available and don't change
Don't ignore errors, edge cases or faulty input. If something is really wrong, stop immediately with a descriptive and distinctive error.
Laravel provides the most value when you write things the way Laravel intended you to write it. If there's a documented way to achieve something, follow it. Whenever you do something differently, make sure you have a justification for why you didn't follow the defaults.
Configuration files must use kebab-case.
config/my-config.php
Configuration keys must use snake_case.
// config/my-config.php
return [
'live_key' => env('WAPPLA_LIVE_KEY'),
];
Avoid using the env
helper outside of configuration files. Create a configuration value from the env
variable like above.
The names given to artisan commands should all be kebab-cased.
# Good
php artisan make-good-guidelines
# Bad
php artisan deleteBadGuidelines
Public-facing urls must use kebab-case.
https://wappla.com/client-page
When referencing routes , use the action() helper or use named routes.
<a href="{{ route('namedRoute') }}">Named route example</a>
<a href="{{ action('NamedRouteController@index') }}">Action helper example</a>
Try to keep controllers simple and stick to the default CRUD keywords (index
, create
, store
, show
, edit
, update
, destroy
). Extract a new controller if you need other actions.
In the following example, we could have PostsController@favorite
, and PostsController@unfavorite
, or we could extract it to a seperate FavoritePostsController
.
class PostsController
{
public function create()
{
// ...
}
// ...
public function favorite(Post $post)
{
request()->user()->favorites()->attach($post);
return response(null, 200);
}
public function unfavorite(Post $post)
{
request()->user()->favorites()->detach($post);
return response(null, 200);
}
}
Here we fall back to default CRUD words, create
and destroy
.
class FavoritePostsController
{
public function create(Post $post)
{
request()->user()->favorites()->attach($post);
return response(null, 200);
}
public function destroy(Post $post)
{
request()->user()->favorites()->detach($post);
return response(null, 200);
}
}
This is a loose guideline that depends on the situation.
Policies must use camelCase.
Gate::define('editPost', function ($user, $post) {
return $user->id == $post->user_id;
});
@can('editPost', $post)
//
@endcan
Try to name abilities using default CRUD words, but do this from the user point of view.
Examples :
- replace
show
withview
. A server shows a resource, a user views it. - replace
index
withlist
. A server has a index of posts. A user can view a list of them.
1 space after start control structure directives
@if, @elseif, @for, @foreach, @endforeach, @unless, @forelse
No spaces after other directives
@endif, @endfor, @else, @endforelse, @endunless, @section, @endsection, @show, @yield,
@extends, @parent, @empty, @continue, @break, @php, @endphp, @include, @includeIf, @each,
@push, @endpush
1 space between render braces and their content
{{ $x }}
{!! $x !!}