為Windows,Mac和Linux桌面操作系統創建用HTML,CSS,JavaScript和PHP編寫的輕巧,可安裝的應用程序。

與傳統桌面應用程序開發相比,使用PHP應用程序服務器可以創建的是真實的可安裝軟件應用程序。從想法/概念到1/10的全部部署,並僅使用一個代碼庫來支持每個主要的桌面操作系統。

PHP App Server是用PHP編寫的完整且可擴展的Web服務器,其自定義功能專門設計用於傳統的台式OS環境中。當用戶通過其開始菜單,應用程序啟動器等運行軟件時,軟件啟動服務器,然後啟動用戶首選的Web瀏覽器以訪問應用程序。當Web瀏覽器處理顯示用戶界面的所有細節細節時,PHP會為後端提供動力。現成的安裝程序腳本簡化了創建最終版本軟件包的過程,以交付給用戶的計算機系統。
下載或克隆最新的軟件版本。克隆時,請確保在開始開發之前為您的應用程序創建分支。這樣做會避免在PHP App Server本身獲取上游更新時不小心覆蓋軟件。
對於本指南的其餘部分,假定已安裝了最新版本的PHP。有很多方法可以實現這一目標。
從命令行中,運行以下以獲取命令行選項列表:
php server.php -?
通過運行:在端口9002上啟動Web服務器:
php server.php -port=9002
PHP應用程序服務器的目錄結構如下:
server.php需要操作的文件。在“ www”目錄中創建一個index.php文件:
<?php
phpinfo ();使用您的Web瀏覽器連接到運行服務器:
http://127.0.0.1:9002/
phpinfo()的輸出顯示在瀏覽器中,並將請求的結果寫入命令行。
將URL更改為:
http://127.0.0.1:9002/api/v1/account/info
相同的index.php文件運行。
重命名或將index.php文件複製到api.php並重新加載頁面。現在稱為api.php 。 PHP App Server的虛擬目錄功能在開發應用程序時可能會很有用。
安裝的軟件應用程序無法寫入“ www”。應用程序通常由系統上的特權用戶安裝,但是運行該軟件的人員通常沒有足夠的權限寫入“ www”目錄。這是在使用PHP App Server開發軟件應用程序時要牢記的重要考慮因素。幸運的是,服務器中已經內置的此問題有一個解決方案:雙文檔根。
當PHP代碼從應用程序的“ www”目錄執行時,它可以訪問五個$_SERVER變量,這些變量通過PHP App Server環境傳遞,並且是唯一的:
DOCUMENT_ROOT_USER的父目錄。也可以寫入但不能由URL引用。對於為應用程序存儲私有數據(例如SQLite數據庫)有用。server.php居住)。對於support子目錄中的文件很有用。當向Web服務器提出請求時,PHP App Server首先在應用程序的“ www”目錄中查找文件。如果它在那裡找不到文件,則將檢查DOCUMENT_ROOT_USER指定的路徑中的文件。
編寫依賴Web瀏覽器的LocalHost服務器應用程序可能會導致嚴重的系統安全性違規行為,從數據控制丟失到損害用戶的文件系統。只要正確編寫了應用程序,Web瀏覽器的策略通常將保護用戶免受嘗試訪問PHP App Server控制內容的惡意網站和用戶。
但是,這裡有一些重要的,請選擇所有基於PHP App Server的軟件應用程序必須積極抗辯(按重要性順序):
$_SERVER["PAS_USER_FILES"]或用戶定義的位置存儲敏感用戶數據,而不是$_SERVER["DOCUMENT_ROOT_USER"] 。始終詢問用戶如果他們認為某些東西要敏感該怎麼辦(例如,詢問與向用戶顯示複選框一樣簡單)。以隱私為中心的人通常會說出自己的想法。$_SERVER["PAS_SECRET"]與CubicleSoft Admin Pack或其他應用程序框架相結合,有助於解決此問題。服務器擴展程序通常還需要基於$_SERVER["PAS_SECRET"]身份驗證令牌。OWASP排名前10名列表中還有許多其他安全考慮因素,並且還要記住OWASP攻擊列表,但這是很大的。
PHP App Server包含功能強大的服務器擴展名和兩個SDK,可讓啟動,管理和監視長期運行的過程輕鬆且可從PHP和JavaScript中安全。啟動流程作為用戶運行,PHP App Server正在運行AS,但不受常規CGI/FASTCGI請求(例如常規CGI/FASTCGI請求)的限制。可以通過隨附的JavaScript SDK從Web瀏覽器中積極監控運行過程,甚至可以與之進行交互。

