WordPress PHP Coding Standards

ก่อนอ่านโพสต์นี้ อยากให้อ่าน WordPress Coding Standards สำคัญไฉน? ก่อน

WordPress ถูกพัฒนาด้วยภาษา PHP แต่ว่าโค้ดสไตล์อาจจะต่างกับสำนักอื่นบ้าง แล้วก็อาจจะดูคล้ายๆ กับ Pear Standards บ้าง เราควรจะยึดอย่างใดอย่างหนึ่งเท่านั้น เพื่อให้โค้ดสไตล์ทั้งโปรเจคไปในทิศทางเดียวกัน 🙂 มาดูกันว่ามีอะไรบ้าง

การใช้ Single Quote และ Double Quotes

ถ้าเราไม่ต้องการที่จะ evaluate อะไรในสตริง ให้เราใช้ single quote แล้วก็สิ่งที่ไม่ควรทำคือ ไม่ควร escape ตัว quote ในสตริง เพราะว่าเราสามารถใช้ quote ตามตัวอย่างข้างล่างนี้ได้

echo '<a title="Yeah yeah!" href="/static/link">Link name</a>';
echo "<a title="$linktitle" href="$link">$linkname</a>";

แต่จุดนี้ก็มีข้อยกเว้นสำหรับ JavaScript เพราะว่าบางทีก็ต้องการ double quotes เท่านั้น แล้วค่า attribute อะไรที่อยู่ในแท็ก เราควรจะเอาใส่ฟังก์ชั่น esc_attr() ก่อน จะทำให้พวก single หรือ double quotes ไม่ก่อปัญหาด้านความปลอดภัย เวลาที่เราใส่ quote พวกนี้ไม่ครบคู่

การ Indentation

การ indent โค้ดควรจะใช้ tab จริงๆ ไม่ใช่ space (ตรงจุดนี้ผมไม่ค่อยเห็นด้วยเท่าไหร่ เพราะว่า editor ของแต่ละคนตั้งค่าความกว้างของ tab ไม่เท่ากัน แล้วจะทำให้โค้ดแสดงผลในบางเครื่องเละ ผมชอบ space มากกว่า)

ถ้ามีโค้ดส่วนไหนที่เป็นบล็อกๆ เราควรจะจัดให้มันเรียงสวยๆ ตรงกันหน่อย ตัวอย่างเช่น

$foo   = 'somevalue';
$foo2  = 'somevalue';
$foo34 = 'somevalue';
$foo5  = 'somevalue';

สำหรับอาเรย์ที่เป็น associative เราก็ควรให้มันอยู่คนละบรรทัดกัน

$my_array = array(
    'foo'   => 'somevalue',
    'foo2'  => 'somevalue2',
    'foo3'  => 'somevalue3',
    'foo34' => 'somevalue3',
);

สังเกตตรงเครื่องหมายคอมม่าที่ตัวล่างสุด แนะนำให้ใส่ที่ตัวสุดท้ายทุกครั้ง เพราะจะง่ายมาก เวลาที่เราสลับอันดับของ ของที่อยู่ในอาเรย์ ไม่ต้องมากังวล syntax error อีกต่อไป

การเปิดปิดปีกกา Brace Style

ควรจะใช้เมื่อเรามีโค้ดเป็นบล็อกๆ เช่น

if ( condition ) {
    action1();
    action2();
} elseif ( condition2 && condition3 ) {
    action3();
    action4();
} else {
    defaultaction();
}

แล้วถ้าเรามีบล็อกยาวๆ ที่หลีกเลี่ยงไม่ได้ เราก็ควรจะใส่คอมเม้นต์สั้นๆ ไว้เพื่อบอกจุดสิ้นสุด แต่ถ้าบล็อกไหนมีแค่บรรทัดเดียว เราก็สามารถละไว้ได้ เช่น

if ( condition )
    action1();
elseif ( condition2 )
    action2();
else
    action3();

ถ้าเกิน 1 บรรทัดเมื่อไหร่ ก็ควรใช้ brace นะ แล้วก็ถ้าเขียนเป็นลูป ควรจะใช้ตลอด เพราะจะทำให้อ่านง่ายขึ้น

foreach ( $items as $item ) {
    process_item( $item );
}

การใช้ Regular Expression

ควรจะใช้ฟังก์ชั่นที่นำหน้าด้วย preg_ เพราะจะ compatible กับภาษา Perl ลองอ่านรายละเอียดที่นี่ PCRE แล้วก็ให้ใช้ single quote เป็นหลัก เพราะ regualr expression จะมี \' กับ \\ เท่านั้น

