1. 程式人生 > >介紹一個基於 .NET 的船新 PHP SDK + Runtime: PeachPie

介紹一個基於 .NET 的船新 PHP SDK + Runtime: PeachPie

## 前言 這幾天想基於 .NET Core 搞一個自己的部落格網站,於是在網上搜刮各種部落格引擎,找到了這些候選:Blogifier、Miniblog 以及 edi 寫的 Moonglade。 Blogifier:這是前端是個 Angular SPA 應用,不利於 SEO,同時首屏載入速度慢,因此排除。 Miniblog:顧名思義 Mini,可以完美承載內容但是主題實在是過於簡單,沒有可自定義性,因此排除。 Moonglade:總體感覺不錯,介面設計得也很好,功能全面,然而需要 SQL Server 作為資料庫,然而 SQL Server 雖然有 Linux 版本,但受限於主機配置和預算因此也被排除。 難道就沒有適合我需求的部落格引擎了嗎?答案當然是:有。 眾所周知 PHP 是世界上最好的語言(滑稽),還是眾所周知有一個叫做 WordPress 的部落格引擎生態非常龐大,而且是使用 PHP 構建的。 可是 PHP 和 .NET 又有什麼關係呢? ## PeachPie PeachPie 是一個完全構建於 .NET Standard 之上的一套完整的 PHP SDK + Runtime,包含編譯器和執行時等等,相容 PHP 5.4-7.4(當然部分功能仍在開發中)。 官網:https://www.peachpie.io 那麼 PeachPie 有什麼優點呢: - 開源:https://github.com/peachpiecompiler/peachpie - 跨平臺:因為 PeachPie 完全構建於 .NET 之上,因此也就跟著跨平臺了,Windows、MacOS、Linux 等等,從架構上跨 x86、x86_64、ARM、ARM64,未來甚至還會有 MIPS、MIPS64、Risc-V 等等...... - 純託管程式碼:藉助 VS 強大的偵錯程式和 IDE 體驗,從開發、除錯到測試、Profile 一條龍非常爽 - 編譯:PHP 是沒有編譯之說的,這門動態型別語言和 Python 面臨一樣的問題,幾乎無法在編譯時發現程式碼中的錯誤,即便藉助 linter 診斷出了語法錯誤也很難診斷出型別的錯誤。而 PeachPie 則有完善的編譯器套件將 PHP 程式碼完整的編譯為 .NET Standard 程式集,意味著在編譯期就做好了語法和型別檢查,保證了執行時不會因為程式碼問題導致程式崩潰,同時應用分發的時候也不需要原始碼,確保了原始碼安全 - 與 .NET 互操作:PeachPie 在保留了 PHP 原本的生態基礎上做到了 PHP 和 .NET 的互操作,一個 PeachPie 專案不但可以使用 PHP 原有生態中的包和外掛,還能享受 .NET 的生態,快樂超級加倍 - 執行在 .NET 上:CLR/CoreCLR 自帶久經考驗的 JIT 和 GC,因此通過 PeachPie 編譯的程式集執行在 CLR/CoreCLR 之上則無需做任何的程式碼改動即可享受到這些東西,在 php-bench 中,藉助 CoreCLR 平臺的 JIT,函式呼叫效能拉開了原來 PHP 幾個數量級 - .NET Foundation 專案:背後有 .NET Foundation 支援,瓦利亞高品質,有保證 可是有人就要問了,為什麼我不直接用 PHP 而是選用 PeachPie 曲線救國呢? 因為我樂意,雨女無瓜(逃 ## 開始使用 本文開發環境採用 Visual Studio Code(需要安裝 PeachPie 外掛),當然你也可以用 Visual Studio 等其他開發工具。 安裝 PeachPie 最新的專案模板: ```bash dotnet new -i Peachpie.Templates::* ``` 然後就會出現三個新的專案模板:Console Application、Class library 和 ASP.NET Core Empty。 我們這次整個 Console Application 看看。 ```bash dotnet new console -lang PHP ``` 然後隨便寫點程式碼: ```php "Joe", "score" => 83, "last_name" => "Smith"), array("first_name" => "Frank", "score" => 92, "last_name" => "Barbson"), array("first_name" => "Benji", "score" => 90, "last_name" => "Warner") ); foreach ($students as $value) { echo $value["first_name"], " ", $value["last_name"], "'s score is ", $value["score"], "\n"; } } main(); ``` 用配置 .NET Core 專案的方式寫好 Visual Studio Code 需要的 tasks.json 和 launch.json,隨便下點斷點然後編譯 + F5 執行! 編譯輸出(請無視掉我的霓虹語電腦環境): ``` .NET Core 向け Microsoft (R) Build Engine バージョン 16.7.0-preview-20220-01+80e487bff Copyright (C) Microsoft Corporation.All rights reserved. 復元対象のプロジェクトを決定しています... 復元対象のすべてのプロジェクトは最新です。 プレビュー版の .NET Core を使用しています。https://aka.ms/dotnet-core-preview をご覧ください PeachPie PHP Compiler version 0.9.981+565af85b9aafc42fe1af2f30ccd73ff093a2fad7 PeachPieConsole -> C:\Users\hez20\source\repos\PeachPieConsole\bin\Debug\netcoreapp3.1\PeachPieConsole.dll ビルドに成功しました。 0 個の警告 0 エラー 経過時間 00:00:12.98 ``` Voila! ![Debug](https://img2020.cnblogs.com/blog/1590449/202005/1590449-20200512210947373-550627439.jpg) 輸出: ``` Joe Smith's score is 83 Frank Barbson's score is 92 Benji Warner's score is 90 ``` 如果去掉打錯一個變數 `$value` 變成 `$vuale` 會怎麼樣呢? ```php "Joe", "score" => 83, "last_name" => "Smith"), array("first_name" => "Frank", "score" => 92, "last_name" => "Barbson"), array("first_name" => "Benji", "score" => 90, "last_name" => "Warner") ); foreach ($students as $value) { echo $vuale["first_name"], " ", $value["last_name"], "'s score is ", $value["score"], "\n"; } } main(); ``` 編譯輸出: ``` .NET Core 向け Microsoft (R) Build Engine バージョン 16.7.0-preview-20220-01+80e487bff Copyright (C) Microsoft Corporation.All rights reserved. 復元対象のプロジェクトを決定しています... 復元対象のすべてのプロジェクトは最新です。 プレビュー版の .NET Core を使用しています。https://aka.ms/dotnet-core-preview をご覧ください PeachPie PHP Compiler version 0.9.981+565af85b9aafc42fe1af2f30ccd73ff093a2fad7 program.php(13,14): warning PHP5007: Undefined variable: $vuale [C:\Users\hez20\source\repos\PeachPieConsole\PeachPieConsole.msbuildproj] PeachPieConsole -> C:\Users\hez20\source\repos\PeachPieConsole\bin\Debug\netcoreapp3.1\PeachPieConsole.dll ビルドに成功しました。 program.php(13,14): warning PHP5007: Undefined variable: $vuale [C:\Users\hez20\source\repos\PeachPieConsole\PeachPieConsole.msbuildproj] 1 個の警告 0 エラー 経過時間 00:00:09.51 ``` 由於上述程式碼在 PHP 中是合法程式碼,因此為了保持相容性,PeachPie 不會報錯而是給了警告。 但如果我們少一個分號呢: ```php "Joe", "score" => 83, "last_name" => "Smith"), array("first_name" => "Frank", "score" => 92, "last_name" => "Barbson"), array("first_name" => "Benji", "score" => 90, "last_name" => "Warner") ) foreach ($students as $value) { echo $value["first_name"], " ", $value["last_name"], "'s score is ", $value["score"], "\n"; } } main(); ``` 編譯輸出: ``` .NET Core 向け Microsoft (R) Build Engine バージョン 16.7.0-preview-20220-01+80e487bff Copyright (C) Microsoft Corporation.All rights reserved. 復元対象のプロジェクトを決定しています... 復元対象のすべてのプロジェクトは最新です。 プレビュー版の .NET Core を使用しています。https://aka.ms/dotnet-core-preview をご覧ください PeachPie PHP Compiler version 0.9.981+565af85b9aafc42fe1af2f30ccd73ff093a2fad7 program.php(12,5): error PHP2014: Syntax error: unexpected token 'foreach' [C:\Users\hez20\source\repos\PeachPieConsole\PeachPieConsole.msbuildproj] ビルドに失敗しました。 program.php(12,5): error PHP2014: Syntax error: unexpected token 'foreach' [C:\Users\hez20\source\repos\PeachPieConsole\PeachPieConsole.msbuildproj] 0 個の警告 1 エラー 経過時間 00:00:01.77 ``` 這次就會直接報錯了。 由此可見,使用 PeachPie 能夠無需第三方工具輔助,直接在編譯時就驗證程式碼正確性,對專案的健壯性有很大幫助。 ## PHP 與 .NET 互操作 我們試試互操作,在 PHP 裡面建立一個 .NET 中的 `HashSet`: ```php ; $list->Add("test"); $list->Add("hello"); $list->Add("hello"); $list->Add("lol"); foreach ($list as $key => $value) { echo $key, ": ", $value, "\n"; } } main(); ``` 輸出: ``` 0: test 1: hello 2: lol ``` 完美,另外,鑑於 PHP 程式碼最後都會被編譯成 .NET Standard 程式集,因此反過來當然也沒問題,就不做介紹了。 ## 一些坑 PeachPie 已經發展了好幾年的時間了,儘管大多數 PHP 程式碼都能正常執行,但是標準庫仍存在一些相容性問題,具體可以去這裡跟蹤:https://docs.peachpie.io/compatibility-status 。 由於目前還在補全相容性問題,所以很多優化工作(比如陣列的優化)都沒有做,效能方面還有很大的提升空間。 不過官方目前開發進度十分快,因此短時間內就能看到大量的新庫函式被實現,到目前已經是 0.9.800,1.0 正式版也快要釋出了,很快就能正式投入生產使用啦。 ## Blog 搭建 回到前面的主題,有了 PeachPie,我就能把 WordPress 放到 .NET Core 上面跑啦。 當然,直接下載下來 WordPress 的原始碼編譯跑到 ASP.NET Core 上面時會出現一些問題,比如資源載入全部 404,這是因為 PeachPie 在編譯 PHP 程式碼時預設不會將非 .php 的檔案包含到編譯過程中,我們需要修改 .msbuildproj 調整專案屬性將資原始檔包含在編譯過程中,並作為 Content 引入。 另外由於 WordPress 首次配置會現場生成一個 config.php 檔案,但是由於該檔案是編譯後的程式集在執行時生成的,未參與編譯過程,因此執行時是找不到這個類的,除非重新編譯一遍。因此我們想採用更清真的方式,直接在 appsettings.json 裡面寫入配置然後執行時讀入代替原來的 config.php。 總之需要經過一系列操作,並且編寫少量程式碼。不過,PeachPie 已經幫我們做好了這一切:iolevel 提供了一個即插即用的 WordPress 包 `PeachPied.WordPress.AspNetCore`(https://github.com/iolevel/wpdotnet-sdk ),可直接作為 ASP.NET Core 中介軟體使用,非常方便。 那麼事情就簡單了: ```bash dotnet new web dotnet add package PeachPied.WordPress.AspNetCore --version 1.0.0-* ``` 然後編寫少量服務端程式碼,配置一下 https 跳轉、響應壓縮和靜態檔案什麼的,再加入 WordPress 中介軟體: Startup.cs ```csharp using System.Linq; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace KeBlogs { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddResponseCompression(options => { options.Providers.Add(); options.Providers.Add(); options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "image/svg+xml", "image/png", "font/woff", "font/woff2", "font/ttf", "font/eof", "image/x-icon", "application/json", "application/octet-stream" }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseHttpsRedirection(); app.UseResponseCompression(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); app.UseStaticFiles(); app.UseWordPress(); } } } ``` 程式碼部分搞定,當然上述程式碼你也可以用 PHP 來寫。 然後在 appsettings.json 寫入自己的配置,比如(SALT 部分可以沒有): ``` { "WordPress": { "dbhost": "localhost", "dbpassword": "password", "dbuser": "root", "dbname": "wordpress", "dbTablePrefix": "wp_", "SALT": { "AUTH_KEY": "r(EoMbKEvlg){+!T42fh-e+~IGj-4q}g8HHB9hjbiC0J*ySU1Y*3z[3c}F;6=TA5", "AUTH_SALT": "q0#AzvJ*[4~Bexa9*M(sC_#pDuGQBdjL1}j*RilSe0ku]P~KuTir[7PxjE:4)_zR", "LOGGED_IN_KEY": "!AAienFSridCUzF(v}m#}_;+t%Rclg;mOPKwe;w7dN0M{d,]?8V+TRW_UG)tSswa", "LOGGED_IN_SALT": "C=(4(8WPMeRu_h?g7!ddI*P:+SYU=3C%g)92oV}-y5tE0r?DHWl!fjPOp=bjx2YJ", "NONCE_KEY": "Z[e37@=y)m.CHa:OSldh#RT@nIZxKYGwu!/hd:vK#^{_Ec7e{KNb(G.8ch/MkH(d", "NONCE_SALT": ";v7Wv/BV)Pz{W,FaAKC0buH*5U4:g]qn~;b94x]f8=lm6!yyYSbW5*2y*kRXXEF5", "SECURE_AUTH_KEY": "pc}_Pv52,m=j9l#llSkLVQib.Zm!;9FRzg:{(G]tM8}[}]pPDwB4k{xV+!e)9lmR", "SECURE_AUTH_SALT": "#n]+o^w/%-~MVzf{AUuxUAwF[n03r{kr^r1V?wqQ?Vjt}!0HSkCB-):u-ra1%tB=" }, "constants": { } } } ``` 然後釋出我們的 WordPress! ```bash dotnet publish -c Release ``` 最後打包 bin/Release/netcoreapp3.1/publish 上傳到伺服器上面,搭建好資料庫然後執行即可。 ## 完結撒花 進入管理面板,大多數主題、外掛都能正常工作,安裝點主題,配置配置外掛和 SMTP,就全部搞定啦。 ![Admin](https://img2020.cnblogs.com/blog/1590449/202005/1590449-20200512210924111-981887807.jpg) 記憶體佔用 195 MB,執行在 .NET Core 3.1.3 上,非常清真! 至此我的 Blog 搭建完成,歡迎大家訪問:https://hez2010.com 。 評論和註冊什麼的也開放了,歡迎大家常光臨~ 後續我也會不斷在上面更新文章,當然,這個 Blog 上面的內容也就不僅限於程式設計啦,敬請期待~ ![Blog](https://img2020.cnblogs.com/blog/1590449/202005/1590449-20200512210912933-854344128.jpg) 從此 PHP 也是 .NET 上的一門語言了,完結撒花~