Code Quality
Prolog
Bagaimana jika bug bisa terdeteksi tanpa harus menge-run program? Bagaimana jika bug bisa terdeteksi secara otomatis, tanpa harus melakukan code review?
Pesan di atas adalah contoh hasil static code analysis dengan Larastan terhadap kode yang sudah lolos code review. Larastan 1, code reviewer 0.
Kemampuan dan energi code reviewer (manusia) ada batasnya. Hal ini yang kadang menyebabkan kode yang buruk masih bisa lolos reviu dan naik ke production. Untuk itulah peran tools seperti Larastan menjadi sangat penting untuk menjaga kualitas kode. Selain karena bukan manusia (jadi tidak ada kata lelah), melakukan reviu kode dengan menggunakan tools juga lebih obyektif karena ada seperangkat rules yang terdefinisi dan telah disepakati sebelumnya.
Code reviewer bisa fokus mengurusi hal-hal penting lainnya, seperti:
- Penamaan
- Algoritma
- Arsitektur Class
- Dan hal-hal lain yang butuh wisdom.
Code Style: Pint
Tujuan
Memastikan cara penulisan kode seragam antar programmer meskipun menggunakan IDE/editor yang berbeda.
Tools
https://github.com/laravel/pint
Instalasi
composer require laravel/pint --dev
Pemakaian
🌟 Jalankan perintah-perintah berikut ini dari folder aplikasi.
Melakukan fixing otomatis:
vendor/bin/pint
Melakukan pengecekan saja tanpa fixing, tambahkan opsi --dry-run
:
vendor/bin/pint --test
Jika command-nya terlalu panjang untuk diingat, tambahkan shortcut/alias ke composer.json:
"scripts": {
"pint": [
"vendor/bin/pint"
]
}
Selanjutnya, kita cukup memanggil alias yang sudah didefinisikan di atas:
composer pint
Customize Config
pint.json
{
"preset": "psr12",
"rules": {
"simplified_null_return": true,
"braces": false,
"new_with_braces": {
"anonymous_class": false,
"named_class": false
}
}
}
Code Style: PHP-CS-Fixer
Tujuan
Dikarenakan Pint mengharuskan spesifikasi khusus minimal PHP 8.0, apabila ada project yang masih perlu menggunakan PHP-CS-Fixer karena masalah compatibility (masih menggunakan < PHP 8.0), kita bisa membuat konfigurasi PHP-CS-Fixer yang sejalan dengan Pint.
Tools
https://github.com/FriendsOfPHP/PHP-CS-Fixer
Instalasi
composer require friendsofphp/php-cs-fixer --dev
Pemakaian
🌟 Jalankan perintah-perintah berikut ini dari folder aplikasi.
Melakukan fixing otomatis:
vendor/bin/php-cs-fixer fix --diff
Melakukan pengecekan saja tanpa fixing, tambahkan opsi --dry-run
:
vendor/bin/php-cs-fixer fix --dry-run --diff
Jika command-nya terlalu panjang untuk diingat, tambahkan shortcut/alias ke composer.json:
"scripts": {
"cs-check": [
"vendor/bin/php-cs-fixer fix --dry-run --diff"
],
"cs-fix": [
"vendor/bin/php-cs-fixer fix --diff"
]
}
Selanjutnya, kita cukup memanggil alias yang sudah didefinisikan di atas:
composer cs-check
composer cs-fix
Customize Config
.php-cs-fixer.php
<?php
$rules = [
'array_indentation' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => [
'default' => 'single_space',
'operators' => ['=>' => null],
],
'blank_line_after_namespace' => true,
'blank_line_after_opening_tag' => true,
'blank_line_before_statement' => [
'statements' => ['return'],
],
'braces' => true,
'cast_spaces' => true,
'class_attributes_separation' => [
'elements' => [
'const' => 'one',
'method' => 'one',
'property' => 'one',
'trait_import' => 'none',
],
],
'class_definition' => [
'multi_line_extends_each_single_line' => true,
'single_item_single_line' => true,
'single_line' => true,
],
'clean_namespace' => true,
'compact_nullable_typehint' => true,
'concat_space' => [
'spacing' => 'none',
],
'constant_case' => ['case' => 'lower'],
'declare_equal_normalize' => true,
'elseif' => true,
'encoding' => true,
'full_opening_tag' => true,
'fully_qualified_strict_types' => true,
'function_declaration' => true,
'function_typehint_space' => true,
'general_phpdoc_tag_rename' => true,
'heredoc_to_nowdoc' => true,
'include' => true,
'increment_style' => ['style' => 'post'],
'indentation_type' => true,
'integer_literal_case' => true,
'lambda_not_used_import' => true,
'linebreak_after_opening_tag' => true,
'line_ending' => true,
'lowercase_cast' => true,
'lowercase_keywords' => true,
'lowercase_static_reference' => true,
'magic_method_casing' => true,
'magic_constant_casing' => true,
'method_argument_space' => [
'on_multiline' => 'ignore',
],
'multiline_whitespace_before_semicolons' => [
'strategy' => 'no_multi_line',
],
'native_function_casing' => true,
'native_function_type_declaration_casing' => true,
'no_alias_functions' => true,
'no_extra_blank_lines' => [
'tokens' => [
'extra',
'throw',
'use',
],
],
'no_alias_language_construct_call' => true,
'no_alternative_syntax' => true,
'no_binary_string' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_closing_tag' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_mixed_echo_print' => [
'use' => 'echo',
],
'no_multiline_whitespace_around_double_arrow' => true,
'no_short_bool_cast' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_spaces_after_function_name' => true,
'no_space_around_double_colon' => true,
'no_spaces_around_offset' => [
'positions' => ['inside', 'outside'],
],
'no_spaces_inside_parenthesis' => true,
'no_trailing_comma_in_list_call' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_trailing_whitespace' => true,
'no_trailing_whitespace_in_comment' => true,
'no_unneeded_control_parentheses' => [
'statements' => ['break', 'clone', 'continue', 'echo_print', 'return', 'switch_case', 'yield'],
],
'no_unneeded_curly_braces' => true,
'no_unset_cast' => true,
'no_unreachable_default_argument_value' => true,
'no_useless_return' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'no_unused_imports' => true,
'normalize_index_brace' => true,
'not_operator_with_successor_space' => true,
'object_operator_without_whitespace' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'psr_autoloading' => false,
'phpdoc_indent' => true,
'phpdoc_inline_tag_normalizer' => true,
'phpdoc_no_access' => true,
'phpdoc_no_package' => true,
'phpdoc_no_useless_inheritdoc' => true,
'phpdoc_scalar' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_summary' => false,
'phpdoc_to_comment' => false,
'phpdoc_tag_type' => true,
'phpdoc_trim' => true,
'phpdoc_types' => true,
'phpdoc_var_without_name' => true,
'return_type_declaration' => [
'space_before' => 'none',
],
'self_accessor' => true,
'short_scalar_cast' => true,
'simplified_null_return' => false,
'single_blank_line_at_eof' => true,
'single_blank_line_before_namespace' => true,
'single_class_element_per_statement' => [
'elements' => ['const', 'property'],
],
'single_import_per_statement' => true,
'single_line_after_imports' => true,
'single_line_comment_style' => [
'comment_types' => ['hash'],
],
'single_quote' => true,
'space_after_semicolon' => true,
'standardize_not_equals' => true,
'switch_case_semicolon_to_colon' => true,
'switch_case_space' => true,
'ternary_operator_spaces' => true,
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'visibility_required' => [
'elements' => ['method', 'property'],
],
'whitespace_after_comma_in_array' => true,
'Laravel/laravel_phpdoc_alignment' => true,
'Laravel/laravel_phpdoc_order' => true,
'Laravel/laravel_phpdoc_separation' => true,
];
$finder = PhpCsFixer\Finder::create()
->in([
__DIR__.'/app',
__DIR__.'/config',
__DIR__.'/database',
__DIR__.'/modules',
__DIR__.'/resources',
__DIR__.'/routes',
__DIR__.'/tests',
])
->name('*.php')
->notName('*.blade.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return (new PhpCsFixer\Config())
->setFinder($finder)
->setRules($rules)
->setRiskyAllowed(true)
->setUsingCache(true)
->registerCustomFixers([
// Laravel...
new \Laravolt\Pint\Fixers\LaravelPhpdocOrderFixer(),
new \Laravolt\Pint\Fixers\LaravelPhpdocOrderFixer(),
new \Laravolt\Pint\Fixers\LaravelPhpdocOrderFixer(),
]);
Static Analysis
Tujuan
Menemukan bug sebelum bug menemukan kita.
Tools
- https://phpstan.org/
- https://github.com/nunomaduro/larastan
Instalasi
composer require nunomaduro/larastan --dev
composer require spaze/phpstan-disallowed-calls --dev
Konfigurasi
phpstan.neon
includes:
- ./vendor/nunomaduro/larastan/extension.neon
- ./vendor/spaze/phpstan-disallowed-calls/extension.neon
parameters:
paths:
- app
- config
# The level 9 is the highest level
level: 8
disallowedFunctionCalls:
- function: 'env()'
message: 'use config() instead'
allowIn:
- config/*.php
Pemakaian
vendor/bin/phpstan analyse
Cognitive Complexity
Tujuan
Memastikan kode mudah dibaca dan di-maintain untuk jangka waktu yang panjang.
Tools
https://www.sonarlint.org/
Instalasi
- https://blog.javan.co.id/meningkatkan-code-quality-dengan-plugin-sonarlint-di-intellij-idea-36705b6cd8fa
- https://blog.javan.co.id/cara-mudah-meningkatkan-kualitas-kode-menggunakan-sonar-37c6f8e0239b
GrumPHP
Tujuan
Menjalankan semua tools di atas setiap kali kita melakukan perubahan kode bisa jadi menyita cukup waktu dan berpotensi lupa. Oleh sebab itu, Laravolt sudah dilengkapi dengan GrumpPHP (https://github.com/phpro/grumphp) yang akan melakukan pengecekan secara otomatis setiak kali kita melakukan commit. Jika ada satu saja pengecekan yang gagal, maka commit tidak bisa dilakukan dan kita harus memperbaiki kode terlebih dahulu.
Instalasi
composer require phpro/grumphp --dev
Konfigurasi
grumphp.yml
grumphp:
tasks:
phpcsfixer:
config: .php-cs-fixer.php
phpstan:
configuration: phpstan.neon
use_grumphp_paths: false
Pemakaian
Pengecekan Manual
vendor/bin/grumphp run
Otomatis
vendor/bin/grumphp git:init
Setelah kode di atas dijalankan, maka GrumPHP akan dijalankan secara otomatis sebelum commit.