生效了!!!!!!
参考的官网下面的例子,思路和我一开始说的差不多,就是获取pid,然后cmd去kill掉它。proc_open 创建的资源可以直接从父进程中获取执行的pid,所以在不借助其他中间媒介的情况下使用kill命令去kill程序。因为是proc_open是非阻塞的,所以也不会阻挡监控
https://www.php.net/manual/zh/function.proc-terminate.php
好像没生效
这里写了一个关于windows下 reload webman 代码的实现。其中文件变动部分参照webman安装后的部分。关于唤起和重启部分一开始也考虑的很多方案(cmd start
后台,文件通信,http与监控通信,)结果发现 popen 本身就是可以在 windows 下运行。并且 popen 运行模式也比较干净(1,程序中可直接关闭,2,主程退出后,子程序也同步退出,如果用 start \B
则会产生一个跟随窗口的程序。不会主动退出。并且cmd
start \b
比较难以直接获取)
近期有事情,暂不提pr了,有需要的同学可以参考以下狗啃版本(写的太乱了)。
打算将webman自带的监控该为两个,一个负责调度重启,一个负责文件监控。当前调度重启的代码和文件监控合在一起了。暂时不提pr,最近有点其他事情。
效果如下
<?php
require_once __DIR__ . '/vendor/autoload.php';
ini_set('display_errors', 'on');
error_reporting(E_ALL);
class M
{
/**
* @var array
*/
protected $_paths = [];
/**
* @var array
*/
protected $_extensions = [];
public function __construct($monitor_dir, $monitor_extensions)
{
$this->_extensions = $monitor_extensions;
exec('php -v ', $out, $var);
$this->checkMode = ($var === 0);
}
public function check_files_change($monitor_dir)
{
static $last_mtime;
if (!$last_mtime) {
$last_mtime = time();
}
clearstatcache();
if (!is_dir($monitor_dir)) {
if (!is_file($monitor_dir)) {
return;
}
$iterator = [new \SplFileInfo($monitor_dir)];
} else {
// recursive traversal directory
$dir_iterator = new \RecursiveDirectoryIterator($monitor_dir);
$iterator = new \RecursiveIteratorIterator($dir_iterator);
}
foreach ($iterator as $file) {
/** var SplFileInfo $file */
if (is_dir($file)) {
continue;
}
if ($file->getFilename() === 'log.php') {
// echo ($file->getFilename()) . PHP_EOL;
// echo date('Y-m-d H:i:s', $file->getMTime()) . PHP_EOL;
}
// check mtime
if ($last_mtime < $file->getMTime() && in_array($file->getExtension(), $this->_extensions, true)) {
$var = 0;
if ($this->checkMode) {
$phpBin = PHP_BINARY;
exec('"' . $phpBin . '" -l ' . $file, $out, $var);
var_dump($var, $out);
} else {
exec(PHP_BINARY . " -l " . $file, $out, $var);
}
var_dump($var);
if ($var) {
$last_mtime = $file->getMTime();
continue;
}
echo $file . " update and reload\n";
// send SIGUSR1 signal to master process for reload
$last_mtime = $file->getMTime();
return true;
}
}
return false;
}
}
//$str = <<<EOF
//wmic process where "name like '%php%' and commandline like '%[start.php]%' " get processid
//EOF;
//exec($run,$out,$var);
//exec("start /b php start.php",$out, $var);
//exec("taskkill /f /t /im php.exe");
//var_dump($out,$var);
$m = new M([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/process',
__DIR__ . '/resource',
__DIR__ . '/support',
], [
'php', 'html', 'htm', 'env'
]);
$phpBin = '"' . PHP_BINARY . '"';
// 用 cmd 后台启动web服务
//exec('start /c php start.php start ', $out, $var);
//exec("start /b php start.php",$out, $var);
//var_dump($out,$var);
$descriptorspec = [STDIN, STDOUT, STDOUT];
$run = 'php start.php start';
$resource = proc_open('php start.php start', $descriptorspec, $pipes);
//var_dump($pipes);
pln("run");
while (true) {
$r = $m->check_files_change(__DIR__ . '/');
// var_dump($r);
// exec("taskkill /f /t /im php.exe");
if ($r) {
// exec("taskkill /f /pid $pid", $out, $var);
// pln("kill");
// fwrite($pipes[0], 'die');
// proc_terminate($resource);
// proc_close($resource);
$pstatus = proc_get_status($resource);
$PID = $pstatus['pid'];
kill($PID); // instead of proc_terminate($resource);
// fclose($pipes[0]);
// fclose($pipes[1]);
// fclose($pipes[2]);
proc_close($resource);
$resource = proc_open('php start.php start', $descriptorspec, $pipes);
pln("restart");
}
sleep(1);
}
function pln($data)
{
echo $data . PHP_EOL;
}
function kill($pid){
return stripos(php_uname('s'), 'win')>-1 ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}
这应该之跑了一个不知道为啥windows下显示跑了俩web进程。不过qps是2w。同时可以reloading了。这样就可以使得 win下的php 在引用更多的依赖的前提下,高效开发了(比如引入node,py,go的文件监控来控制php)。
如果着急用的同学可以把下面代码直接放到webman的跟目录,在win下可以直接运行
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Webman\Config;
use Dotenv\Dotenv;
ini_set('display_errors', 'on');
error_reporting(E_ALL);
/**
* 1、win下可以使用 taskkill /F /T /PID $pid 精确杀死进程,如果通过执行程序名则会出现误杀现象
* 2、win下路由可能存在空格,所以可执行文件路径需要加入引号包裹,以防止被完整路径无法被cmd识别
* 3、win下可以使用 start http://xxx.xxx.xxx.xx:xx/ 可直接唤起默认浏览器
*/
if(!stristr(PHP_OS, 'WIN')){
echo "请在windows下使用本程序";
return;
}
if (class_exists('Dotenv\Dotenv') && file_exists(base_path().'/.env')) {
if (method_exists('Dotenv\Dotenv', 'createUnsafeImmutable')) {
Dotenv::createUnsafeImmutable(base_path())->load();
} else {
Dotenv::createMutable(base_path())->load();
}
}
Config::load(config_path(), ['route', 'container']);
if ($timezone = config('app.default_timezone')) {
date_default_timezone_set($timezone);
}
$config = config('server');
$pUrl = parse_url($config['listen']);
$localUrl = 'http://127.0.0.1'.(isset($pUrl['port'])?':'.$pUrl['port']:'');
class M
{
/**
* @var array
*/
protected $_paths = [];
/**
* @var array
*/
protected $_extensions = [];
public function __construct($monitor_dir, $monitor_extensions)
{
$this->_extensions = $monitor_extensions;
$this->_paths = $monitor_dir;
exec('php -v ', $out, $var);
$this->checkMode = ($var === 0);
}
public function checkAll()
{
foreach ($this->_paths as $path) {
if ($this->check_files_change($path)) {
return true;
}
}
return false;
}
public function check_files_change($monitor_dir)
{
static $last_mtime;
if (!$last_mtime) {
$last_mtime = time();
}
clearstatcache();
if (!is_dir($monitor_dir)) {
if (!is_file($monitor_dir)) {
return false;
}
$iterator = [new \SplFileInfo($monitor_dir)];
} else {
// recursive traversal directory
$dir_iterator = new \RecursiveDirectoryIterator($monitor_dir);
$iterator = new \RecursiveIteratorIterator($dir_iterator);
}
foreach ($iterator as $file) {
/** var SplFileInfo $file */
if (is_dir($file)) {
continue;
}
if ($file->getFilename() === 'log.php') {
// echo ($file->getFilename()) . PHP_EOL;
// echo date('Y-m-d H:i:s', $file->getMTime()) . PHP_EOL;
}
// check mtime
if ($last_mtime < $file->getMTime() && in_array($file->getExtension(), $this->_extensions, true)) {
$var = 0;
if ($this->checkMode) {
$phpBin = PHP_BINARY;
exec('"' . $phpBin . '" -l ' . $file, $out, $var);
} else {
exec(PHP_BINARY . " -l " . $file, $out, $var);
}
if ($var) {
$last_mtime = $file->getMTime();
continue;
}
echo $file . " update and reload\n";
// send SIGUSR1 signal to master process for reload
$last_mtime = $file->getMTime();
return true;
}
}
return false;
}
}
$m = new M([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
__DIR__ . '/process',
__DIR__ . '/resource',
__DIR__ . '/support',
], [
'php', 'html', 'htm', 'env'
]);
$phpBin = '"' . PHP_BINARY . '"';
$descriptorspec = [STDIN, STDOUT, STDOUT];
$run = 'php start.php start';
$resource = proc_open('php start.php start', $descriptorspec, $pipes);
exec("start $localUrl");
while (true) {
$r = $m->checkAll();
if ($r) {
$pStatus = proc_get_status($resource);
$PID = $pStatus['pid'];
kill($PID);
proc_close($resource);
$resource = proc_open('php start.php start', $descriptorspec, $pipes);
}
sleep(1);
}
function pln($data)
{
echo $data . PHP_EOL;
}
function kill($pid)
{
return stripos(php_uname('s'), 'win') > -1 ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}
很好很好,好用,提高开发效率啦!!!赞
非常棒的分享。
666
好用
参考楼主思路,webman从 1.2.3版本开始支持windows下热更新,并同时支持自定义进程。windows下启动方式为双击windows.bat或者运行php windows.php
https://github.com/walkor/webman/pull/316
关于windows上 php 安装路径带有空格导致文件检查失败,提了一个pr,win/ubuntu 双环境已做验证。看下需要合并一下吗。
已经合并,等下个版本发布