PHP built-in web server

步履不停
Release: 2023-04-06 21:16:01
Original
7829 people have browsed it

PHP built-in web server

Preface

PHP has provided a built-in web server since 5.4.

This is mainly used for local development. Cannot be used in online environments. Now I will introduce how to use this tool.

Basic Application

First we assume that the project directory is /home/baoguoxiao/www/php/demo, and the directory accessible to the outside world is /home/baoguoxiao /www/php/demo/public. Then the access port is 8000, and the entry files are index.php and index.html. Then we can execute the following command:

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

Then we can access it normally at this time.

So now there is a question, is it necessary to enter the public folder every time to start the web server? In fact, we can specify the root directory, then we can use the following command:

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

So now there is a problem, that is, if we use a single entry, and still use the PATHINFO mode. Then there may be a problem with the above.

For this, we can use the following solution:

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

router.php file code

/**
 * 对URL进行解析,并获取请求的文件名
 */$uri = urldecode(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH));/**
 * 判断是否存在该文件,如果不存在,则直接继续加载入口文件
 */if ($uri !== "/" && file_exists(__DIR__ . "$uri")) {    return false;
}/**
 * 加载入口文件
 */require_once "./index.php";
Copy after login

Through this routing file, we can support currently commonly used development situations .

Frame Reference

The above method is our own implementation, then we can also look at the implementation methods of related well-known frameworks.

Such as Laravel and Symfony.

Laravel

In the Installation section in Laravel, a command is introduced that can use the PHP built-in web server to achieve external access. The implemented command is:

php artisan serve
Copy after login

We can take a look at the relevant code:

The specific file path is: 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'))
    );
}
Copy after login

Translate the above command, it is actually executed

cd ./public
php -S 0.0.0.0:8000 ../server.php
Copy after login

note:

Here we can see a difference, which is the code I wrote before. , hosts are all localhost, but what is written here is 0.0.0.0. What's the difference between these two?

In fact, the difference is very simple. For example, the IP bound to localhost I wrote before is 127.0.0.1. This is equivalent to a loopback address, so we only allow the local IP to access. And 0.0.0.0 means that we have no restrictions on IP and all IPs can be accessed.

Then let’s take a look at 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';
Copy after login

under the project root directory and find that it is the same as the routing file I wrote before. Yes, I copied it from here.

Basically this is how Larvel is implemented.

Symfony

If you are using the Symfony framework, you will find that Symfony has a component called web-server-bundle. This component has the same function as Laravel, without any help. The web server enables access to applications through a browser.

For basic operations, please refer to This page

I will mainly talk about how Symfony is implemented here.

There is a piece of code in Symfony which is Like this:

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(&#39;Unable to start the server process.&#39;);
    }

    // 进入这个判断,表示执行的是父进程,表示不用继续向下执行
    if ($pid > 0) {
        return self::STARTED;
    }

    // 从此往后是子进程运行,首先通过 posix_setsid 变为守护进程,意思是使其脱离终端的管理,自立门户,谁也没办法管理这个进程,除了PID。
    if (posix_setsid() < 0) {
        throw new \RuntimeException(&#39;Unable to set the child process as session leader.&#39;);
    }

    // 创建命令,命令类似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;
}
Copy after login

I commented in the above code to describe how Symfony is started.

There is a problem in usingpcntl_fork , this extension is not supported in Windows. Therefore, the Symfony framework will prompt you to use the php bin/console server:run command to run the program.

Future Outlook

In fact, there is another way, that is, Workman is a web server implemented by itself, and it does not rely on the php -S command. I haven't fully understood this part of the code, and I think this can be discussed in a few separate chapters. Hope to have this opportunity in the future.

Summary

Through our learning of PHP commands to achieve web server access and the analysis of Laravel and Symfony frameworks, I learned that in the development process of Windows, we can completely use this method to get rid of The dependence on the web server not only facilitates our development in the Windows environment but also allows us to learn a skill in PHP. It feels good.

If you have any questions about this, you can comment and communicate.

For more PHP-related technical articles, please visit the PHP Tutorial column to learn!

The above is the detailed content of PHP built-in web server. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!