編寫異步庫 - 讓#x27; s將html轉換為pdf
關鍵要點
- PHP異步編程,例如HTML轉PDF,允許非阻塞操作,通過同時執行其他代碼來提高性能。
- 在異步框架中使用Promise和回調可以簡化延遲操作和潛在錯誤的處理,使代碼更健壯、更易於維護。
- 開發自定義異步庫(如本文討論的HTML轉PDF轉換器)涉及創建抽象,使用ReactPHP和Amp等工具有效地管理異步任務。
- 異步代碼可以適應同步執行,確保不同應用程序架構之間的兼容性和靈活性,而不會犧牲異步編程的優勢。
- 通過將並行執行邏輯抽像到通用的驅動程序系統中,可以支持多個框架和環境,該系統可以與各種異步庫接口。
- 本文闡述了PHP中異步HTML轉PDF轉換的實際實現,強調了理解和利用現代編程範例對高效應用程序開發的重要性。
本文由Thomas Punt同行評審。感謝所有SitePoint的同行評審員,使SitePoint的內容達到最佳狀態!
幾乎在每一次會議上,都會討論到PHP異步編程這個話題。我很高興它現在如此頻繁地被提及。但是,這些演講者沒有透露一個秘密……
創建異步服務器、解析域名、與文件系統交互:這些都是簡單的事情。創建你自己的異步庫很難。而這正是你花費大部分時間的地方!
這些簡單的事情之所以簡單,是因為它們是概念驗證——使異步PHP與NodeJS競爭。你可以看到它們的早期接口有多相似:
var http = require("http"); var server = http.createServer(); server.on("request", function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello World"); }); server.listen(3000, "127.0.0.1");
此代碼使用Node 7.3.0測試
require "vendor/autoload.php"; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $server = new React\Http\Server($socket); $server->on("request", function($request, $response) { $response->writeHead(200, [ "Content-Type" => "text/plain" ]); $response->end("Hello world"); }); $socket->listen(3000, "127.0.0.1"); $loop->run();
此代碼使用PHP 7.1和react/http:0.4.2測試
今天,我們將研究一些方法,使你的應用程序代碼在異步架構中良好運行。別擔心——你的代碼仍然可以在同步架構中工作,因此你不必為了學習這項新技能而放棄任何東西。除了花費一些時間……
你可以在Github上找到本教程的代碼。我已經用PHP 7.1和最新版本的ReactPHP和Amp測試過它了。
充滿希望的理論
異步代碼有一些常見的抽象。我們已經看到其中一個:回調。回調顧名思義,描述了它們如何處理緩慢或阻塞操作。同步代碼充滿了等待。請求某些東西,等待事情發生。
因此,異步框架和庫可以使用回調。請求某些東西,當它發生時:框架或庫將回調你的代碼。
在HTTP服務器的情況下,我們不會搶先處理所有請求。我們也不會等待請求發生。我們只是描述應該調用的代碼,如果請求發生的話。事件循環負責其餘的工作。
第二個常見的抽像是Promise。回調是等待未來事件的鉤子,而Promise是對未來值的引用。它們看起來像這樣:
var http = require("http"); var server = http.createServer(); server.on("request", function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello World"); }); server.listen(3000, "127.0.0.1");
這比單獨使用回調多了一些代碼,但這是一種有趣的方法。我們等待某些事情發生,然後做另一件事。如果出現問題,我們會捕獲錯誤並做出合理的響應。這看起來很簡單,但並沒有被充分討論。
我們仍在使用回調,但我們已經將它們包裝在一個抽像中,這在其他方面對我們有幫助。一個好處是它們允許多個解析回調……
require "vendor/autoload.php"; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $server = new React\Http\Server($socket); $server->on("request", function($request, $response) { $response->writeHead(200, [ "Content-Type" => "text/plain" ]); $response->end("Hello world"); }); $socket->listen(3000, "127.0.0.1"); $loop->run();
我還想讓我們關注另一件事。那就是Promise提供了一種通用的語言——一種通用的抽象——來思考同步代碼如何變成異步代碼。
讓我們獲取一些應用程序代碼並使其異步,使用Promise……
製作PDF文件
應用程序生成某種摘要文檔是很常見的——無論是發票還是庫存清單。假設你有一個電子商務應用程序,它通過Stripe處理付款。當客戶購買商品時,你希望他們能夠下載該交易的PDF收據。
你可以通過多種方式做到這一點,但一種非常簡單的方法是使用HTML和CSS生成文檔。你可以將其轉換為PDF文檔,並允許客戶下載它。
我最近需要做類似的事情。我發現沒有很多好的庫支持這種操作。我找不到單個抽象可以讓我在不同的HTML→PDF引擎之間切換。所以我開始自己構建一個。
我開始考慮我的抽象需要做什麼。我選擇了一個非常類似的接口:
readFile() ->then(function(string $content) { print "content: " . $content; }) ->catch(function(Exception $e) { print "error: " . $e->getMessage(); });
為了簡單起見,我希望除了render方法之外的所有方法都能充當getter和setter。給定這組預期方法,接下來要做的是創建一個實現,使用一個可能的引擎。我將domPDF添加到我的項目中,並開始使用它:
$promise = readFile(); $promise->then(...)->catch(...); // ...让我们向现有代码添加日志记录 $promise->then(function(string $content) use ($logger) { $logger->info("file was read"); });
我不會詳細介紹如何使用domPDF。我認為文檔做得足夠好,讓我可以專注於此實現的異步部分。
我們稍後會查看data和parallel方法。關於此Driver實現的重要一點是它將數據(如果已設置,否則為默認值)和自定義選項收集在一起。它將這些傳遞給我們希望異步運行的回調。
domPDF不是異步庫,將HTML轉換為PDF是一個非常緩慢的過程。那麼我們如何使其異步呢?好吧,我們可以編寫一個完全異步的轉換器,或者我們可以使用現有的同步轉換器;但在並行線程或進程中運行它。
這就是我為parallel方法所做的:
var http = require("http"); var server = http.createServer(); server.on("request", function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello World"); }); server.listen(3000, "127.0.0.1");
在這裡,我實現了getter-setter方法,認為我可以將它們重用於下一個實現。 data方法充當收集各種文檔屬性到數組中的快捷方式,使它們更容易傳遞給匿名函數。
parallel方法開始變得有趣:
require "vendor/autoload.php"; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $server = new React\Http\Server($socket); $server->on("request", function($request, $response) { $response->writeHead(200, [ "Content-Type" => "text/plain" ]); $response->end("Hello world"); }); $socket->listen(3000, "127.0.0.1"); $loop->run();
我非常喜歡Amp項目。它是一組支持異步架構的庫的集合,它們是async-interop項目的關鍵支持者。
他們的一個庫稱為amphp/parallel,它支持多線程和多進程代碼(通過Pthreads和Process Control擴展)。這些spawn方法返回Amp的Promise實現。這意味著render方法可以像任何其他返回Promise的方法一樣使用:
readFile() ->then(function(string $content) { print "content: " . $content; }) ->catch(function(Exception $e) { print "error: " . $e->getMessage(); });
這段代碼有點複雜。 Amp還提供了一個事件循環實現和所有輔助代碼,以便能夠將普通的PHP生成器轉換為協程和Promise。你可以在我寫的另一篇文章中閱讀這甚至是如何可能的,以及它與PHP的生成器有什麼關係。
返回的Promise也正在標準化。 Amp返回Promise規範的實現。它與我上面顯示的代碼略有不同,但仍然執行相同的函數。
生成器的工作方式類似於具有協程的語言中的協程。協程是可以中斷的函數,這意味著它們可以用於執行短時間的操作,然後在等待某些事情時暫停。暫停時,其他函數可以使用系統資源。
實際上,這看起來像這樣:
$promise = readFile(); $promise->then(...)->catch(...); // ...让我们向现有代码添加日志记录 $promise->then(function(string $content) use ($logger) { $logger->info("file was read"); });
這看起來比一開始只編寫同步代碼複雜得多。但它允許的是,當我們等待funcReturnsPromise完成時,其他事情可以發生。
生成Promise正是我們所說的抽象。它為我們提供了一個框架,我們可以通過它來創建返回Promise的函數。代碼可以以可預測和可理解的方式與這些Promise交互。
看看使用我們的驅動程序呈現PDF文檔的樣子:
interface Driver { public function html($html = null); public function size($size = null); public function orientation($orientation = null); public function dpi($dpi = null); public function render(); }
這不如在異步HTTP服務器中生成PDF那樣有用。有一個名為Aerys的Amp庫,它使創建這些類型的服務器更容易。使用Aerys,你可以創建以下HTTP服務器代碼:
class DomDriver extends BaseDriver implements Driver { private $options; public function __construct(array $options = []) { $this->options = $options; } public function render() { $data = $this->data(); $custom = $this->options; return $this->parallel( function() use ($data, $custom) { $options = new Options(); $options->set( "isJavascriptEnabled", true ); $options->set( "isHtml5ParserEnabled", true ); $options->set("dpi", $data["dpi"]); foreach ($custom as $key => $value) { $options->set($key, $value); } $engine = new Dompdf($options); $engine->setPaper( $data["size"], $data["orientation"] ); $engine->loadHtml($data["html"]); $engine->render(); return $engine->output(); } ); } }
同樣,我現在不會詳細介紹Aerys。這是一個令人印象深刻的軟件,非常值得擁有自己的文章。你不需要理解Aerys的工作原理就能看到我們的轉換器代碼在它旁邊看起來有多自然。
我的老闆說“不要用異步!”
如果你不確定你多久才能構建異步應用程序,為什麼要費這麼大的勁?編寫此代碼使我們能夠深入了解新的編程範例。而且,僅僅因為我們正在將此代碼編寫為異步的,並不意味著它不能在同步環境中工作。
要在同步應用程序中使用此代碼,我們只需要將一些異步代碼移到內部:
abstract class BaseDriver implements Driver { protected $html = ""; protected $size = "A4"; protected $orientation = "portrait"; protected $dpi = 300; public function html($body = null) { return $this->access("html", $html); } private function access($key, $value = null) { if (is_null($value)) { return $this->$key; } $this->$key = $value; return $this; } public function size($size = null) { return $this->access("size", $size); } public function orientation($orientation = null) { return $this->access("orientation", $orientation); } public function dpi($dpi = null) { return $this->access("dpi", $dpi); } protected function data() { return [ "html" => $html, "size" => $this->size, "orientation" => $this->orientation, "dpi" => $this->dpi, ]; } protected function parallel(Closure $deferred) { // TODO } }
使用此裝飾器,我們可以編寫看起來像是同步代碼的代碼:
var http = require("http"); var server = http.createServer(); server.on("request", function(request, response) { response.writeHead(200, { "Content-Type": "text/plain" }); response.end("Hello World"); }); server.listen(3000, "127.0.0.1");
它仍然異步運行代碼(至少在後台),但所有這些都不會暴露給消費者。你可以在同步應用程序中使用它,並且永遠不會知道幕後發生了什麼。
支持其他框架
Amp具有一些特定的要求,使其不適合所有環境。例如,基本Amp(事件循環)庫需要PHP 7.0。 parallel庫需要Pthreads擴展或Process Control擴展。
我不想強加這些限制給每個人,並且想知道我如何才能支持更廣泛的系統。答案是將並行執行代碼抽像到另一個驅動程序系統中:
require "vendor/autoload.php"; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $server = new React\Http\Server($socket); $server->on("request", function($request, $response) { $response->writeHead(200, [ "Content-Type" => "text/plain" ]); $response->end("Hello world"); }); $socket->listen(3000, "127.0.0.1"); $loop->run();
我可以為Amp以及(限制較少,但更舊的)ReactPHP實現它:
readFile() ->then(function(string $content) { print "content: " . $content; }) ->catch(function(Exception $e) { print "error: " . $e->getMessage(); });
我習慣於將閉包傳遞給多線程和多進程工作程序,因為這就是Pthreads和Process Control的工作方式。使用ReactPHP Process對象完全不同,因為它們依賴於exec進行多進程執行。我決定實現我習慣使用的相同閉包功能。這對於異步代碼來說不是必需的——這純粹是品味的問題。
SuperClosure庫序列化閉包及其綁定的變量。這裡的大部分代碼都是你期望在worker腳本中找到的代碼。事實上,使用ReactPHP的子進程庫的唯一方法(除了序列化閉包之外)是將任務發送到worker腳本。
現在,我們不再使用$this->parallel和Amp特定的代碼加載我們的驅動程序,而是可以傳遞運行程序實現。作為異步代碼,這類似於:
$promise = readFile(); $promise->then(...)->catch(...); // ...让我们向现有代码添加日志记录 $promise->then(function(string $content) use ($logger) { $logger->info("file was read"); });
不要被ReactPHP代碼與Amp代碼的不同之處所震驚。 ReactPHP沒有實現與Amp相同的協程基礎。相反,ReactPHP更喜歡使用回調來處理大多數事情。這段代碼仍然只是並行運行PDF轉換,並返回生成的PDF數據。
通過抽象的運行程序,我們可以使用任何我們想要的異步框架,並且我們可以預期我們將使用的驅動程序將返回該框架的抽象。
我可以使用這個嗎?
最初只是一個實驗,變成了一個多驅動程序、多運行程序的HTML→PDF庫;稱為Paper。它就像HTML→PDF的Flysystem等效項,但它也是如何編寫異步庫的一個很好的例子。
當你嘗試製作異步PHP應用程序時,你會發現庫生態系統中的差距。不要被這些嚇倒!相反,抓住機會思考一下你將如何使用ReactPHP和Amp提供的抽象來製作你自己的異步庫。
你最近是否構建了一個有趣的異步PHP應用程序或庫?請在評論中告訴我們。
關於異步轉換HTML為PDF的常見問題解答(FAQ)
異步轉換HTML為PDF的意義是什麼?
異步編程在將HTML轉換為PDF方面起著至關重要的作用。它允許執行非阻塞操作,這意味著引擎在後台運行,允許你的其餘代碼在異步操作完成時繼續執行。這導致更有效地利用資源並提高性能,尤其是在涉及大量I/O操作的應用程序中,例如將HTML轉換為PDF。
ReactPHP如何在創建異步庫方面提供幫助?
ReactPHP是一個用於PHP中事件驅動編程的低級庫。它為在PHP中創建異步庫提供了核心基礎設施。使用ReactPHP,你可以使用PHP熟悉的語法編寫非阻塞代碼,從而更容易創建高性能應用程序。
異步轉換HTML為PDF涉及哪些步驟?
異步轉換HTML為PDF的過程涉及幾個步驟。首先,你需要設置一個HTML模板,該模板定義PDF的結構和內容。接下來,你使用ReactPHP之類的異步庫來處理轉換過程。這包括讀取HTML文件,將其轉換為PDF,然後保存生成的PDF文件。此過程的異步性質意味著你的應用程序可以在轉換進行時繼續執行其他任務。
我可以使用PHP以外的其他語言進行異步編程嗎?
是的,你可以使用其他語言進行異步編程。例如,Node.js由於其事件驅動的架構,是構建異步應用程序的流行選擇。但是,如果你已經熟悉PHP,那麼像ReactPHP這樣的庫可以讓你輕鬆利用異步編程的優勢,而無需學習新的語言。
如何在異步轉換HTML為PDF期間處理錯誤?
錯誤處理是異步編程的一個重要方面。在ReactPHP中,你可以通過將錯誤事件處理程序附加到Promise對象來處理錯誤。如果在轉換過程中發生錯誤,則將調用此處理程序,允許你記錄錯誤或採取其他適當的操作。
將HTML轉換為PDF的好處是什麼?
將HTML轉換為PDF有很多好處。它允許你創建一個靜態的、可移植的網頁版本,可以脫機查看、打印或輕鬆共享。 PDF還保留了原始HTML的格式和佈局,確保內容無論在什麼設備或平台上查看都看起來相同。
如何優化我的異步PHP應用程序的性能?
有幾種方法可以優化異步PHP應用程序的性能。一種方法是使用ReactPHP之類的庫,它為事件驅動編程提供低級接口。這允許你編寫非阻塞代碼,這可以顯著提高I/O密集型操作(如將HTML轉換為PDF)的性能。
我可以同步轉換HTML為PDF嗎?
是的,可以同步轉換HTML為PDF。但是,這種方法可能會阻塞你的應用程序的執行,直到轉換過程完成,這可能會導致I/O密集型應用程序出現性能問題。另一方面,異步轉換允許你的應用程序在轉換進行時繼續執行其他任務,從而獲得更好的性能和資源利用率。
PHP中異步編程的挑戰是什麼?
由於PHP的同步特性,PHP中的異步編程可能具有挑戰性。但是,像ReactPHP這樣的庫提供了在PHP中編寫非阻塞代碼所需的架構。理解事件驅動編程模型並掌握Promise的使用也可能具有挑戰性,但它們是利用異步編程優勢的關鍵。
如何測試異步PHP應用程序的性能?
測試異步PHP應用程序的性能包括在不同的負載條件下測量關鍵指標,例如響應時間、內存使用率和CPU利用率。 Apache JMeter或Siege之類的工具可用於模擬應用程序上的負載並收集性能數據。此外,Xdebug之類的分析工具可以幫助你識別代碼中的瓶頸並優化其性能。
以上是編寫異步庫 - 讓#x27; s將html轉換為pdf的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

PHP8.1中的枚舉功能通過定義命名常量增強了代碼的清晰度和類型安全性。 1)枚舉可以是整數、字符串或對象,提高了代碼可讀性和類型安全性。 2)枚舉基於類,支持面向對象特性,如遍歷和反射。 3)枚舉可用於比較和賦值,確保類型安全。 4)枚舉支持添加方法,實現複雜邏輯。 5)嚴格類型檢查和錯誤處理可避免常見錯誤。 6)枚舉減少魔法值,提升可維護性,但需注意性能優化。

