สวัสดีครับเพื่อนๆ ชาว PHP ทุกคน! ผมเองก็เคยเป็นมือใหม่ที่เขียนโค้ดรกๆ รุงรังเหมือนห้องนอนตอนเช้าวันจันทร์มาก่อน 😅
ลองนึกภาพดูนะครับ ถ้าห้องนอนเรารกมากๆ จนหาของไม่เจอ มันก็คงเหมือนกับโค้ดที่ยุ่งเหยิงไม่มีระเบียบ ที่เวลาจะแก้ไขอะไรทีก็ต้องนั่งเกาหัวปวดใจ… แต่ไม่ต้องกังวลไปครับ! วันนี้ผมจะมาแชร์ 7 ขั้นตอนการเขียน PHP ให้ Clean ขึ้นด้วย Clean Code 🧹 ที่จะช่วยให้โค้ดของเพื่อนๆ สะอาด เป็นระเบียบ และดูแลรักษาง่ายขึ้นเยอะ
🌟 ทำไมต้อง Clean Code?
ก่อนจะเข้าเนื้อหา มาดูกันก่อนว่าทำไม Clean Code ถึงสำคัญ:
- ประหยัดเวลาในระยะยาว – โค้ดที่สะอาดอ่านง่าย แก้ไขง่าย ทำให้พัฒนาต่อยอดได้เร็วขึ้น
- ลดข้อผิดพลาด – โค้ดที่มีระเบียบช่วยลดโอกาสเกิด Bug
- ทำงานร่วมกับทีมได้ดีขึ้น – เพื่อนร่วมทีมจะเข้าใจโค้ดของเราได้ง่ายขึ้น
- ง่ายต่อการ Maintain – โค้ดที่สะอาดคือการลงทุนเพื่ออนาคต
📜 1.Meaningful Names ตั้งชื่อให้สื่อความหมาย
เคยเจอโค้ดแบบนี้มั้ยครับ? 🤔
// ❌Bad Code
function calc($a, $b) {
return $a + $b;
}
$x = 5;
$y = 10;
$z = calc($x, $y);
โอ้ว… นี่มันอะไรกันเนี่ย? $a, $b, $x, $y ไม่มีใครรู้ว่าตัวแปรพวกนี้เก็บค่าอะไร! มาดูโค้ดที่ดีกว่ากัน:
// ✅Good Code
function calculateTotalPrice(float $basePrice, float $taxAmount): float {
return $basePrice + $taxAmount;
}
$productPrice = 5.00;
$salesTax = 10.00;
$totalPrice = calculateTotalPrice($productPrice, $salesTax);
💡 Tips สำหรับการตั้งชื่อ
– ใช้คำที่สื่อความหมายชัดเจน
– หลีกเลี่ยงตัวย่อที่กำกวม (ยกเว้นกรณีที่เป็นที่รู้จักกันดี เช่น ID, HTML)
– ใช้รูปแบบการตั้งชื่อที่สอดคล้องกัน (camelCase สำหรับ method, PascalCase สำหรับ class)
– ตั้งชื่อตามหน้าที่หรือความหมาย ไม่ใช่ตามชนิดข้อมูล
🎯 2.Single Responsibility Principle หนึ่งหน้าที่ หนึ่งความรับผิดชอบ
คลาสหรือฟังก์ชันควรมีหน้าที่เดียวที่ชัดเจน เหมือนกับพนักงานในร้านอาหาร พ่อครัวทำอาหาร พนักงานเสิร์ฟเสิร์ฟอาหาร แคชเชียร์คิดเงิน ไม่ใช่ให้คนเดียวทำทุกอย่าง!
// ❌Bad Code
class User {
public function saveUser($userData) {
// Validate data
if (empty($userData['email'])) {
throw new Exception('Email required');
}
// Connect to database
$db = new PDO('mysql:host=localhost;dbname=myapp', 'user', 'pass');
// Save to database
$stmt = $db->prepare("INSERT INTO users ...");
// Send welcome email
mail($userData['email'], 'Welcome!', 'Thank you for registering...');
}
}
มาแยก Class ให้ทำหน้าที่เดียวดีกว่า:
// ✅Good Code
class UserValidator {
public function validate(array $userData): bool {
return !empty($userData['email']);
}
}
class UserRepository {
private $db;
public function save(User $user): void {
// Database logic only
}
}
class EmailService {
public function sendWelcomeEmail(string $email): void {
// Email logic only
}
}
class UserRegistration {
private $validator;
private $repository;
private $emailService;
public function register(array $userData): void {
$this->validator->validate($userData);
$user = new User($userData);
$this->repository->save($user);
$this->emailService->sendWelcomeEmail($userData['email']);
}
}
💡 Tips & Tricks
– ถ้าต้องใช้คำว่า “และ” ในการอธิบายว่าคลาส/ฟังก์ชันทำอะไร แสดงว่าควรแยกมันออก
– ใช้ Dependency Injection เพื่อแยกความรับผิดชอบ
– แต่ละคลาสไม่ควรมีเกิน 3-4 properties
– ถ้าฟังก์ชันยาวเกิน 20 บรรทัด ลองพิจารณาแยกมันออก
🛠 3.Functions/Methods Best Practices เขียนฟังก์ชันให้สวยใสไร้สิว
ผมมีกฎง่ายๆ ในการเขียนฟังก์ชัน: “ทำน้อย ทำดี ทำชัด” 😎
// ❌Bad Code
function handleUser($user, $action, $data = null) {
if ($action == 'create') {
if ($data) {
if (isset($data['name'])) {
// 20 lines of creation logic
} else {
throw new Exception('Name required');
}
}
} elseif ($action == 'update') {
if ($data) {
// 20 lines of update logic
}
} elseif ($action == 'delete') {
// 10 lines of deletion logic
}
}
มาดูโค้ดที่สะอาดกว่ากัน:
// ✅Good Code
class UserModel
{
public function __construct(private UserRepository $userRepository) {}
public function createUser(array $userData): User
{
$this->validateUserData($userData);
$user = new User($userData);
$this->userRepository->save($user);
return $user;
}
public function updateUser(User $user, array $newData): User
{
$this->validateUserData($newData);
$user->update($newData);
$this->userRepository->save($user);
return $user;
}
public function deleteUser(User $user): void
{
$this->userRepository->delete($user);
}
private function validateUserData(array $data): void
{
if (!isset($data['name'])) {
throw new ValidationException('Name is required');
}
}
}
💡 Tips & Tricks
– ฟังก์ชันไม่ควรรับพารามิเตอร์เกิน 3 ตัว
– ใช้ Type Hinting และ Return Type Declaration เสมอ
– หลีกเลี่ยง flag parameters ให้แยกเป็นฟังก์ชันต่างหาก
– ระวัง side effects ฟังก์ชันควรทำแค่สิ่งที่ชื่อมันบอก
📝 4.Comments and Documentation – เขียน Comment ให้เป็น
Comment เหมือนเครื่องปรุงอาหารนะครับ ใส่พอดีอร่อย ใส่เยอะเกินพัง! 🌶️
// ❌Bad Comments
// Loop through users
foreach ($users as $user) {
// Check if user is active
if ($user->isActive()) {
// Send email
$this->mailer->send($user->email);
}
}
มาดู Comment ที่มีประโยชน์กัน:
// ✅Good Code
/**
* Sends promotional emails to active users who haven't been
* contacted in the last 30 days.
*
* @param User[] $users Array of user objects
* @throws MailerException When email service is unavailable
*/
function sendPromotionalEmails(array $users): void {
$thirtyDaysAgo = new DateTime('-30 days');
foreach ($users as $user) {
if (!$this->shouldSendPromoEmail($user, $thirtyDaysAgo)) {
continue;
}
$this->mailer->sendPromoEmail($user);
}
}
💡 Tips & Tricks
– ใช้ PHPDoc สำหรับเอกสาร API
– Comment ควรอธิบาย “ทำไม” ไม่ใช่ “ทำอะไร”
– ถ้าต้องเขียน Comment อธิบายโค้ด แสดงว่าโค้ดอาจซับซ้อนเกินไป
– อัพเดท Comment เมื่อโค้ดเปลี่ยน!
📏 5.Code Formatting and PSR Standards – จัดโค้ดให้สวย
โค้ดก็เหมือนการแต่งตัวนะครับ ถ้าดูดี ความมั่นใจก็มา! ✨
// ❌Bad Formatting
class userController{
function create($data){
$user=new User;
$user->name=$data['name'];
$user->email=$data['email'];
$user->save();
}
}
มาดูโค้ดที่จัดระเบียบตาม PSR-12:
// ✅Good Code
class UserController
{
public function create(array $data): User
{
$user = new User();
$user->setName($data['name']);
$user->setEmail($data['email']);
$user->save();
return $user;
}
}
💡 Tips & Tricks
– ใช้ Tools อย่าง PHP_CodeSniffer หรือ PHP CS Fixer
– ตั้งค่า IDE ให้ format โค้ดอัตโนมัติ
– ทำตาม PSR-1 และ PSR-12
– ใช้ .editorconfig เพื่อให้ทุกคนในทีมใช้การจัดรูปแบบเดียวกัน
🚨 6.Error Handling จัดการ Error อย่างมืออาชีพ
Error ก็เหมือนปัญหาชีวิตต้องรับมือให้เป็น ไม่ใช่เมินใส่! 😤
// ❌Bad Error Handling
function processPayment($amount) {
try {
// process payment
} catch (Exception $e) {
// แย่มาก! จับ Exception ทั้งหมดแล้วเงียบเฉย
error_log($e->getMessage());
}
}
มาดูการจัดการ Error ที่ดีกว่า:
// ✅Good Code
class PaymentService
{
public function processPayment(Payment $payment): TransactionResult
{
try {
$this->validatePayment($payment);
$result = $this->gateway->charge($payment);
$this->logger->info('Payment processed', [
'payment_id' => $payment->getId(),
'amount' => $payment->getAmount()
]);
return $result;
} catch (PaymentValidationException $e) {
$this->logger->warning('Invalid payment', [
'payment_id' => $payment->getId(),
'error' => $e->getMessage()
]);
throw $e;
} catch (PaymentGatewayException $e) {
$this->logger->error('Payment gateway error', [
'payment_id' => $payment->getId(),
'error' => $e->getMessage()
]);
throw new PaymentFailedException(
'Payment processing failed',
$e->getCode(),
$e
);
}
}
}
หลักการจัดการ Error:
– สร้าง Custom Exception ที่มีความหมาย
– Log error message ที่มีประโยชน์
– จัดการ error ในระดับที่เหมาะสม
– ไม่เปิดเผยข้อมูลสำคัญในข้อความ error
🧪 7.Testing Practices เขียนเทสต์อย่างมืออาชีพ
การเขียนเทสต์เหมือนการซ้อมก่อนแสดงจริง ช่วยให้มั่นใจว่าทุกอย่างจะทำงานได้ตามที่ตั้งใจไว้ และช่วยป้องกันบั๊กที่อาจเกิดขึ้นในอนาคต
// ❌Bad Test
class ProductTest extends TestCase
{
public function testEverything()
{
$product = new Product();
$product->setName('Phone');
$product->setPrice(1000);
$product->setStock(5);
$this->assertTrue($product->save());
$this->assertTrue($product->updateStock(3));
$this->assertEquals(2, $product->getStock());
}
}
มาดูโค้ด Testing ที่ดีกัน:
// ✅Good Code
class ProductTest extends TestCase
{
private Product $product;
protected function setUp(): void
{
$this->product = new Product();
}
public function testProductCanBeSaved(): void
{
$this->product->setName('Phone');
$this->product->setPrice(1000);
$result = $this->product->save();
$this->assertTrue($result);
$this->assertDatabaseHas('products', [
'name' => 'Phone',
'price' => 1000
]);
}
public function testStockCanBeUpdated(): void
{
$this->product->setStock(5);
$this->product->updateStock(3);
$this->assertEquals(2, $this->product->getStock());
}
/**
* @test
* @dataProvider invalidStockValues
*/
public function itShouldThrowExceptionForInvalidStock($invalidValue): void
{
$this->expectException(InvalidStockException::class);
$this->product->setStock($invalidValue);
}
public function invalidStockValues(): array
{
return [
'negative value' => [-1],
'non numeric' => ['abc'],
'too large' => [1000001]
];
}
}
💡 Tips & Tricks
– เขียน test cases ที่ครอบคลุมทั้ง happy path และ edge cases
– ใช้ Data Providers สำหรับทดสอบหลายๆ กรณี
– แยก tests ให้เป็นหมวดหมู่และตั้งชื่อให้สื่อความหมาย
– อย่าลืมทดสอบ error cases ด้วย
– ใช้ fixtures และ factories เพื่อสร้าง test data
– รัน tests ก่อน commit code เสมอ
🎯 สรุป เริ่มต้นเขียน Clean Code วันนี้!
การเขียน Clean Code อาจดูเหมือนเป็นภาระในตอนแรก แต่เชื่อผมเถอะครับ ว่ามันคุ้มค่าในระยะยาวแน่นอน! เหมือนกับการล้างจานทันทีหลังกินข้าว อาจจะขี้เกียจตอนแรก แต่ดีกว่าปล่อยให้มันกองจนล้างยากในภายหลัง 😅
🔑 Key Takeaways
- ตั้งชื่อให้สื่อความหมาย ไม่ต้องให้คนอื่นมานั่งเดาว่าตัวแปรนี้เก็บอะไร
- แยกความรับผิดชอบให้ชัดเจน อย่าให้ฟังก์ชันเดียวทำงานหลายอย่าง
- เขียนฟังก์ชันให้กระชับ อ่านง่าย
- คอมเมนต์เท่าที่จำเป็น แต่ให้มีคุณภาพ
- จัดระเบียบโค้ดตามมาตรฐาน PSR
- จัดการ errors อย่างเป็นระบบ
- เขียน tests ให้ครอบคลุม
📚 แหล่งเรียนรู้เพิ่มเติม
🎯 ลองฝึกปฏิบัติ
- เลือกโค้ดเก่าสักส่วนที่คุณเคยเขียนไว้
- ลองประเมินตามกฎทั้ง 7 ข้อ
- Refactor โค้ดให้สะอาดขึ้น
- แชร์ผลลัพธ์กับเพื่อนร่วมทีมและขอ feedback
จำไว้ว่า Clean Code ไม่ได้เกิดขึ้นในวันเดียวมันเป็นทักษะที่ต้องฝึกฝนและพัฒนาไปเรื่อยๆ แต่ยิ่งฝึกเร็ว ยิ่งได้เปรียบ! 💪
แล้วเจอกันใหม่ในบทความหน้านะครับเพื่อนๆ! Happy Coding! 🚀