溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

PHP內置web服務器應該怎么做

發布時間:2020-04-08 13:48:35 來源:億速云 閱讀:116 作者:小新 欄目:編程語言

今天小編給大家分享的是PHP內置web服務器應該怎么做,很多人都不太了解,今天小編為了讓大家更加了解PHP內置web服務器的方法,所以給大家總結了以下內容,一起往下看吧。一定會有所收獲的哦。

PHP內置web服務器應該怎么做

前言

PHP從5.4開始,就提供了一個內置的web服務器。

這個主要是用來做本地的開發用的。不能用于線上環境?,F在我就介紹一下這個工具如何使用。

基礎應用

首先我們假定項目目錄是/home/baoguoxiao/www/php/demo,外界可訪問的目錄是/home/baoguoxiao/www/php/demo/public。然后訪問的端口是8000,入口文件是index.phpindex.html。那么我們可以執行如下命令:

cd /home/baoguoxiao/www/php/demo/public
php -S localhost:8000

然后這個時候就可以正常訪問了。

那么現在有個問題,就是難道每次必須要進入public文件夾才能啟動web服務器嗎,其實我們可以指定根目錄的,那么可以使用如下命令:

cd /home/baoguoxiao/www/php/demo
php -S localhost:8000 -t public/

那么現在有一個問題就是說,如果我們使用了單入口,而且還是用了PATHINFO模式。那么上面的可能就有問題了。

對此,我們可以使用如下方案:

cd /home/baoguoxiao/www/php/demo
php -S localhost:8000 router.php

router.php 文件的代碼

/**
 * 對URL進行解析,并獲取請求的文件名
 */$uri = urldecode(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));/**
 * 判斷是否存在該文件,如果不存在,則直接繼續加載入口文件
 */if ($uri !== "/" && file_exists(__DIR__ . "$uri")) {    return false;
}/**
 * 加載入口文件
 */require_once "./index.php";

通過這個路由文件,我們就可以支持目前常用的開發情況了。

框架參考

上面的方式是我們自己的實現,那么我們也可以看看相關知名框架的實現方法。

比如 Laravel 和 Symfony。

Laravel

在Laravel中的安裝一節中介紹了一個命令可以使用PHP內置web服務器實現外部訪問的命令。實現的命令是:

php artisan serve

我們可以看一下相關代碼:

具體的文件路徑為:vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php

/**
 * 執行命令.
 *
 * @return int
 *
 * @throws \Exception
 */
public function handle()
{
    // 切換路徑到 public 目錄
    chdir(public_path());

    // 在命令臺進行輸出相關內容
    $this->line("<info>Laravel development server started:</info> <http://{$this->host()}:{$this->port()}>");

    // 執行外部程序,并且 $status 為系統的返回狀態
    passthru($this->serverCommand(), $status);

    // $status 為0 表示執行正常, 為其他大于0的數字表示出現了錯誤,有可能是端口被搶占了,這個時候就會接著判斷是否進行再次嘗試
    if ($status && $this->canTryAnotherPort()) {
        // 對綁定的端口號加1 默認是8000, 如果失敗則重試端口號為8001,再次失敗重試端口號為8002,以此類推。
        $this->portOffset += 1;
        // 再次調用此程序
        return $this->handle();
    }
    // 返回狀態值
    return $status;
}

/**
 * 獲取完整的 server 命令.
 *
 * @return string
 */
protected function serverCommand()
{
    return sprintf('%s -S %s:%s %s',
        
        // 獲取PHP可執行命令的路徑
        ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false)),
        
        // 獲取需要綁定的host
        $this->host(),

        // 獲取需要綁定的端口
        $this->port(),

        // 對需要執行的參數進行轉義處理。這里的 server 就是我們之前說的路由文件,它在項目的根路徑下
        ProcessUtils::escapeArgument(base_path('server.php'))
    );
}

對上面的命令進行翻譯一下,實際上就是執行的

cd ./public
php -S 0.0.0.0:8000 ../server.php

note:

這里我們可以看到一個區別就是之前我自己寫的代碼,host 都是 localhost, 但是這里寫的是 0.0.0.0。這兩個有什么區別呢?

其實區別很簡單,比如我之前寫的 localhost 綁定的ip 是 127.0.0.1, 這個相當于一個回環地址,那么我們就只允許本機的IP進行訪問。而 0.0.0.0,則表示我們對ip不進行限制,所有的IP都可以進行訪問。

那我們接著再來看看項目根目錄下面的server.php:

/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylor@laravel.com>
 */

$uri = urldecode(
    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);

// 這個文件允許我們從內置 PHP web 服務器中模擬 Apache 的 "mod_rewrite" 功能.
// 這提供了一種測試 Laravel 應用程序的便捷方法,
// 而無需在此安裝"真正的" web 服務器軟件。
if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
    return false;
}

require_once __DIR__.'/public/index.php';

發現跟我之前寫的路由文件相同。沒錯,我就是從這里抄過來的。

