ข้อดีอย่างหนึ่งในการเขียนการฟีเจอร์ต่างๆ แยกมาเป็นคลาสคลาสหนึ่ง คือเราสามารถเก็บค่าทุกค่า ฟังก์ชันทุกฟังก์ชัน เอาไว้แค่ในตัวมันได้โดยไม่ต้องกลัวว่ามันจะไปตีกับค่าเดียวกันหรือฟังก์ชันเดียวกัน (จริงๆ มันเรียกว่า property กับ method) ที่อยู่ในคลาสอื่น หรือที่เขาเรียกกันว่า encapsulation
ตัวอย่างการทำ encapsulation ที่เราเคยเขียนไว้ คือการใช้ ACF ทำบล็อก Gutenberg
แต่ข้อเสียอย่างหนึ่งของการทำ encapsulation นั่นคือเราต้องมานั่ง include ไฟล์คลาสจำนวนมาก (ยิ่งเขียนเยอะยิ่ง include เยอะ) ซึ่งถึงจุดหนึ่งแล้วมันจะกลายเป็นเรื่องน่ารำคาญขึ้นมาแทน แต่เราก็มีทางออก คือการทำ PSR-4 Autoloading
การทำ PSR-4 Autoloading จำเป็นต้องใช้ Composer เข้ามาช่วย ใครที่ใช้ Sage 9 อยู่แล้วก็ไปต่อได้เลย (เพราะมันใช้ composer อยู่แล้ว) ส่วนใครใช้ธีมอื่นที่ไม่ได้ใช้ composer ก็จัดการสั่ง composer init
ให้ธีมตัวเองให้เรียบร้อย เมื่อเสร็จแล้วจะได้ไฟล์ composer.json ขึ้นมาหนึ่งไฟล์
เพิ่ม namespace ให้คลาส สำหรับทำ Autoloading
คลาสที่จะนำมาทำ Autoloading ได้นั้นจำเป็นจำต้องอยู่ในเนมสเปซโดยเราสามารถประกาศ namespace ได้ด้วยการวางคีย์เวิร์ด namespace
เอาไว้ที่บรรทัดบนสุดในไฟล์
<?php namespace App\Widget; class News { ... }
การกำหนดเนมสเปซนั้น ปกติจะเริ่มด้วยชื่อ vendor (มักจะเป็นชื่อคนทำ หรือชื่อโปรเจ็กท์ ซึ่งจริงๆ มันใช้เป็นชื่อของอะไรก็ได้) และอาจจะตามด้วย subnamespace อีกชั้น (กี่ชั้นก็ได้) สำหรับใช้แยกส่วนคลาสต่างๆ เหมือนการเก็บไฟล์ไว้ในโฟลเดอร์ย่อย
ข้อดีของการกำหนดเนมสเปซคือเราสามารถใช้ชื่อคลาสซ้ำกันในแต่ละส่วนได้ เช่นเราอาจจะมีคลาส News
ที่อยู่ในเนมสเปซ App\Widget
และมีคลาส News
อีกคลาสหนึ่งที่อยู่ในเนมสเปซ App\Endpoint
ก็ได้ด้วยเช่นกัน
แต่ในทางกลับกัน เมื่อเราเพิ่มเนมสเปซให้คลาสแล้ว เมื่อใดก็ตามที่เราเรียกใช้คลาสใดๆ ภายในไฟล์นี้ PHP จะโหลดคลาสนั้นโดยหาจากเนมสเปซเดียวกัน เช่นในตัวอย่างข้างบน ถ้าเราเรียกใช้คลาส WP_Query
PHP ก็จะพยายามโหลดคลาส App\Widget\WP_Query
ขึ้นมาแทน และจะจบด้วยข้อผิดพลาด Class not found หน้าตาประมาณนี้

