Checklist các case thường gặp khi review code PHP (Fuel, Laravel, Symfony) và cách refactor cho ngắn gọn, dễ hiểu, tối ưu.
// Trước
if ($user) {
if ($user->is_active) {
return $user;
}
}
return null;
// Sau
if (!$user || !$user->is_active) {
return null;
}
return $user;// Trước
if ($role === 'admin') {
$permission = 10;
} elseif ($role === 'editor') {
$permission = 5;
} else {
$permission = 1;
}
// Sau (mapping)
$map = [
'admin' => 10,
'editor' => 5,
];
$permission = $map[$role] ?? 1;// Trước
if (($age > 18 && $is_student) || ($age > 60 && !$is_student)) { ... }
// Sau
$eligibleYoung = $age > 18 && $is_student;
$eligibleSenior = $age > 60 && !$is_student;
if ($eligibleYoung || $eligibleSenior) { ... }// Trước
foreach ($users as $user) {
foreach ($user->posts as $post) {
$titles[] = $post->title;
}
}
// Sau (collection helper)
$titles = array_merge(...array_map(fn($u) => array_column($u->posts, 'title'), $users));// Trước
if ($plan < 3) { ... }
// Sau
if ($plan < self::PLAN_PREMIUM) { ... }// Trước
$price1 = $product1->getTaxedPrice();
$price2 = $product2->getTaxedPrice();
// Sau
$prices = array_map(fn($p) => $p->getTaxedPrice(), [$product1, $product2]);// Trước
if ($status === 'draft' || $status === 'pending' || $status === 'rejected') {
return false;
}
// Sau
if (in_array($status, ['draft', 'pending', 'rejected'], true)) {
return false;
}// Trước
$str = '';
foreach ($items as $i) {
$str .= $i . ',';
}
// Sau
$str = implode(',', $items);// Trước
$result = $a;
foreach ($b as $item) {
$result[] = $item;
}
// Sau
$result = array_merge($a, $b);// Trước
$value = isset($data['key']) ? $data['key'] : null;
// Sau
$value = $data['key'] ?? null;// Trước
foreach ($users as $u) {
$posts = DB::select('*')->from('posts')->where('user_id', $u['id'])->execute();
}
// Sau
$users = DB::select('users.*', 'posts.*')
->from('users')
->join('posts', 'LEFT')->on('users.id', '=', 'posts.user_id')
->execute();// Trước
$found = false;
foreach ($list as $item) {
if ($item === $target) {
$found = true;
}
}
// Sau
$found = false;
foreach ($list as $item) {
if ($item === $target) {
$found = true;
break; // tối ưu
}
}// Trước
$sum = 0;
foreach ($arr as $x) {
$sum += $x;
}
// Sau
$sum = array_sum($arr);// Trước
try {
$this->process();
} catch (Exception $e) {}
// Sau
try {
$this->process();
} catch (SpecificException $e) {
Log::error($e->getMessage());
throw $e; // hoặc xử lý hợp lý
}// Trước
if (!$user) {
throw new Exception("User not found");
}
// Sau (fail fast với custom exception)
if (!$user) {
throw new UserNotFoundException();
}// Trước
DB::query('DELETE FROM users')->execute();
DB::query('DELETE FROM posts')->execute();
// Sau (transaction)
DB::start_transaction();
try {
DB::query('DELETE FROM users')->execute();
DB::query('DELETE FROM posts')->execute();
DB::commit_transaction();
} catch (Exception $e) {
DB::rollback_transaction();
throw $e;
}// Trước
$name = $user->profile->name;
// Sau
$name = $user->profile->name ?? 'Guest';// Trước
DB::query("SELECT * FROM users WHERE id = $id");
// Sau
DB::query("SELECT * FROM users WHERE id = :id")
->parameters(['id' => $id])
->execute();// Trước
DB::select('*')->from('users')->execute();
// Sau
DB::select('id', 'name', 'email')->from('users')->execute();// Trước
$users = Model_User::query()->where('status', 1)->where('deleted_at', null)->get();
// Sau (ngắn gọn)
$users = Model_User::query()->where([['status', 1], ['deleted_at', null]])->get();// Trước
public function getUsers($active = true) { ... }
// Sau → tách hàm
public function getActiveUsers() { ... }
public function getInactiveUsers() { ... }// Trước
if ($user) {
return $user;
} else {
return null;
}
// Sau
if (!$user) return null;
return $user;// Trước
function add($a, $b) {
return $a + $b;
}
// Sau
function add(int $a, int $b): int {
return $a + $b;
}- ❌ Không để
var_dump,print_r,echotrong code. - ✅ Dùng
Log::debug(),Log::info(),Log::error().
// Trước
print_r($data);
// Sau
Log::debug('User data', $data);- Tên biến phải rõ nghĩa, có đơn vị nếu cần (
$priceUsd,$durationMinutes). - Comment giải thích WHY chứ không phải WHAT.
- Không viết tắt khó hiểu (
$cnt,$tmp).
- Sử dụng
isset()nhanh hơnarray_key_exists()trong nhiều trường hợp. - Dùng
count($arr)một lần, không gọi nhiều lần trong loop. - Cache kết quả query nếu dữ liệu không thay đổi thường xuyên.
- Dùng
foreachthay vìforkhi không cần index để code gọn hơn. - Prefetch/Chunk dữ liệu lớn thay vì load tất cả vào memory.
Dán trực tiếp vào PR comment để check nhanh:
- Code null-safe, không crash khi input rỗng/null
- Input (GET/POST) validate đúng type/format
- Không có SQL injection (luôn dùng parameter binding)
- Không có hardcode/magic numbers
- Không query lồng nhau trong loop (N+1 query)
- Exception được log & xử lý (không nuốt lỗi)
- Không còn
var_dump,print_r,echodebug - Tên biến/hàm rõ nghĩa, đúng convention
- Không còn code thừa, comment-out code
- Hàm ngắn, có thể test được, không quá nhiều trách nhiệm
-
SELECT *→ chỉ chọn field cần thiết - Không trùng lặp logic (có thể refactor thành helper)
- Có unit test hoặc test case cho logic quan trọng
- Performance ổn (loop ngắn, query tối ưu, không load dư data)
- Security ổn (escape output, không log thông tin nhạy cảm)
Khi review code:
- Refactor để code ngắn gọn, dễ đọc, tránh lặp lại.
- Tiên quyết phải xử lý: null safety, validation, query security, exception handling, performance.
- Luôn nghĩ: "Nếu input sai, flow sai, query lỗi thì chuyện gì xảy ra?"