ลูปเหตุการณ์เครื่องปฏิกรณ์หลักของ ReactPHP ที่ไลบรารีสามารถใช้สำหรับเหตุการณ์ I/O
เวอร์ชันการพัฒนา: สาขานี้มีโค้ดสำหรับเวอร์ชัน v3 ที่กำลังจะมาถึง สำหรับโค้ดของเวอร์ชัน v1 ที่เสถียรในปัจจุบัน โปรดดูที่สาขา
1.x
การเปิดตัว v3 ที่กำลังจะมาถึงจะเป็นหนทางข้างหน้าสำหรับแพ็คเกจนี้ อย่างไรก็ตาม เราจะยังคงสนับสนุนเวอร์ชัน 1 สำหรับผู้ที่ยังไม่ได้เป็นเวอร์ชันล่าสุด ดูคำแนะนำในการติดตั้งสำหรับรายละเอียดเพิ่มเติม
เพื่อให้ไลบรารีที่ใช้อะซิงก์ทำงานร่วมกันได้ ไลบรารีเหล่านั้นจำเป็นต้องใช้ลูปเหตุการณ์เดียวกัน ส่วนประกอบนี้มี LoopInterface
ทั่วไปที่ไลบรารีใดๆ สามารถกำหนดเป้าหมายได้ สิ่งนี้ทำให้สามารถใช้พวกมันในลูปเดียวกันได้ด้วยการเรียก run()
เพียงครั้งเดียวที่ควบคุมโดยผู้ใช้
สารบัญ
ตัวอย่างการเริ่มต้นอย่างรวดเร็ว
การใช้งาน
วิ่ง()
หยุด()
เพิ่มตัวจับเวลา()
addPeriodicTimer()
ยกเลิกตัวจับเวลา()
ฟิวเจอร์ทิค()
เพิ่มสัญญาณ()
ลบสัญญาณ()
เพิ่มReadStream()
addWriteStream()
ลบReadStream()
ลบWriteStream()
สตรีมSelectLoop
ExtEventLoop
ExtEvLoop
ExtUvLoop
วิธีการวนซ้ำ
วนรอบการทำงานอัตโนมัติ
รับ()
วนซ้ำ
การใช้งานแบบวนซ้ำ
อินเทอร์เฟซแบบวนซ้ำ
ติดตั้ง
การทดสอบ
ใบอนุญาต
มากกว่า
นี่คือเซิร์ฟเวอร์ HTTP แบบอะซิงก์ที่สร้างขึ้นโดยมีเพียงลูปเหตุการณ์
<?phpuse ReactEventLoopLoop; ต้องการ __DIR__ '/vendor/autoload.php';$server = stream_socket_server('tcp://127.0.0.1:8080');stream_set_blocking($เซิร์ฟเวอร์, false); วนรอบ::addReadStream($server, function ($server) {$conn = stream_socket_accept($server);$data = "HTTP/1.1 200 OKrnContent-Length: 3rnrnHin"; วนรอบ::addWriteStream($conn, ฟังก์ชั่น ($conn) ใช้ (&$data) {$เขียน = fwrite($conn, $data);if ($เขียน === strlen($data)) {fclose($conn ); วนรอบ::removeWriteStream($conn); } อื่น ๆ {$data = substr($data, $write); - - - Loop::addPeriodicTimer(5, function () {$memory = memory_get_usage() / 1024;$formatted = number_format($memory, 3).'K';echo "การใช้หน่วยความจำปัจจุบัน: {$formatted}n"; -
ดูตัวอย่างด้วย
แอปพลิเคชันทั่วไปจะใช้คลาส Loop
เพื่อใช้ลูปเหตุการณ์เริ่มต้นดังนี้:
ใช้ ReactEventLoopLoop;$timer = Loop::addPeriodicTimer(0.1, function () {echo 'Tick' . PHP_EOL; - วนรอบ::addTimer(1.0, ฟังก์ชั่น () ใช้ ($timer) { วนซ้ำ::cancelTimer($timer);echo 'เสร็จสิ้น' PHP_EOL; -
อีกทางเลือกหนึ่ง คุณยังสามารถสร้างอินสแตนซ์ลูปเหตุการณ์อย่างชัดเจนตั้งแต่ตอนเริ่มต้น นำมาใช้ใหม่ตลอดทั้งโปรแกรมของคุณ และสุดท้ายให้รันอินสแตนซ์ดังกล่าวที่ส่วนท้ายของโปรแกรมในลักษณะนี้:
$loop = ReactEventLoopLoop::get();$timer = $loop->addPeriodicTimer(0.1, function () {echo 'Tick' . PHP_EOL; });$loop->addTimer(1.0, function () use ($loop, $timer) {$loop->cancelTimer($timer);echo 'Done' . PHP_EOL; });$ลูป->รัน();
แม้ว่าแบบแรกจะกระชับกว่า แต่แบบหลังจะชัดเจนกว่า ในทั้งสองกรณี โปรแกรมจะดำเนินการตามขั้นตอนเดียวกันทุกประการ
อินสแตนซ์ลูปเหตุการณ์ถูกสร้างขึ้นที่จุดเริ่มต้นของโปรแกรม สิ่งนี้จะทำโดยปริยายในครั้งแรกที่คุณเรียกใช้คลาส Loop
(หรือโดยการสร้างอินสแตนซ์ของการใช้งานลูปใด ๆ ด้วยตนเอง)
ลูปเหตุการณ์ถูกใช้โดยตรงหรือส่งผ่านเป็นอินสแตนซ์ไปยังไลบรารีและโค้ดแอปพลิเคชัน ในตัวอย่างนี้ ตัวจับเวลาแบบคาบจะถูกลงทะเบียนกับลูปเหตุการณ์ซึ่งจะส่งสัญญาณเอาท์พุต Tick
ทุกเศษส่วนของวินาที จนกระทั่งตัวจับเวลาอื่นหยุดตัวจับเวลาแบบคาบหลังจากหนึ่งวินาที
ลูปเหตุการณ์จะทำงานเมื่อสิ้นสุดโปรแกรม สิ่งนี้จะถูกทำโดยอัตโนมัติเมื่อใช้คลาส Loop
หรืออย่างชัดเจนด้วยการเรียก run()
เพียงครั้งเดียวที่ส่วนท้ายของโปรแกรม
ตั้งแต่ v1.2.0
เราขอแนะนำอย่างยิ่งให้ใช้คลาส Loop
คำแนะนำการวนซ้ำที่ชัดเจนยังคงใช้ได้และอาจยังคงมีประโยชน์ในบางแอปพลิเคชัน โดยเฉพาะในช่วงการเปลี่ยนผ่านไปสู่รูปแบบที่กระชับมากขึ้น
คลาส Loop
มีอยู่ในฐานะตัวเข้าถึงส่วนกลางที่สะดวกสำหรับลูปเหตุการณ์
คลาส Loop
จัดเตรียมวิธีการทั้งหมดที่มีอยู่ใน LoopInterface
เป็นวิธีการแบบคงที่:
วิ่ง()
หยุด()
เพิ่มตัวจับเวลา()
addPeriodicTimer()
ยกเลิกตัวจับเวลา()
ฟิวเจอร์ทิค()
เพิ่มสัญญาณ()
ลบสัญญาณ()
เพิ่มReadStream()
addWriteStream()
ลบReadStream()
ลบWriteStream()
หากคุณกำลังทำงานกับ event loop ในโค้ดแอปพลิเคชันของคุณ มักจะเป็นวิธีที่ง่ายที่สุดในการเชื่อมต่อกับวิธีคงที่ที่กำหนดไว้ในคลาส Loop
เช่นนี้
ใช้ ReactEventLoopLoop;$timer = Loop::addPeriodicTimer(0.1, function () {echo 'Tick' . PHP_EOL; - วนรอบ::addTimer(1.0, ฟังก์ชั่น () ใช้ ($timer) { วนซ้ำ::cancelTimer($timer);echo 'เสร็จสิ้น' PHP_EOL; -
ในทางกลับกัน หากคุณคุ้นเคยกับการเขียนโปรแกรมเชิงวัตถุ (OOP) และการฉีดพึ่งพา (DI) คุณอาจต้องการฉีดอินสแตนซ์ลูปเหตุการณ์และเรียกใช้วิธีการอินสแตนซ์บน LoopInterface
ดังนี้:
ใช้ ReactEventLoopLoop; ใช้ ReactEventLoopLoopInterface; คลาส Greeter {ไพรเวท $loop; ฟังก์ชั่นสาธารณะ __ สร้าง (LoopInterface $loop) {$นี่->ห่วง = $ห่วง; } ทักทายฟังก์ชันสาธารณะ (สตริง $name) {$this->loop->addTimer(1.0, function () use ($name) {echo 'Hello ' . $name . '!' . PHP_EOL; - - }$greeter = new Greeter(Loop::get());$greeter->greet('Alice');$greeter->greet('Bob');
การเรียกเมธอดแบบคงที่แต่ละครั้งจะถูกส่งต่อตามที่เป็นไปยังอินสแตนซ์ลูปเหตุการณ์ที่ซ่อนอยู่โดยใช้การเรียก Loop::get()
ภายใน ดู LoopInterface
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการที่ใช้ได้
เมื่อใช้คลาส Loop
มันจะทำการวนซ้ำเมื่อสิ้นสุดโปรแกรมโดยอัตโนมัติ ซึ่งหมายความว่าตัวอย่างต่อไปนี้จะกำหนดเวลาตัวจับเวลาและจะดำเนินการโปรแกรมโดยอัตโนมัติจนกว่าเหตุการณ์ตัวจับเวลาจะเริ่มทำงาน:
ใช้ ReactEventLoopLoop; วนรอบ::addTimer(1.0, ฟังก์ชั่น () {echo 'Hello' . PHP_EOL; -
ตั้งแต่ v1.2.0
เราขอแนะนำอย่างยิ่งให้ใช้คลาส Loop
ด้วยวิธีนี้และละเว้นการเรียกใช้ run()
ที่ชัดเจน ด้วยเหตุผล BC วิธี run()
ที่ชัดเจนยังคงใช้ได้และอาจยังคงมีประโยชน์ในบางแอปพลิเคชัน โดยเฉพาะอย่างยิ่งในช่วงการเปลี่ยนแปลงไปสู่รูปแบบที่กระชับยิ่งขึ้น
หากคุณไม่ต้องการให้ Loop
ทำงานโดยอัตโนมัติ คุณสามารถ run()
หรือ stop()
ได้อย่างชัดเจน สิ่งนี้มีประโยชน์หากคุณใช้ตัวจัดการข้อยกเว้นส่วนกลางเช่นนี้
ใช้ ReactEventLoopLoop; วนรอบ::addTimer(10.0, function () {echo 'ไม่เคยเกิดขึ้น'; });set_Exception_handler(function (Throwable $e) {echo 'Error: ' . $e->getMessage() . PHP_EOL; วนซ้ำ::หยุด(); }); โยน RuntimeException ใหม่ ('สาธิต');
get(): LoopInterface
เพื่อรับอินสแตนซ์ลูปเหตุการณ์ที่ใช้งานอยู่ในปัจจุบัน
วิธีนี้จะส่งคืนอินสแตนซ์ลูปเหตุการณ์เดียวกันตลอดอายุการใช้งานแอปพลิเคชันของคุณ
ใช้ ReactEventLoopLoop; ใช้ ReactEventLoopLoopInterface;$loop = Loop::get();assert($loop instance of LoopInterface);assert($loop === Loop::get());
สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณใช้การเขียนโปรแกรมเชิงวัตถุ (OOP) และการฉีดการพึ่งพา (DI) ในกรณีนี้ คุณอาจต้องการฉีดอินสแตนซ์ลูปเหตุการณ์และเรียกใช้วิธีการอินสแตนซ์บน LoopInterface
ดังนี้:
ใช้ ReactEventLoopLoop; ใช้ ReactEventLoopLoopInterface; คลาส Greeter {ไพรเวท $loop; ฟังก์ชั่นสาธารณะ __ สร้าง (LoopInterface $loop) {$นี่->ห่วง = $ห่วง; } ทักทายฟังก์ชันสาธารณะ (สตริง $name) {$this->loop->addTimer(1.0, function () use ($name) {echo 'Hello ' . $name . '!' . PHP_EOL; - - }$greeter = new Greeter(Loop::get());$greeter->greet('Alice');$greeter->greet('Bob');
ดู LoopInterface
สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการที่ใช้ได้
นอกจาก LoopInterface
แล้ว ยังมีการใช้งาน event loop อีกหลายรายการ
ลูปเหตุการณ์ทั้งหมดรองรับคุณสมบัติเหล่านี้:
การโพลตัวอธิบายไฟล์
ตัวจับเวลาแบบครั้งเดียว
ตัวจับเวลาเป็นระยะ
การดำเนินการที่เลื่อนออกไปในเครื่องหมายวนซ้ำในอนาคต
สำหรับผู้บริโภคส่วนใหญ่ของแพ็คเกจนี้ การใช้งาน event loop พื้นฐานคือรายละเอียดการใช้งาน คุณควรใช้คลาส Loop
เพื่อสร้างอินสแตนซ์ใหม่โดยอัตโนมัติ
ขั้นสูง! หากคุณต้องการใช้งาน Event Loop อย่างชัดเจน คุณสามารถยกตัวอย่างคลาสใดคลาสหนึ่งต่อไปนี้ด้วยตนเองได้ โปรดทราบว่าคุณอาจต้องติดตั้งส่วนขยาย PHP ที่จำเป็นสำหรับการใช้งานลูปเหตุการณ์ที่เกี่ยวข้องก่อน มิฉะนั้นพวกเขาจะส่ง BadMethodCallException
ในการสร้าง
ลูปเหตุการณ์ตาม stream_select()
สิ่งนี้ใช้ฟังก์ชัน stream_select()
และเป็นการใช้งานเดียวที่ใช้ได้กับ PHP ทันที
Event Loop นี้ใช้งานได้ทันทีกับ PHP เวอร์ชันใดก็ได้ ซึ่งหมายความว่าไม่จำเป็นต้องติดตั้ง และไลบรารีนี้ทำงานได้บนทุกแพลตฟอร์มและเวอร์ชัน PHP ที่รองรับ ดังนั้นคลาส Loop
จะใช้การวนซ้ำเหตุการณ์นี้ตามค่าเริ่มต้น หากคุณไม่ได้ติดตั้งส่วนขยายการวนซ้ำเหตุการณ์ใด ๆ ที่แสดงด้านล่าง
ภายใต้ประทุน มันจะทำการเรียกระบบ select
อย่างง่าย การเรียกระบบนี้จำกัดอยู่ที่หมายเลขตัวอธิบายไฟล์สูงสุดที่ FD_SETSIZE
(ขึ้นอยู่กับแพลตฟอร์ม โดยทั่วไปคือ 1024) และมาตราส่วนด้วย O(m)
( m
คือหมายเลขตัวอธิบายไฟล์สูงสุดที่ส่งผ่าน) ซึ่งหมายความว่าคุณอาจประสบปัญหาเมื่อจัดการสตรีมหลายพันสตรีมพร้อมกัน และคุณอาจต้องการพิจารณาใช้การใช้งานลูปเหตุการณ์ทางเลือกรายการใดรายการหนึ่งด้านล่างในกรณีนี้ หากกรณีการใช้งานของคุณเป็นหนึ่งในกรณีการใช้งานทั่วไปจำนวนมากที่เกี่ยวข้องกับการจัดการสตรีมเพียงไม่กี่สิบหรือสองสามร้อยรายการในคราวเดียว การใช้งานลูปเหตุการณ์นี้จะทำงานได้ดีมาก
หากคุณต้องการใช้การจัดการสัญญาณ (ดู addSignal()
ด้านล่าง) การใช้งานลูปเหตุการณ์นี้ต้องใช้ ext-pcntl
ส่วนขยายนี้ใช้ได้เฉพาะกับแพลตฟอร์มที่คล้าย Unix เท่านั้นและไม่รองรับ Windows โดยทั่วไปจะมีการติดตั้งเป็นส่วนหนึ่งของการแจกแจง PHP จำนวนมาก หากส่วนขยายนี้หายไป (หรือคุณใช้งานบน Windows) ระบบจะไม่รองรับการจัดการสัญญาณและจะส่ง BadMethodCallException
แทน
เป็นที่รู้กันว่าการวนซ้ำเหตุการณ์นี้ต้องอาศัยเวลานาฬิกาแขวนเพื่อกำหนดเวลาตัวจับเวลาในอนาคตเมื่อใช้เวอร์ชันใดๆ ก่อน PHP 7.3 เนื่องจากแหล่งเวลาแบบโมโนโทนิกมีให้บริการเฉพาะใน PHP 7.3 ( hrtime()
) แม้ว่าสิ่งนี้จะไม่ส่งผลกระทบต่อกรณีการใช้งานทั่วไปจำนวนมาก แต่นี่เป็นความแตกต่างที่สำคัญสำหรับโปรแกรมที่ต้องอาศัยความแม่นยำของเวลาสูง หรือบนระบบที่มีการปรับเปลี่ยนเวลาไม่ต่อเนื่อง (การข้ามเวลา) ซึ่งหมายความว่า หากคุณตั้งเวลาให้ทริกเกอร์ใน 30 วินาทีบน PHP < 7.3 แล้วปรับเวลาของระบบไปข้างหน้า 20 วินาที ตัวจับเวลาอาจทริกเกอร์ใน 10 วินาที ดูเพิ่มเติมที่ addTimer()
สำหรับรายละเอียดเพิ่มเติม
ลูปเหตุการณ์ตาม ext-event
สิ่งนี้ใช้ส่วนขยาย PECL event
ซึ่งจัดเตรียมอินเทอร์เฟซให้กับไลบรารี libevent
libevent
รองรับแบ็กเอนด์เฉพาะระบบจำนวนหนึ่ง (epoll, kqueue)
เป็นที่ทราบกันว่าลูปนี้ใช้งานได้กับ PHP 7.1 ถึง PHP 8+
ลูปเหตุการณ์ตาม ext-ev
ลูปนี้ใช้ส่วนขยาย ev
PECL ซึ่งจัดเตรียมอินเทอร์เฟซให้กับไลบรารี libev
libev
เองรองรับแบ็กเอนด์เฉพาะระบบจำนวนหนึ่ง (epoll, kqueue)
เป็นที่ทราบกันว่าลูปนี้ใช้งานได้กับ PHP 7.1 ถึง PHP 8+
ลูปเหตุการณ์ตาม ext-uv
ลูปนี้ใช้ส่วนขยาย uv
PECL ซึ่งจัดเตรียมอินเทอร์เฟซให้กับไลบรารี libuv
libuv
เองรองรับแบ็กเอนด์เฉพาะระบบจำนวนหนึ่ง (epoll, kqueue)
เป็นที่ทราบกันว่าลูปนี้ใช้งานได้กับ PHP 7.1 ถึง PHP 8+
สามารถใช้เมธอด run(): void
เพื่อรันลูปเหตุการณ์ได้จนกว่าจะไม่มีงานที่ต้องทำอีกต่อไป
สำหรับแอปพลิเคชันจำนวนมาก วิธีการนี้เป็นเพียงการเรียกใช้ที่มองเห็นได้โดยตรงบนลูปเหตุการณ์เท่านั้น ตามหลักทั่วไปแล้ว ขอแนะนำให้แนบทุกอย่างเข้ากับอินสแตนซ์ลูปเดียวกัน จากนั้นเรียกใช้ลูปหนึ่งครั้งที่ด้านล่างสุดของแอปพลิเคชัน
$ห่วง->วิ่ง();
วิธีนี้จะให้ลูปทำงานต่อไปจนกว่าจะไม่มีงานให้ทำอีกต่อไป กล่าวอีกนัยหนึ่ง: วิธีนี้จะบล็อกจนกว่าตัวจับเวลา สตรีม และ/หรือสัญญาณสุดท้ายจะถูกลบออก
ในทำนองเดียวกัน ก็จำเป็นที่จะต้องแน่ใจว่าแอปพลิเคชันเรียกใช้วิธีนี้จริง ๆ หนึ่งครั้ง การเพิ่ม Listener ในลูปและขาดหายไปในการรันจริงจะส่งผลให้แอปพลิเคชันออกโดยไม่รอ Listener ใดๆ ที่แนบมาจริงๆ
จะต้องไม่ถูกเรียกใช้เมธอดนี้ในขณะที่ลูปกำลังทำงานอยู่ เมธอดนี้อาจถูกเรียกมากกว่าหนึ่งครั้งหลังจากที่ stop()
ped อย่างชัดเจน หรือหลังจากที่หยุดโดยอัตโนมัติเนื่องจากก่อนหน้านี้ไม่มีอะไรให้ทำอีกต่อไป
เมธอด stop(): void
สามารถใช้เพื่อสั่งให้ลูปเหตุการณ์ที่กำลังรันอยู่หยุดทำงาน
วิธีนี้ถือเป็นการใช้งานขั้นสูงและควรใช้ด้วยความระมัดระวัง ตามหลักทั่วไปแล้ว แนะนำให้ปล่อยให้การวนซ้ำหยุดโดยอัตโนมัติเมื่อไม่มีอะไรให้ทำอีกต่อไปเท่านั้น
วิธีการนี้สามารถใช้เพื่อสั่งการวนซ้ำเหตุการณ์ให้หยุดอย่างชัดเจน:
$loop->addTimer(3.0, ฟังก์ชั่น () ใช้ ($loop) {$loop->stop(); -
การเรียกใช้เมธอดนี้บนอินสแตนซ์ลูปที่ไม่ทำงานในปัจจุบันหรือบนอินสแตนซ์ลูปที่ถูกหยุดแล้วจะไม่มีผลใดๆ
addTimer(float $interval, callable $callback): TimerInterface
สามารถใช้เพื่อจัดคิวการโทรกลับที่จะเรียกใช้หนึ่งครั้งหลังจากช่วงเวลาที่กำหนด
พารามิเตอร์ที่สองต้องเป็นฟังก์ชันโทรกลับตัวจับเวลาที่ยอมรับอินสแตนซ์ตัวจับเวลาเป็นพารามิเตอร์เดียว หากคุณไม่ได้ใช้อินสแตนซ์ตัวจับเวลาภายในฟังก์ชันโทรกลับตัวจับเวลา คุณอาจใช้ฟังก์ชันที่ไม่มีพารามิเตอร์เลย
ฟังก์ชั่นโทรกลับตัวจับเวลาจะต้องไม่โยน Exception
ค่าที่ส่งคืนของฟังก์ชันเรียกกลับของตัวจับเวลาจะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ ขอแนะนำว่าอย่าส่งคืนโครงสร้างข้อมูลที่มากเกินไป
วิธีการนี้จะส่งคืนอินสแตนซ์ตัวจับเวลา อินสแตนซ์ตัวจับเวลาเดียวกันจะถูกส่งผ่านไปยังฟังก์ชันเรียกกลับตัวจับเวลาตามที่อธิบายไว้ข้างต้น คุณสามารถเรียกใช้ cancelTimer
เพื่อยกเลิกตัวจับเวลาที่ค้างอยู่ ซึ่งแตกต่างจาก addPeriodicTimer()
วิธีการนี้จะให้แน่ใจว่าการโทรกลับจะถูกเรียกใช้เพียงครั้งเดียวหลังจากช่วงเวลาที่กำหนด
$loop->addTimer(0.8, function () {echo 'world!' . PHP_EOL; });$loop->addTimer(0.3, function () {echo 'hello '; -
ดูตัวอย่าง #1 ด้วย
หากคุณต้องการเข้าถึงตัวแปรใดๆ ภายในฟังก์ชันการโทรกลับ คุณสามารถผูกข้อมูลที่ต้องการเข้ากับการปิดการโทรกลับได้ดังนี้:
ฟังก์ชั่นสวัสดี($ชื่อ, LoopInterface $loop) {$loop->addTimer(1.0, function () ใช้ ($name) {echo "hello $namen"; - }สวัสดี('ผู้ทดสอบ', $ลูป);
อินเทอร์เฟซนี้ไม่บังคับใช้ความละเอียดของตัวจับเวลาใดๆ เป็นพิเศษ ดังนั้นอาจต้องใช้ความระมัดระวังเป็นพิเศษหากคุณพึ่งพาความแม่นยำสูงมากโดยมีความแม่นยำระดับมิลลิวินาทีหรือต่ำกว่า การใช้งาน Event Loop ควรทำงานบนพื้นฐานความพยายามอย่างดีที่สุด และควรให้ความแม่นยำอย่างน้อยมิลลิวินาที เว้นแต่จะระบุไว้เป็นอย่างอื่น เป็นที่ทราบกันว่าการใช้งาน Event Loop ที่มีอยู่จำนวนมากนั้นมีความแม่นยำระดับไมโครวินาที แต่โดยทั่วไปแล้วไม่แนะนำให้ใช้ความแม่นยำสูงนี้
ในทำนองเดียวกัน ไม่รับประกันลำดับการดำเนินการของตัวจับเวลาที่กำหนดให้ดำเนินการในเวลาเดียวกัน (ภายในความแม่นยำที่เป็นไปได้)
อินเทอร์เฟซนี้แนะนำว่าการใช้งานลูปเหตุการณ์ควรใช้แหล่งเวลาแบบโมโนโทนิก หากมี เนื่องจากแหล่งที่มาของเวลาแบบโมโนโทนิกมีให้บริการตั้งแต่ PHP 7.3 ตามค่าเริ่มต้นเท่านั้น การใช้งานลูปเหตุการณ์อาจถอยกลับไปเป็นการใช้เวลาแบบนาฬิกาแขวน แม้ว่าสิ่งนี้จะไม่ส่งผลกระทบต่อกรณีการใช้งานทั่วไปจำนวนมาก แต่นี่เป็นความแตกต่างที่สำคัญสำหรับโปรแกรมที่ต้องอาศัยความแม่นยำของเวลาสูง หรือบนระบบที่มีการปรับเปลี่ยนเวลาไม่ต่อเนื่อง (การข้ามเวลา) ซึ่งหมายความว่า หากคุณกำหนดเวลาให้ตัวจับเวลาเริ่มทำงานใน 30 วินาที แล้วปรับเวลาของระบบไปข้างหน้า 20 วินาที ตัวจับเวลาควรจะทริกเกอร์ใน 30 วินาที ดูเพิ่มเติมที่การใช้งาน event loop สำหรับรายละเอียดเพิ่มเติม
addPeriodicTimer(float $interval, callable $callback): TimerInterface
สามารถใช้เพื่อจัดคิวการโทรกลับเพื่อเรียกใช้ซ้ำๆ หลังจากช่วงเวลาที่กำหนด
พารามิเตอร์ที่สองต้องเป็นฟังก์ชันโทรกลับตัวจับเวลาที่ยอมรับอินสแตนซ์ตัวจับเวลาเป็นพารามิเตอร์เดียว หากคุณไม่ได้ใช้อินสแตนซ์ตัวจับเวลาภายในฟังก์ชันโทรกลับตัวจับเวลา คุณอาจใช้ฟังก์ชันที่ไม่มีพารามิเตอร์เลย
ฟังก์ชั่นโทรกลับตัวจับเวลาจะต้องไม่โยน Exception
ค่าที่ส่งคืนของฟังก์ชันเรียกกลับของตัวจับเวลาจะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ ขอแนะนำว่าอย่าส่งคืนโครงสร้างข้อมูลที่มากเกินไป
วิธีการนี้จะส่งคืนอินสแตนซ์ตัวจับเวลา อินสแตนซ์ตัวจับเวลาเดียวกันจะถูกส่งผ่านไปยังฟังก์ชันเรียกกลับตัวจับเวลาตามที่อธิบายไว้ข้างต้น ซึ่งแตกต่างจาก addTimer()
วิธีการนี้จะให้แน่ใจว่าการโทรกลับจะถูกเรียกใช้อย่างไม่สิ้นสุดหลังจากช่วงเวลาที่กำหนดหรือจนกว่าคุณจะเรียกใช้ cancelTimer
$timer = $loop->addPeriodicTimer(0.1, function () {echo 'tick!' . PHP_EOL; });$loop->addTimer(1.0, function () use ($loop, $timer) {$loop->cancelTimer($timer);echo 'Done' . PHP_EOL; -
ดูตัวอย่าง #2 ด้วย
หากคุณต้องการจำกัดจำนวนการดำเนินการ คุณสามารถผูกข้อมูลที่กำหนดเองเข้ากับการปิดการโทรกลับได้ดังนี้:
ฟังก์ชั่นสวัสดี($ชื่อ, LoopInterface $loop) {$n = 3;$loop->addPeriodicTimer(1.0, ฟังก์ชั่น ($timer) ใช้ ($name, $loop, &$n) {if ($n > 0) { --$n;echo "สวัสดี $namen"; } อื่น ๆ {$loop->cancelTimer($timer); - - }สวัสดี('ผู้ทดสอบ', $ลูป);
อินเทอร์เฟซนี้ไม่บังคับใช้ความละเอียดของตัวจับเวลาใดๆ เป็นพิเศษ ดังนั้นอาจต้องใช้ความระมัดระวังเป็นพิเศษหากคุณพึ่งพาความแม่นยำสูงมากโดยมีความแม่นยำระดับมิลลิวินาทีหรือต่ำกว่า การใช้งาน Event Loop ควรทำงานบนพื้นฐานความพยายามอย่างดีที่สุด และควรให้ความแม่นยำอย่างน้อยมิลลิวินาที เว้นแต่จะระบุไว้เป็นอย่างอื่น เป็นที่ทราบกันว่าการใช้งาน Event Loop ที่มีอยู่จำนวนมากนั้นมีความแม่นยำระดับไมโครวินาที แต่โดยทั่วไปแล้วไม่แนะนำให้ใช้ความแม่นยำสูงนี้
ในทำนองเดียวกัน ไม่รับประกันลำดับการดำเนินการของตัวจับเวลาที่กำหนดให้ดำเนินการในเวลาเดียวกัน (ภายในความแม่นยำที่เป็นไปได้)
อินเทอร์เฟซนี้แนะนำว่าการใช้งานลูปเหตุการณ์ควรใช้แหล่งเวลาแบบโมโนโทนิก หากมี เนื่องจากแหล่งที่มาของเวลาแบบโมโนโทนิกมีให้บริการตั้งแต่ PHP 7.3 ตามค่าเริ่มต้นเท่านั้น การใช้งานลูปเหตุการณ์อาจถอยกลับไปเป็นการใช้เวลาแบบนาฬิกาแขวน แม้ว่าสิ่งนี้จะไม่ส่งผลกระทบต่อกรณีการใช้งานทั่วไปจำนวนมาก แต่นี่เป็นความแตกต่างที่สำคัญสำหรับโปรแกรมที่ต้องอาศัยความแม่นยำของเวลาสูง หรือบนระบบที่มีการปรับเปลี่ยนเวลาไม่ต่อเนื่อง (การข้ามเวลา) ซึ่งหมายความว่า หากคุณกำหนดเวลาให้ตัวจับเวลาเริ่มทำงานใน 30 วินาที แล้วปรับเวลาของระบบไปข้างหน้า 20 วินาที ตัวจับเวลาควรจะทริกเกอร์ใน 30 วินาที ดูเพิ่มเติมที่การใช้งาน event loop สำหรับรายละเอียดเพิ่มเติม
นอกจากนี้ ตัวจับเวลาเป็นระยะอาจเลื่อนไปเนื่องจากตัวจับเวลาถูกจัดกำหนดการใหม่หลังจากการเรียกใช้แต่ละครั้ง ด้วยเหตุนี้ โดยทั่วไปจึงไม่แนะนำให้ใช้สิ่งนี้สำหรับช่วงเวลาความแม่นยำสูงที่มีความแม่นยำระดับมิลลิวินาทีหรือต่ำกว่า
cancelTimer(TimerInterface $timer): void
สามารถใช้เพื่อยกเลิกตัวจับเวลาที่ค้างอยู่
ดูเพิ่มเติมที่ addPeriodicTimer()
และตัวอย่าง #2
การเรียกใช้เมธอดนี้บนอินสแตนซ์ตัวจับเวลาที่ไม่ได้ถูกเพิ่มเข้ากับอินสแตนซ์ลูปนี้หรือบนตัวจับเวลาที่ถูกยกเลิกไปแล้วจะไม่มีผลใด ๆ
futureTick(callable $listener): void
สามารถใช้เพื่อกำหนดเวลาการโทรกลับที่จะเรียกใช้ในขีดในอนาคตของลูปเหตุการณ์
วิธีนี้ทำงานคล้ายกับตัวจับเวลาที่มีช่วงเวลาเป็นศูนย์วินาทีมาก แต่ไม่ต้องการค่าใช้จ่ายในการจัดกำหนดการคิวตัวจับเวลา
ฟังก์ชันการเรียกกลับแบบติ๊กต้องสามารถยอมรับพารามิเตอร์เป็นศูนย์ได้
ฟังก์ชันการเรียกกลับแบบติ๊กต้องไม่ทำให้เกิด Exception
ค่าที่ส่งคืนของฟังก์ชันการเรียกกลับแบบขีดจะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ จึงไม่แนะนำให้ส่งคืนโครงสร้างข้อมูลที่มากเกินไป
หากคุณต้องการเข้าถึงตัวแปรใดๆ ภายในฟังก์ชันการโทรกลับ คุณสามารถผูกข้อมูลที่ต้องการเข้ากับการปิดการโทรกลับได้ดังนี้:
ฟังก์ชั่นสวัสดี($ชื่อ, LoopInterface $loop) {$loop->futureTick(function () ใช้ ($name) {echo "hello $namen"; - }สวัสดี('ผู้ทดสอบ', $ลูป);
ต่างจากตัวจับเวลา การโทรกลับแบบติ๊กรับประกันว่าจะดำเนินการตามลำดับที่ถูกจัดคิว นอกจากนี้ เมื่ออยู่ในคิวการโทรกลับแล้ว จะไม่มีทางยกเลิกการดำเนินการนี้ได้
ซึ่งมักใช้เพื่อแบ่งงานใหญ่ๆ ออกเป็นขั้นตอนเล็กๆ (รูปแบบหนึ่งของการทำงานร่วมกันหลายอย่างพร้อมกัน)
$loop->futureTick(ฟังก์ชั่น () {echo 'b'; });$loop->futureTick(function () {echo 'c'; });เอคโค่ 'a';
ดูตัวอย่าง #3 ด้วย
addSignal(int $signal, callable $listener): void
สามารถใช้เพื่อลงทะเบียน Listener เพื่อรับการแจ้งเตือนเมื่อสัญญาณถูกจับโดยกระบวนการนี้
สิ่งนี้มีประโยชน์ในการจับสัญญาณขัดจังหวะของผู้ใช้หรือสัญญาณการปิดเครื่องจากเครื่องมือเช่น supervisor
หรือ systemd
พารามิเตอร์ตัวที่สองต้องเป็นฟังก์ชันเรียกกลับของผู้ฟังที่ยอมรับสัญญาณเป็นพารามิเตอร์เดียว หากคุณไม่ได้ใช้สัญญาณภายในฟังก์ชันโทรกลับของผู้ฟัง คุณอาจใช้ฟังก์ชันที่ไม่มีพารามิเตอร์เลย
ฟังก์ชั่นการเรียกกลับของผู้ฟังจะต้องไม่ส่ง Exception
ค่าที่ส่งคืนของฟังก์ชันเรียกกลับ Listener จะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ ขอแนะนำว่าอย่าส่งคืนโครงสร้างข้อมูลที่มากเกินไป
$loop->addSignal(SIGINT, function (int $signal) {echo 'สัญญาณขัดจังหวะผู้ใช้ที่ตรวจพบ' . PHP_EOL; -
ดูตัวอย่าง #4 ด้วย
การส่งสัญญาณใช้ได้เฉพาะบนแพลตฟอร์มที่คล้าย Unix เท่านั้น ไม่รองรับ Windows เนื่องจากข้อจำกัดของระบบปฏิบัติการ วิธีนี้อาจส่ง BadMethodCallException
หากสัญญาณไม่ได้รับการสนับสนุนบนแพลตฟอร์มนี้ เช่น เมื่อส่วนขยายที่จำเป็นหายไป
หมายเหตุ: สามารถเพิ่มผู้ฟังได้เพียงครั้งเดียวในสัญญาณเดียวกัน ความพยายามที่จะเพิ่มมากกว่าหนึ่งครั้งจะถูกละเว้น
สามารถใช้เมธอด removeSignal(int $signal, callable $listener): void
เพื่อลบ Listener สัญญาณที่เพิ่มไว้ก่อนหน้านี้
$loop->removeSignal(SIGINT, $listener);
ความพยายามใดๆ ที่จะลบ Listener ที่ไม่ได้ลงทะเบียนจะถูกละเว้น
ขั้นสูง! โปรดทราบว่า API ระดับต่ำนี้ถือเป็นการใช้งานขั้นสูง กรณีการใช้งานส่วนใหญ่น่าจะใช้ Stream API ในระดับที่สูงกว่าที่อ่านได้แทน
สามารถใช้เมธอด addReadStream(resource $stream, callable $callback): void
เพื่อลงทะเบียน Listener เพื่อรับการแจ้งเตือนเมื่อสตรีมพร้อมที่จะอ่าน
พารามิเตอร์แรกต้องเป็นทรัพยากรสตรีมที่ถูกต้องซึ่งรองรับการตรวจสอบว่าพร้อมที่จะอ่านโดยการนำลูปนี้ไปใช้หรือไม่ ต้องไม่เพิ่มทรัพยากรสตรีมรายการเดียวมากกว่าหนึ่งครั้ง ให้เรียก removeReadStream()
ก่อนหรือตอบสนองต่อเหตุการณ์นี้ด้วยผู้ฟังคนเดียว จากนั้นจึงส่งจากผู้ฟังรายนี้ วิธีนี้อาจส่ง Exception
หากการใช้งานลูปนี้ไม่รองรับประเภททรัพยากรที่กำหนด
พารามิเตอร์ตัวที่สองต้องเป็นฟังก์ชันเรียกกลับ Listener ที่ยอมรับทรัพยากรสตรีมเป็นพารามิเตอร์เดียว หากคุณไม่ได้ใช้ทรัพยากรสตรีมภายในฟังก์ชันเรียกกลับ Listener คุณอาจใช้ฟังก์ชันที่ไม่มีพารามิเตอร์เลย
ฟังก์ชั่นการเรียกกลับของผู้ฟังจะต้องไม่ส่ง Exception
ค่าที่ส่งคืนของฟังก์ชันเรียกกลับ Listener จะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ ขอแนะนำว่าอย่าส่งคืนโครงสร้างข้อมูลที่มากเกินไป
หากคุณต้องการเข้าถึงตัวแปรใดๆ ภายในฟังก์ชันการโทรกลับ คุณสามารถผูกข้อมูลที่ต้องการเข้ากับการปิดการโทรกลับได้ดังนี้:
$loop->addReadStream($stream, function ($stream) use ($name) {echo $name . ' กล่าวว่า: ' . fread($stream); -
ดูตัวอย่าง #11 ด้วย
คุณสามารถเรียกใช้ removeReadStream()
เพื่อลบ Listener เหตุการณ์การอ่านสำหรับสตรีมนี้
ไม่รับประกันลำดับการดำเนินการของผู้ฟังเมื่อสตรีมหลายรายการพร้อมพร้อมกัน
เป็นที่ทราบกันว่าการใช้งานลูปเหตุการณ์บางอย่างจะทริกเกอร์ผู้ฟังเฉพาะเมื่อสตรีม สามารถ อ่านได้ (ทริกเกอร์ที่ขอบ) และอาจไม่ทริกเกอร์หากสตรีมสามารถอ่านได้ตั้งแต่เริ่มต้นแล้ว นอกจากนี้ยังบอกเป็นนัยว่าสตรีมอาจไม่ได้รับการยอมรับว่าสามารถอ่านได้เมื่อข้อมูลยังคงอยู่ในบัฟเฟอร์สตรีมภายในของ PHP ด้วยเหตุนี้ ขอแนะนำให้ใช้ stream_set_read_buffer($stream, 0);
เพื่อปิดการใช้งานบัฟเฟอร์การอ่านภายในของ PHP ในกรณีนี้
ขั้นสูง! โปรดทราบว่า API ระดับต่ำนี้ถือเป็นการใช้งานขั้นสูง กรณีการใช้งานส่วนใหญ่น่าจะใช้ Stream API ที่เขียนได้ระดับสูงกว่าแทน
สามารถใช้เมธอด addWriteStream(resource $stream, callable $callback): void
เพื่อลงทะเบียน Listener เพื่อรับการแจ้งเตือนเมื่อสตรีมพร้อมที่จะเขียน
พารามิเตอร์แรกต้องเป็นทรัพยากรสตรีมที่ถูกต้องซึ่งรองรับการตรวจสอบว่าพร้อมที่จะเขียนโดยการนำลูปนี้ไปใช้หรือไม่ ต้องไม่เพิ่มทรัพยากรสตรีมรายการเดียวมากกว่าหนึ่งครั้ง ให้เรียก removeWriteStream()
ก่อนหรือตอบสนองต่อเหตุการณ์นี้ด้วย Listener เดียว จากนั้นจึงส่งจาก Listener นี้ วิธีนี้อาจส่ง Exception
หากการใช้งานลูปนี้ไม่รองรับประเภททรัพยากรที่กำหนด
พารามิเตอร์ตัวที่สองต้องเป็นฟังก์ชันเรียกกลับ Listener ที่ยอมรับทรัพยากรสตรีมเป็นพารามิเตอร์เดียว หากคุณไม่ได้ใช้ทรัพยากรสตรีมภายในฟังก์ชันเรียกกลับ Listener คุณอาจใช้ฟังก์ชันที่ไม่มีพารามิเตอร์เลย
ฟังก์ชั่นการเรียกกลับของผู้ฟังจะต้องไม่ส่ง Exception
ค่าที่ส่งคืนของฟังก์ชันเรียกกลับ Listener จะถูกละเว้นและไม่มีผลกระทบ ดังนั้น ด้วยเหตุผลด้านประสิทธิภาพ ขอแนะนำว่าอย่าส่งคืนโครงสร้างข้อมูลที่มากเกินไป
หากคุณต้องการเข้าถึงตัวแปรใดๆ ภายในฟังก์ชันการโทรกลับ คุณสามารถผูกข้อมูลที่ต้องการเข้ากับการปิดการโทรกลับได้ดังนี้:
$loop->addWriteStream($stream, function ($stream) use ($name) {fwrite($stream, 'Hello ' . $name); -
ดูตัวอย่าง #12 ด้วย
คุณสามารถเรียกใช้ removeWriteStream()
เพื่อลบ Listener เหตุการณ์การเขียนสำหรับสตรีมนี้
ไม่รับประกันลำดับการดำเนินการของผู้ฟังเมื่อสตรีมหลายรายการพร้อมพร้อมกัน
สามารถใช้เมธอด removeReadStream(resource $stream): void
เพื่อลบตัวฟังเหตุการณ์การอ่านสำหรับสตรีมที่กำหนด
การลบสตรีมออกจากลูปที่ถูกลบไปแล้วหรือพยายามลบสตรีมที่ไม่เคยเพิ่มหรือไม่ถูกต้องจะไม่มีผลใดๆ
สามารถใช้เมธอด removeWriteStream(resource $stream): void
เพื่อลบ Listener เหตุการณ์การเขียนสำหรับสตรีมที่กำหนด
การลบสตรีมออกจากลูปที่ถูกลบไปแล้วหรือพยายามลบสตรีมที่ไม่เคยเพิ่มหรือไม่ถูกต้องจะไม่มีผลใดๆ
วิธีที่แนะนำในการติดตั้งไลบรารีนี้คือผ่าน Composer ยังใหม่กับนักแต่งเพลงใช่ไหม?
เมื่อเปิดตัวแล้ว โปรเจ็กต์นี้จะติดตาม SemVer ในขณะนี้จะติดตั้งเวอร์ชันการพัฒนาล่าสุด:
ผู้แต่งต้องการ react/event-loop:^3@dev
ดูเพิ่มเติมที่ CHANGELOG สำหรับรายละเอียดเกี่ยวกับการอัปเกรดเวอร์ชัน
โปรเจ็กต์นี้มีจุดมุ่งหมายเพื่อทำงานบนแพลตฟอร์มใดๆ ดังนั้นจึงไม่จำเป็นต้องมีส่วนขยาย PHP ใดๆ และรองรับการทำงานบน PHP 7.1 จนถึง PHP 8+ ปัจจุบัน ขอแนะนำอย่างยิ่งให้ใช้ PHP เวอร์ชันล่าสุดที่รองรับ สำหรับโปรเจ็กต์นี้
แนะนำให้ติดตั้งส่วนขยายลูปเหตุการณ์ใดๆ แต่เป็นทางเลือกทั้งหมด ดูเพิ่มเติมที่การใช้งาน event loop สำหรับรายละเอียดเพิ่มเติม
หากต้องการรันชุดทดสอบ คุณต้องโคลน repo นี้ก่อน จากนั้นจึงติดตั้งการขึ้นต่อกันทั้งหมดผ่าน Composer:
ติดตั้งผู้แต่ง
หากต้องการรันชุดทดสอบ ให้ไปที่รูทโปรเจ็กต์แล้วรัน:
ผู้ขาย/bin/phpunit
MIT โปรดดูไฟล์ลิขสิทธิ์
ดูองค์ประกอบสตรีมของเราสำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการใช้สตรีมในแอปพลิเคชันในโลกแห่งความเป็นจริง
ดูวิกิผู้ใช้ของเราและผู้ที่พึ่งพา Packagist สำหรับรายการแพ็คเกจที่ใช้ EventLoop ในแอปพลิเคชันในโลกแห่งความเป็นจริง