理想情況下,長期運行的腳本應存儲在主PHP App Server“支持”目錄中的“腳本”子目錄中。這樣,它們遠離了主要的Web根部,但是該應用程序仍然可以通過$_SERVER["PAS_ROOT"]找到它們。
這是使用PHP SDK啟動名為“ Test.php”的PHP腳本的示例:
<?php
$ rootpath = str_replace ( "\" , " / " , dirname ( __FILE__ ));
// Load the PHP App Server common functions.
require_once $ _SERVER [ " PAS_ROOT " ] . " /support/process_helper.php " ;
require_once $ _SERVER [ " PAS_ROOT " ] . " /support/pas_functions.php " ;
$ cmd = escapeshellarg ( PAS_GetPHPBinary ());
$ cmd .= " " . escapeshellarg ( realpath ( $ _SERVER [ " PAS_ROOT " ] . " /support/scripts/test.php " ));
$ options = array (
// "rules" => array(
// "start" => time() + 5,
// "maxqueued" => 3
// ),
// "stdin" => false,
// "dir" => $_SERVER["PAS_ROOT"] . "/support/scripts/",
// "env" => ProcessHelper::GetCleanEnvironment(),
// "extraenv" => array("PASSWORD" => "supersecret"),
// "extra" => array(
// "title" => "Custom window title",
// "inputmode" => "readline"
// )
);
// Start the process.
require_once $ rootpath . " /support/pas_run_process_sdk.php " ;
$ rp = new PAS_RunProcessSDK ();
$ result = $ rp -> StartProcess ( " demo " , $ cmd , $ options );
if (! $ result [ " success " ]) echo " An error occurred while starting a long-running process. " ;
echo " Done. " ;
?>每個過程都會給出一個標籤,該標籤允許通過標籤對多個運行過程進行分組。在上面的示例中,標籤稱為“演示”。 Javacsript SDK稍後可用於顯示使用特定標籤的過程:
<?php
header ( " Content-Type: text/html; charset=UTF8 " );
?>
<!DOCTYPE html>
<html>
<body>
<?php
$ rootpath = str_replace ( "\" , " / " , dirname ( __FILE__ ));
require_once $ rootpath . " /support/pas_run_process_sdk.php " ;
PAS_RunProcessSDK:: OutputCSS ();
PAS_RunProcessSDK:: OutputJS ();
?>
<div id="terminal-manager"></div>
<script type="text/javascript">
// NOTE: Always put Javascript RunProcesSDK and TerminalManager class instances in a Javascript closure like this one to limit the XSRF attack surface.
(function() {
// Establish a new connection with a compatible WebSocket server.
var runproc = new RunProcessSDK(' <?= PAS_RunProcessSDK:: GetURL () ?> ', false, ' <?= PAS_RunProcessSDK:: GetAuthToken () ?> ');
// Debugging mode dumps incoming and outgoing packets to the web browser's debug console.
runproc.debug = true;
// Establish a new terminal manager instance.
var elem = document.getElementById('terminal-manager');
// Automatically attach to all channels with the 'demo' tag.
var options = {
tag: 'demo'
};
var tm = new TerminalManager(runproc, elem, options);
})();
</script>
</body>
</html>PHP SDK簡化了將必要的CSS和Javscript依賴項排放到HTML中。上面的代碼演示了設置與PHP App Server擴展名的Websocket連接並將其連接到終端Manager實例,以監視使用“演示”標籤的進程。 TerminalManager是一個包含的JavaScript類,它會根據輸入標準自動創建和管理一個或多個execterminal(也包括)。在這種情況下,TerminalManager將自動連接到使用“演示”標籤創建的任何過程。外觀看起來像這樣:

每個execterminal都包含一個具有其他功能的Xterm.js終端實例:
還有更多。
請注意,管理長期運行的過程並不需要終端管理器和外觀,但它們確實處理了很多常見的情況。上面的示例代碼僅刮擦可以完成的操作的表面。
以下是終端man選項的完整列表:
隨附的XTERM PHP類提供了從長期運行腳本到瀏覽器中與XTerm兼容的execterminal的輸出的無縫和簡化控制。無需記住ANSI逃生代碼。這是一個示例腳本:
<?php
if (! isset ( $ _SERVER [ " argc " ]) || ! $ _SERVER [ " argc " ])
{
echo " This file is intended to be run from the command-line. " ;
exit ();
}
$ rootpath = str_replace ( "\" , " / " , dirname ( __FILE__ ));
require_once $ rootpath . " /../xterm.php " ;
for ( $ x = 0 ; $ x < 5 ; $ x ++)
{
echo " Test: " . ( $ x + 1 ) . "n" ;
sleep ( 1 );
}
echo " That's boring. Let's... " ;
sleep ( 1 );
XTerm:: SetItalic ();
echo " spice it up! n" ;
XTerm:: SetItalic ( false );
sleep ( 1 );
$ palette = XTerm:: GetBlackOptimizedColorPalette ();
for ( $ x = 5 ; $ x < 10 ; $ x ++)
{
$ num = mt_rand ( 17 , 231 );
XTerm:: SetForegroundColor ( $ palette [ $ num ]);
echo " Test: " . ( $ x + 1 ) . " (Color " . $ palette [ $ num ] . " ) n" ;
sleep ( 1 );
}
XTerm:: SetForegroundColor ( false );
XTerm:: SetTitle ( " Changing the title... " );
usleep ( 250000 );
XTerm:: SetTitle ( " Changing the title...like " );
usleep ( 250000 );
XTerm:: SetTitle ( " Changing the title...like a " );
usleep ( 250000 );
XTerm:: SetTitle ( " Changing the title...like a BOSS! " );
usleep ( 500000 );
echo "n" ;
echo " Enter some text: " ;
$ line = rtrim ( fgets ( STDIN ));
XTerm:: SetBold ();
echo " Here's what you wrote: " . $ line . "nn" ;
XTerm:: SetBold ( false );
echo " [Switching to 'readline_secure' mode] nn" ;
XTerm:: SetCustomInputMode ( ' readline_secure ' );
echo " Enter some more text: " ;
XTerm:: SetColors ( 0 , 0 );
$ line = rtrim ( fgets ( STDIN ));
XTerm:: SetColors ( false , false );
XTerm:: SetCustomInputMode ( ' readline ' );
XTerm:: SetBold ();
echo " Here's what you wrote: " . $ line . "nn" ;
XTerm:: SetBold ( false );
echo " Done. n" ;
?>最後,如果您使用CubicleSoft Admin Pack或FlexForms來構建應用程序,則PHP SDK包含本地FlexForms Integration(即無需編寫JavaScript/HTML):
<?php
// Admin Pack and FlexForms integration.
require_once " support/pas_run_process_sdk.php " ;
$ contentopts = array (
" desc " => " Showing all long-running processes with the 'demo' tag. " ,
" fields " => array (
array (
" type " => " pas_run_process " ,
// "debug" => true,
" options " => array (
" tag " => " demo "
)
)
)
);
BB_GeneratePage ( " Process Demo " , $ menuopts , $ contentopts );
?> 編寫擴展名需要一些有關PHP應用程序服務器的工作方式的知識:擴展程序在啟動期間的早期加載,以便如果需要的話,他們可以參與啟動序列(主要僅用於安全相關的擴展)。 Web服務器啟動後,每個Web請求都會瀏覽擴展名單,並問:“您可以處理此請求嗎?”如果擴展名在肯定的(即返回true)中響應,則其餘的請求將傳遞給擴展名處理。
由於擴展程序是直接與Core Server直接運行的,因此它們會得到顯著的性能提升,並且可以做一些事情,例如通過Websocket做出響應或開始長期運行的過程,這些過程通常在30秒後被普通的PHP路徑殺死。
但是,這些好處帶有兩個主要缺點。首先是,如果擴展名提出了未被發現的異常或以其他方式崩潰,則將整個Web服務器帶有它。第二是,對擴展程序進行代碼更改需要重新啟動Web服務器以測試更改,這可能有些麻煩。通常,正常的“ www”路徑足以滿足大多數需求,並且擴展是偶爾的專業邏輯段。
隨附的安全令牌擴展名是構建可以正確處理請求的擴展程序的絕佳起點。安全令牌擴展名相當簡短,注重且作品。
服務器假設文件名是類名稱的一部分。無論命名PHP文件如何,都必須效仿的類名稱,否則PHP App Server將無法加載擴展名。擴展名稱應以一個數字開頭,這表示調用擴展名的預期順序。
普通PHP腳本可用的變量也可以通過全局$baseenv變量(例如$baseenv["DOCUMENT_ROOT_USER"]和$baseenv["PAS_USER_FILES"] )。請不要更改$baseenv值,因為這會對應用程序的其餘部分產生負面影響。
始終在啟動外部,長期運行的進程時,請始終使用ProcessHelper::StartProcess()靜態函數。 ProcessHelper類旨在在所有平台的背景中啟動非阻滯過程。請注意,開始長期運行過程的首選方法是使用長期運行的過程擴展。
對於某些任務,重要的是要告訴PHP App Server退出。例如,在Windows上升級PHP App Server應用程序時,需要更新PHP本身,因此在升級過程中不能運行。在最後一個瀏覽器選項卡關閉後不久,退出應用程序也不久。
有兩種可用的方法來觸發服務器的早期終止:
/exit-app/ ,並在JSON對像中發送有效的authtoken和一個delay該對象指定了等待終止服務器的秒數。使用後,應用程序的每個頁面都必須連接到/exit-app/以保持服務器的生命。建議最小延遲3秒。 Web瀏覽器離開頁面或關閉選項卡後往往會刪除Websocket連接。X-Exit-App標頭。標題的值是等待終止服務器的秒數的整數數。建議最小延遲3秒。這個特殊的標題未傳遞給網絡瀏覽器,而是內部處理的。出口應用擴展方法的示例PHP代碼:
<script type="text/javascript">
// NOTE: Always put WebSocket class instances in a Javascript closure like this one to limit the XSRF attack surface.
( function () {
function InitExitApp ()
{
var ws = new WebSocket ((window.location.protocol === ' https: ' ? ' wss:// ' : ' ws:// ' ) + window.location.host + ' /exit-app/ ' );
ws. addEventListener ( ' open ' , function ( e ) {
var msg = {
authtoken: ' <?=hash_hmac("sha256", "/exit-app/", $_SERVER["PAS_SECRET"])?> ' ,
delay: 3
};
ws. send ( JSON . stringify (msg));
});
ws. addEventListener ( ' close ' , function ( e ) {
setTimeout (InitExitApp, 500 );
});
}
InitExitApp ();
})();
</script>標題方法的示例PHP代碼:
<?php
// User clicked an "Exit application" link or something.
header ( " X-Exit-App: 3 " );
?>擴展程序是一種更可靠的方法,可以檢測到應用程序的所有瀏覽器選項卡已關閉。但是,如果該應用程序由於某種原因無法支持擴展程序,請改用標頭方法。標題方法最好在有意義的頁面上使用(例如帶有升級信息的頁面)。
在運行生成安裝程序軟件包的各種腳本之前,需要創建,重命名和/或修改各種文件。每個以“ yourapp”開頭的文件都需要重命名為您的應用程序名稱,最好僅限於所有小寫AZ和連字符。這需要做到這一點,以便對軟件的更新不會意外覆蓋您的工作,以便任何圍繞目錄結構戳戳的用戶都可以看到應用程序的實際名稱,而不是“ yourapp”。
“ yourapp.phpapp”文件是一個PHP文件,可以執行啟動Web Server(Server.php)的實際應用程序啟動序列,然後啟動用戶的Web瀏覽器。文件中有一個$options數組,應根據您的應用程序的需求進行修改:
最後三個選項旨在用於高度專業化的方案。將“主機”更改為“ 127.0.1.1”之類的東西可能還可以,但不要使用“ 0.0.0.0”或“ :: 0”,該服務器將服務器公開綁定到網絡接口。在用戶試圖重新啟動應用程序時,在用戶開始抱怨錯誤消息之前,與特定的“端口”數字綁定似乎是個好主意。
“ Quitdelay”選項很有趣。 PHP應用程序服務器的服務器部分將一直持續到最後一個客戶端斷開連接後的“ Quitdelay”。該應用程序應每五分鐘發送一次“心跳”請求,以確保在用戶使用應用程序完成用戶之前不會終止自身。
每個平台包裝工具都有自己的說明:
xdg-utils (GNOME,KDE,XFCE等)的Freedesktop.org兼容窗口管理器。有一些已知的包裝問題:
安裝程序和服務器軟件背後有一些有趣的故事。也許有一天我會分享這些故事。現在,請在PHP App Server中構建下一個應用程序!