前言 PHP从5.4开始,就提供了一个内置的web服务器。
这个主要是用来做本地的开发用的。不能用于线上环境。现在我就介绍一下这个工具如何使用。
基础应用 首先我们假定项目目录是/home/baoguoxiao/www/php/demo
,外界可访问的目录是/home/baoguoxiao/www/php/demo/public
。然后访问的端口是8000
,入口文件是index.php
和index.html
。那么我们可以执行如下命令:
1 2 cd /home/baoguoxiao/www/php/demo/publicphp -S localhost:8000
然后这个时候就可以正常访问了。
那么现在有个问题,就是难道每次必须要进入public
文件夹才能启动web服务器吗,其实我们可以指定根目录的,那么可以使用如下命令:
1 2 cd /home/baoguoxiao/www/php/demophp -S localhost:8000 -t public/
那么现在有一个问题就是说,如果我们使用了单入口,而且还是用了PATHINFO模式。那么上面的可能就有问题了。
对此,我们可以使用如下方案:
1 2 cd /home/baoguoxiao/www/php/demophp -S localhost:8000 router.php
router.php 文件的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $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服务器实现外部访问的命令。实现的命令是:
我们可以看一下相关代码:
具体的文件路径为:vendor/laravel/framework/src/Illuminate/Foundation/Console/ServeCommand.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public function handle () { chdir(public_path()); $this ->line("<info>Laravel development server started:</info> <http://{$this->host()}:{$this->port()}>" ); passthru($this ->serverCommand(), $status); if ($status && $this ->canTryAnotherPort()) { $this ->portOffset += 1 ; return $this ->handle(); } return $status; } protected function serverCommand () { return sprintf('%s -S %s:%s %s' , ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false )), $this ->host(), $this ->port(), ProcessUtils::escapeArgument(base_path('server.php' )) ); }
对上面的命令进行翻译一下,实际上就是执行的
1 2 cd ./publicphp -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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $uri = urldecode( parse_url($_SERVER['REQUEST_URI' ], PHP_URL_PATH) ); 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中有一段代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public function start (WebServerConfig $config, $pidFile = null) { $pidFile = $pidFile ?: $this ->getDefaultPidFile(); if ($this ->isRunning($pidFile)) { throw new \RuntimeException(sprintf('A process is already listening on http://%s.' , $config->getAddress())); } $pid = pcntl_fork(); if ($pid < 0 ) { throw new \RuntimeException('Unable to start the server process.' ); } if ($pid > 0 ) { return self ::STARTED; } if (posix_setsid() < 0 ) { throw new \RuntimeException('Unable to set the child process as session leader.' ); } $process = $this ->createServerProcess($config); $process->disableOutput(); $process->start(); if (!$process->isRunning()) { throw new \RuntimeException('Unable to start the server process.' ); } file_put_contents($pidFile, $config->getAddress()); while ($process->isRunning()) { if (!file_exists($pidFile)) { $process->stop(); } sleep(1 ); } return self ::STOPPED; } private function createServerProcess (WebServerConfig $config) { $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' ] : []; $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一个技巧.感觉挺好的.
大家如果对此有什么疑问可以评论进行交流.
参考