ไม่ควรมี Shorthand PHP Tags

ควรจะเขียนให้แท็กให้เต็มตลอด เช่น

<?php ... ?>
<?php echo $var; ?>

ไม่ควร

<? ... ?>
<?=$var; ?>

Space ตามหลังแท็กปิด PHP

ให้เอาออกซะ หลังแท็กปิด PHP ไม่ควรจะมีตัวอักษรอะไรเลย ไม่งั้นอาจจะทำให้เกิดข้อผิดพลาด PHP Warning : cannot modify header information ขึ้นได้

การใช้ Space

ใช้ space หลังคอมม่า แล้วก็ฝั่งซ้ายฝั่งขวาของพวก operation การเปรียบเทียบ และการ assignment ต่างๆ

x == 23
foo && bar
! foo
array( 1, 2, 3 )
$baz . '-5'
$term .= 'X'

ใส่ space ทั้งด้านหน้าด้านหลังของ if, elseif, foreach, for และ switch

foreach ( $foo as $bar ) { ...
if ( ! foo ) { ...

แล้วก็ตอนประกาศฟังก์ชั่น

function my_function( $param1 = 'foo', $param2 = 'bar' ) { ...

ตอนเรียกฟังก์ชั่น

my_function( $param1, func_param( $param2 ) );

ตอนทำ type casting ไม่ต้องใส่ space ตรง type

foreach ( (array) $foo as $bar ) { ...
$foo = (boolean) $bar;

ตอนเรียกของในอาเรย์ให้มี space เฉพาะตอนที่ใช้ตัวแปร

$x = $foo['bar'];   // ใช่
$x = $foo[ 'bar' ]; // ไม่ใช่
$x = $foo[0];       // ใช่
$x = $foo[ 0 ];     // ไม่ใช่
$x = $foo[ $bar ];  // ใช่
$x = $foo[$bar];    // ไม่ใช่

การเขียนฟอร์แมต SQL

ถ้าคำสั่งซับซ้อนหรือว่ายาวเกินไป ก็ให้ตบลงมาหลายๆ บรรทัด จะได้อ่านง่ายขึ้น แล้วก็ส่วนที่เป็นคำสั่งเช่น UPDATE หรือพวก WHERE ให้ใช้ตัวใหญ่หมด

แล้วก็เวลาที่ใช้คำสั่งพวกนี้โดยตรง ให้ใช้ฟังก์ชั่น $wpdb->prepare() ด้วย เพราะจะช่วย escape พวก quote ต่างๆ แล้วก็พวก type casting ได้ ทำให้ระบบมีความปลอดภัยมากขึ้น

$var = "dangerous'"; // ข้อมูลดิบ
$id = some_foo_number(); // ข้อมูลที่เราคาดว่าจะเป็น integer

$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_title = %s WHERE ID = %d", $var, $id ) );

ใช้ %s สำหรับสตริง และ %d สำหรับค่า integer สังเกตว่าในโค้ดไม่ได้ใส่ quote ครอบ %s เลย ฟังก์ชั่น $wpdb->prepare() จะทำให้เราเอง ข้อดีคือเราไม่จำเป็นต้องจำว่าตรงไหนเราต้องใส่ quote หรือไม่ใส่ แล้วอีกอย่างหนึ่งก็คือเราไม่ต้องใช้ esc_sql() เอง

การ Query ข้อมูลจากฐานข้อมูล

เราควรหลีกเลี่ยงการเขียนโค้ดเรียกข้อมูลจากฐานข้อมูลโดยตรง เราควรจะใช้ฟังก์ชั่นที่ WordPress มีอยู่ให้แล้ว จะทำให้โค้ดเรา compatible สูง และในหลายๆ กรณี ผลลัพธ์จากการใช้ฟังก์ชั่นของ WordPress จะถูก cache ไว้ด้วย ซึ่งจะทำให้ระบบเราเร็วกว่าเวลาเราติดต่อกับฐานข้อมูลโดยตรง

แต่ถ้าต้องการจะติดต่อกับฐานข้อมูลโดยตรงจริงๆ ให้ลองคุยกับคนใน wp-hackers mailing list ดู เผื่อว่าเค้าจะมีทางเลือกให้เรา หรือไม่ก็สร้างฟังก์ชั่นสำหรับ WordPress ในเวอร์ชั่นต่อๆ ไปให้ตรงกับความต้องการของเรา 🙂

การตั้งชื่อตัวแปร (Naming Conventions)

ใช้ lowercase กับตัวแปร และชื่อฟังก์ชั่น และแยกคำด้วย underscore ( _ ) ไม่ควรใช้ชื่อย่อโดยไม่จำเป็น เพราะจะทำให้โค้ดกำกวม และต้องเสียเวลามาจำว่าชื่อนี้ย่อมาจากอะไร

ส่วนชื่อคลาส ก็ให้ตัวอักษรตัวแรกเป็นตัวใหญ่ แล้วก็แยกคำด้วย underscore เช่นกัน แต่ถ้ามีชื่อย่อ ก็ให้เป็นตัวใหญ่ให้หมด เช่น

class Walker_Category extends Walker { [...] }
class WP_HTTP { [...] }

ส่วนชื่อไฟล์ก็ควรเป็นตัวเล็กหมด และแยกคำด้วย hyphen หรือ ขีดกลาง ( - ) สำหรับไฟล์คลาส ก็ให้นำหน้าด้วย class- ถ้าสมมุติเรามีคลาสชื่อ WP_Error ก็ให้ตั้งชื่อว่า

class-wp-error.php

ส่วนไฟล์ที่เป็น template ก็ควรต่อท้ายด้วย -template เช่น

general-template.php

Self-Explanatory Flag Values for Function Arguments

การใช้ค่า true หรือ false ตอนที่เรียกฟังก์ชั่น ไม่ควรทำแบบนี้

// ตัวอย่างที่ไม่ดี
function eat( $what, $slowly = true ) {
    ...
}
eat( 'mushrooms' );
eat( 'mushrooms', true ); // true หมายความว่าอะไร?
eat( 'dogfood'  , false ); // false หมายความว่าอะไร? แค่ตรงข้ามกับ true?

เนื่องจาก PHP ไม่ support ชื่อของ argument ค่าที่เป็น flag แบบ true หรือ false จะดูไม่สื่อความหมายเท่าไหร่ และเมื่อไหร่ก็ตามที่เรากลับมาอ่าน เราก็ต้องเปิดฟังก์ชั่นนั้นขึ้นมาหาว่า true คืออะไร false คืออะไรตลอด ดังนั้นเราควรจะทำให้โค้ดอ่านง่ายกว่านี้ ลองดูตัวอย่างข้างล่าง แทนที่จะใช้ true/false ก็ใช้สตริงแทน

// ตัวอย่างที่ดี
function eat( $what, $speed = 'slowly' ) {
    ...
}
eat( 'mushrooms' );
eat( 'mushrooms', 'slowly' );
eat( 'dogfood',   'quickly' );

Ternary Operator

ควรจะใช้เพื่อจะทดสอบ condition เป็นจริงหรือไม่เท่านั้น เนื่องจากโค้ดจะสั้น และก็อาจจะสับสนได้ง่าย ตอนที่กลับมาอ่าน

// (if statement is true) ? (do this) : (else, do this);
$musictype = ( 'jazz' == $music ) ? 'cool' : 'blah';

Yoda Conditions

if ( true == $the_force ) {
    $victorious = you_will( $be );
}

เวลาที่จะเทียบค่าใน if statement ควรจะเอาค่า constant ไว้ทางซ้ายเสมอ ป้องกันกรณีที่เราตกเครื่องหมาย = ไปตัวหนึ่ง ถ้าเราเผลอเขียนโค้ดตามนี้

if ( $the_force = true ) { ...

โค้ดนี้จะหมายถึงการ  assign ค่า true ให้ตัวแปร $the_force ทันที แต่ถ้าเราเอา true ขึ้นต้น แล้วเราตกเครื่องหมาย = ไป PHP จะแจ้งเราว่ามี parse error ดังนั้นการใช้ Yoda Conditions จะช่วยป้องกันเราไม่ให้เขียนโค้ดผิดจุดประสงค์

Clever Code

ข้อนี้ค่อนข้างสำคัญ เพราะว่าการเขียนให้โค้ดอ่านง่ายจะสำคัญกว่าโค้ดเทพ เยอะมาก ลองดูตัวอย่างโค้ดเทพข้างล่างนี้

isset( $var ) || $var = some_function();

แต่ว่าจะดีกว่าถ้าเราเขียนให้ง่ายๆ แบบนี้ ที่ได้ผลลัพธ์แบบเดียวกันเป๊ะ

if ( ! isset( $var ) )
    $var = some_function();

จบ.. แล้ว ยาวเชียว 😀

 

อ้างอิงจากต้นฉบับ WordPress PHP Coding Standards


Mils Burasakorn

Mils Burasakorn