會話劫持可以通過以下步驟實現:1.獲取會話ID,2.使用會話ID,3.保持會話活躍。在PHP中防範會話劫持的方法包括:1.使用session_regenerate_id()函數重新生成會話ID,2.通過數據庫存儲會話數據,3.確保所有會話數據通過HTTPS傳輸。

SOLID原則在PHP開發中的應用包括:1.單一職責原則(SRP):每個類只負責一個功能。 2.開閉原則(OCP):通過擴展而非修改實現變化。 3.里氏替換原則(LSP):子類可替換基類而不影響程序正確性。 4.接口隔離原則(ISP):使用細粒度接口避免依賴不使用的方法。 5.依賴倒置原則(DIP):高低層次模塊都依賴於抽象,通過依賴注入實現。

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

RESTAPI設計原則包括資源定義、URI設計、HTTP方法使用、狀態碼使用、版本控制和HATEOAS。 1.資源應使用名詞表示並保持層次結構。 2.HTTP方法應符合其語義,如GET用於獲取資源。 3.狀態碼應正確使用,如404表示資源不存在。 4.版本控制可通過URI或頭部實現。 5.HATEOAS通過響應中的鏈接引導客戶端操作。

在PHP中,異常處理通過try,catch,finally,和throw關鍵字實現。 1)try塊包圍可能拋出異常的代碼;2)catch塊處理異常;3)finally塊確保代碼始終執行;4)throw用於手動拋出異常。這些機制幫助提升代碼的健壯性和可維護性。

匿名類在PHP中的主要作用是創建一次性使用的對象。 1.匿名類允許在代碼中直接定義沒有名字的類,適用於臨時需求。 2.它們可以繼承類或實現接口,增加靈活性。 3.使用時需注意性能和代碼可讀性,避免重複定義相同的匿名類。