วิธีแก้คือให้เราเรียกคลาสนั้นๆ ขึ้นมาจาก root namespace โดยตรงโดยการเติมเครื่องหมาย \
เข้าไปข้างหน้า เช่น $query = new \WP_Query();
อีกวิธีหนึ่งที่นิยมกันคือการใช้คีย์เวิร์ด use
ในการประกาศใช้คลาสต่างๆ ในไฟล์นั้น โดยเราจะประกาศด้วย Fully Qualified Class Name (FQCN) ซึ่งประกอบด้วยชื่อเนมสเปซและชื่อคลาส เช่น App\Widget\News
หรือถ้าคลาสนั้นๆ ไม่ได้อยู่ในเนมสเปซใดๆ เราก็สามารถใช้ root namespace ด้วยการเติมเครื่องหมาย \
ลงไปได้เล่น เช่น \WP_Query
<?php namespace App\Widget; use \WP_Query; class News { public function __construct() { $data = new WP_Query(); } }
และคีย์เวิร์ด use
นี่ยังมีประโยชน์อีกอย่างหนึ่งคือมันสามารถใช้เปลี่ยนชื่อคลาสที่ซ้ำกันได้ด้วย เช่นถ้าจะเรียกใช้คลาส App\Widget\News
และคลาส App\Endpoint\News
เข้ามาพร้อมกัน ตามปกติมันจะจบลงด้วยข้อผิดพลาดชื่อคลาสซ้ำกัน แต่เราสามารถใช้ use ... as ...
ในการเปลี่ยนชื่อคลาสได้ เช่น
<?php namespace App; use App\Widget\News as NewsWidget; use App\Endpoint\News as NewsEndpoint; class Main { public function __construct() { $widget = new NewsWidget(); $endpoint = new NewsEndpoint(); } }
การตั้งชื่อไฟล์และไดเรคทอรี่
ปกติแล้วเมื่อเราเขียนโปรแกรมแบบ OOP เราจะแยกคลาสเอาไว้ 1 คลาสต่อ 1 ไฟล์เสมอ และตามมาตรฐาน PSR-4 เราจะต้องตั้งชื่อไฟล์และชื่อคลาสให้ตรงกันด้วย เช่นเรามีคลาสที่ชื่อว่า Main
เราก็ต้องตั้งชื่อไฟล์ว่า Main.php
(พิมพ์เล็กพิมพ์ใหญ่ต้องเหมือนกัน)
ในส่วนของโครงสร้างไดเรคทอรี่นั้น ตามหลักจริงๆ เราควรจะสร้าง subdirectory ให้ตรงกับ subnamespace ด้วย เช่นคลาส News
นั้นอยู่ในเนมสเปซ App\Widget
เราก็ควรจะเก็บไฟล์นี้เอาไว้ที่ /Widget/News.php
แต่จริงๆ แล้วเกณฑ์การตั้งชื่อไดเรคทอรี่นั้นค่อนข้างจะยืดหยุ่น เราสามารถตั้งชื่อไดเรคทอรี่ว่าอะไรก็ได้ แล้วเราค่อยไปแมพเอาในไฟล์ composer.json ในภายหลัง แต่มีข้อแม้คือคลาสที่อยู่ในเนมสเปซเดียวกัน ต้องอยู่ในไดเรคทอรี่เดียวกันด้วยเช่นกัน
เพิ่ม autoload ลงใน composer.json
หลังจากเราเตรียมไฟล์ต่างๆ เรียบร้อยแล้ว ขั้นตอนต่อไปคือการเพิ่ม autoload ลงในไฟล์ composer.json โดยให้เราเปิดไฟล์ composer.json ขึ้นมา กรณีที่เราแค่ init โปรเจ็กท์เปล่าๆ ไว้ ไฟล์เราจะมีหน้าตาประมาณนี้
{ "name": "wp63/test", "type": "project", "authors": [ { "name": "WP63", "email": "[email protected]" } ], "require": {} }
ให้เราเพิ่มคีย์ autoload.psr-4
ลงไปในไฟล์ตามนี้
{ "name": "wp63/test", "type": "project", "authors": [ { "name": "WP63", "email": "[email protected]" } ], "require": {}, "autoload": { "psr-4": { } } }
ภายในคีย์ psr-4 ให้เราทำการแมพเนมสเปซกับไดเรคทอรี่ได้เลย โดยฝั่งเนมสเปซจะแทนที่ \
ด้วย \\
และต้องมี \\
ปิดท้ายเนมสเปซด้วยเสมอ ส่วนฝั่งไดเรคทอรี่ก็จะอ้างพาทจากไฟล์ composer.json นี้ และต้องมี /
ปิดท้ายด้วย
เช่นเราเก็บไฟล์เนมสเปซ App\Widgets
เอาไว้ที่ app/Widgets
และเก็บเนมสเปซ App\Endpoints
เอาไว้ที่ app/API
เราจะสามารถแมพค่าได้ดังนี้
{ "name": "wp63/test", "type": "project", "authors": [ { "name": "WP63", "email": "[email protected]" } ], "require": {}, "autoload": { "psr-4": { "App\\Widgets\\": "app/Widgets", "App\\Endpoints\\": "app/API" } } }
จริงๆ นอกจากการแมพคลาสตาม PSR-4 แล้ว เรายังใช้ composer ในการ include ไฟล์เข้ามาได้ด้วย โดยเพิ่มไฟล์ที่ต้องการ include เข้าไปที่เข้าไปในอาเรย์คีย์ autoload.files
เช่นเราจะ include ไฟล์ app/helpers.php
เราสามารถทำได้ดังนี้
{ "name": "wp63/test", "type": "project", "authors": [ { "name": "WP63", "email": "[email protected]" } ], "require": {}, "autoload": { "files": ["app/helpers.php"], "psr-4": { "App\\Widgets\\": "app/Widgets", "App\\Endpoints\\": "app/API" } } }
เมื่อแก้ไขไฟล์เสร็จแล้ว ให้เราสั่งรัน composer install -o
หรือ composer dumpautoload -o
เพื่อให้ composer สร้างไฟล์ autoload ขึ้นมา
การสั่งด้วยพารามิเตอร์ -o นั้นจะเป็นการสั่งให้ composer สร้างไฟล์ autoload ขึ้นมา โดยแปลง autoload แบบ PSR-0 หรือ PSR-4 ให้เป็น class map ที่จะช่วยให้มีประสิทธิภาพดีขึ้น
เมื่อเราต้องการจะเอามาใช้ใน WordPres ก็ให้เปิดไฟล์ functions.php
ของธีมเราขึ้นมา แล้วเพิ่มคำสั่ง include ไฟล์ autoload เข้าไปดังนี้
<?php require __DIR__ . '/vendor/autoload.php';
ฟีเจอร์อื่นๆ เกี่ยวกับ composer ลองเข้าไปดูกันได้ที่ getcomposer.org
Leave a Reply