生效了?。。。。?!
參考的官網(wǎng)下面的例子,思路和我一開始說的差不多,就是獲取pid,然后cmd去kill掉它。proc_open 創(chuàng)建的資源可以直接從父進(jìn)程中獲取執(zhí)行的pid,所以在不借助其他中間媒介的情況下使用kill命令去kill程序。因為是proc_open是非阻塞的,所以也不會阻擋監(jiān)控
https://www.php.net/manual/zh/function.proc-terminate.php
好像沒生效
這里寫了一個關(guān)于windows下 reload webman 代碼的實現(xiàn)。其中文件變動部分參照webman安裝后的部分。關(guān)于喚起和重啟部分一開始也考慮的很多方案(cmd start
后臺,文件通信,http與監(jiān)控通信,)結(jié)果發(fā)現(xiàn) popen 本身就是可以在 windows 下運(yùn)行。并且 popen 運(yùn)行模式也比較干凈(1,程序中可直接關(guān)閉,2,主程退出后,子程序也同步退出,如果用 start \B
則會產(chǎn)生一個跟隨窗口的程序。不會主動退出。并且cmd
start \b
比較難以直接獲?。?br />
近期有事情,暫不提pr了,有需要的同學(xué)可以參考以下狗啃版本(寫的太亂了)。
打算將webman自帶的監(jiān)控該為兩個,一個負(fù)責(zé)調(diào)度重啟,一個負(fù)責(zé)文件監(jiān)控。當(dāng)前調(diào)度重啟的代碼和文件監(jiān)控合在一起了。暫時不提pr,最近有點(diǎn)其他事情。
效果如下
<?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服務(wù)
//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");
}
這應(yīng)該之跑了一個不知道為啥windows下顯示跑了倆web進(jìn)程。不過qps是2w。同時可以reloading了。這樣就可以使得 win下的php 在引用更多的依賴的前提下,高效開發(fā)了(比如引入node,py,go的文件監(jiān)控來控制php)。
如果著急用的同學(xué)可以把下面代碼直接放到webman的跟目錄,在win下可以直接運(yùn)行
<?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 精確殺死進(jìn)程,如果通過執(zhí)行程序名則會出現(xiàn)誤殺現(xiàn)象
* 2、win下路由可能存在空格,所以可執(zhí)行文件路徑需要加入引號包裹,以防止被完整路徑無法被cmd識別
* 3、win下可以使用 start http://xxx.xxx.xxx.xx:xx/ 可直接喚起默認(rèn)瀏覽器
*/
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");
}
參考樓主思路,webman從 1.2.3版本開始支持windows下熱更新,并同時支持自定義進(jìn)程。windows下啟動方式為雙擊windows.bat或者運(yùn)行php windows.php
https://github.com/walkor/webman/pull/316
關(guān)于windows上 php 安裝路徑帶有空格導(dǎo)致文件檢查失敗,提了一個pr,win/ubuntu 雙環(huán)境已做驗證??聪滦枰喜⒁幌聠帷?/p>