基本上 Larvel 的實現方法就是這樣了。

Symfony

如果你在使用 Symfony 框架話,發現Symfony有一個組件叫做web-server-bundle,這個組件的作用跟Laravel相同,也是不借助web服務器,實現通過瀏覽器訪問應用程序。

基本的操作可以參考該頁面

我在這里主要說一下Symfony是如何實現的.

在Symfony中有一段代碼是這樣的:

public function start(WebServerConfig $config, $pidFile = null)
{
    // 獲取默認的PID文件位置
    $pidFile = $pidFile ?: $this->getDefaultPidFile();

    // 判斷是否在運行,如果運行則提示已經在監聽了
    if ($this->isRunning($pidFile)) {
        throw new \RuntimeException(sprintf('A process is already listening on http://%s.', $config->getAddress()));
    }

    // fork了一個子進程,如果成功,會有兩個進程進行同時執行下面的文件,父進程,也就是當前執行的進程會返回子進程的PID,而子進程則返回的PID為0,
    // 如果失敗,則子進程不會創建,并且父進程會返回的pid為-1。更多內容可查看 https://www.php.net/manual/zh/function.pcntl-fork.php
    $pid = pcntl_fork();

    // 表示fork進程失敗
    if ($pid < 0) {
        throw new \RuntimeException('Unable to start the server process.');
    }

    // 進入這個判斷,表示執行的是父進程,表示不用繼續向下執行
    if ($pid > 0) {
        return self::STARTED;
    }

    // 從此往后是子進程運行,首先通過 posix_setsid 變為守護進程,意思是使其脫離終端的管理,自立門戶,誰也沒辦法管理這個進程,除了PID。
    if (posix_setsid() < 0) {
        throw new \RuntimeException('Unable to set the child process as session leader.');
    }

    // 創建命令,命令類似Laravel,不過這里的路由文件跟Laravel類似。也是處理加載規則,并加載入口文件。具體的router.php 路徑為:
    // vendor\symfony\web-server-bundle/Resources/router.php
    // 下面是禁用輸出并且開始運行
    $process = $this->createServerProcess($config);
    $process->disableOutput();
    $process->start();

    // 判斷是否運行成功
    if (!$process->isRunning()) {
        throw new \RuntimeException('Unable to start the server process.');
    }

    // 寫入PID文件
    file_put_contents($pidFile, $config->getAddress());

    // 檢測PID文件,如果PID文件刪除了,那么進程就立即退出。
    while ($process->isRunning()) {
        if (!file_exists($pidFile)) {
            $process->stop();
        }

        sleep(1);
    }

    // 返回停止的狀態
    return self::STOPPED;
}

/**
 * 啟動PHP內置web服務器
 * @return Process The process
 */
private function createServerProcess(WebServerConfig $config)
{
    // 查找PHP的可執行程序
    $finder = new PhpExecutableFinder();
    if (false === $binary = $finder->find(false)) {
        throw new \RuntimeException('Unable to find the PHP binary.');
    }

    $xdebugArgs = ini_get('xdebug.profiler_enable_trigger') ? ['-dxdebug.profiler_enable_trigger=1'] : [];

    // 實例化PHP要執行的命令 php_path -dvariables_order=EGPCS -S 127.0.0.1:8000 vendor\symfony\web-server-bundle/Resources/router.php
    $process = new Process(array_merge([$binary], $finder->findArguments(), $xdebugArgs, ['-dvariables_order=EGPCS', '-S', $config->getAddress(), $config->getRouter()]));
    // 設置工作目錄
    $process->setWorkingDirectory($config->getDocumentRoot());
    // 設置超時時間
    $process->setTimeout(null);

    // 設置環境變量
    if (\in_array('APP_ENV', explode(',', getenv('SYMFONY_DOTENV_VARS')))) {
        $process->setEnv(['APP_ENV' => false]);
        $process->inheritEnvironmentVariables();
    }

    // 返回相關變量
    return $process;
}

我在上面的代碼中進行了注釋, 描述了Symfony是如何啟動的.

里面有一個問題就是使用pcntl_fork, 該擴展在Windows中是不受支持的. 所以 Symfony框架會提示使用php bin/console server:run命令運行程序.

未來展望

其實還有一個方式, 就是 Workman 是通過自身的實現的web服務器,它并沒有借助php -S命令。這一塊的代碼我還沒有吃透,并且我覺得這個也可以單獨拎幾章出來講。希望以后有這個機會。

總結

通過我們學習 PHP 命令實現web服務器訪問以及對 Laravel 和 Symfony 框架的分析, 讓我了解到在Windows的開發過程中,我們完全可以借助該方式來擺脫對web服務器的依賴.既能方便我們在Windows環境進行開發并且學習了PHP一個技巧.感覺挺好的.

看完上訴內容,你們對PHP內置web服務器應該怎么做大概了解了嗎?如果想了解更多,歡迎關注億速云行業資訊頻道哦!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女