php windows 集成环境_自做Windows上界面美观的PHP集成环境软件

界面展示一下:

e82e31a941f822c897022415f9c30af1.png

原因

平常工作中用Nginx比较多,网上虽然也有wnmp集成环境,但是感觉界面不好看,用起来不舒服,所有决定自己做一个吧。

特点

免安装,界面简洁

原料

软件用的是C#,GUI框架是WPF(这个做出来更好看一点),先去官网下载PHP,用的是NTS版本的(因为这里PHP是以CGi的形式跑的),再去下载Windows版的Nginx和Mysql

代码

基类(BaseProgram.cs)

public abstract class BaseProgram: INotifyPropertyChanged

{

///

/// exe 执行文件位置

///

public string exeFile { get; set; }

///

/// 进程名称

///

public string procName { get; set; }

///

/// 进程别名,用来在日志窗口显示

///

public string programName { get; set; }

///

/// 进程工作目录(Nginx需要这个参数)

///

public string workingDir { get; set; }

///

/// 进程日志前缀

///

public Log.LogSection progLogSection { get; set; }

///

/// 进程开启的参数

///

public string startArgs { get; set; }

///

/// 关闭进程参数

///

public string stopArgs { get; set; }

///

///

///

public bool killStop { get; set; }

///

/// 进程配置目录

///

public string confDir { get; set; }

///

/// 进程日志目录

///

public string logDir { get; set; }

///

/// 进程异常退出的记录信息

///

protected string errOutput = "";

public Process ps = new Process();

public event PropertyChangedEventHandler PropertyChanged;

// 是否在运行

private bool running = false;

public bool Running

{

get

{

return this.running;

}

set

{

this.running = value;

if(PropertyChanged != null)

{

PropertyChanged(this, new PropertyChangedEventArgs("Running"));

}

}

}

///

/// 设置状态

///

public void SetStatus()

{

if (IsRunning())

{

this.Running = true;

}

else

{

this.Running = false;

}

}

///

/// 启动进程

///

///

///

///

public void StartProcess(string exe, string args, EventHandler exitedHandler = null)

{

ps = new Process();

ps.StartInfo.FileName = exe;

ps.StartInfo.Arguments = args;

ps.StartInfo.UseShellExecute = false;

ps.StartInfo.RedirectStandardOutput = true;

ps.StartInfo.RedirectStandardError = true;

ps.StartInfo.WorkingDirectory = workingDir;

ps.StartInfo.CreateNoWindow = true;

ps.Start();

// ErrorDataReceived event signals each time the process writes a line

// to the redirected StandardError stream

ps.ErrorDataReceived += (sender, e) => {

errOutput += e.Data;

};

ps.Exited += exitedHandler != null ? exitedHandler : (sender, e) => {

if (!String.IsNullOrEmpty(errOutput))

{

Log.wnmp_log_error("Failed: " + errOutput, progLogSection);

errOutput = "";

}

};

ps.EnableRaisingEvents = true;

ps.BeginOutputReadLine();

ps.BeginErrorReadLine();

}

public virtual void Start()

{

if(IsRunning())

{

return;

}

try {

StartProcess(exeFile, startArgs);

Log.wnmp_log_notice("Started " + programName, progLogSection);

} catch (Exception ex) {

Log.wnmp_log_error("Start: " + ex.Message, progLogSection);

}

}

public virtual void Stop()

{

if(!IsRunning())

{

return;

}

if (killStop == false)

StartProcess(exeFile, stopArgs);

var processes = Process.GetProcessesByName(procName);

foreach (var process in processes) {

process.Kill();

}

Log.wnmp_log_notice("Stopped " + programName, progLogSection);

}

///

/// 杀死进程

///

///

protected void KillProcess(string procName)

{

var processes = Process.GetProcessesByName(procName);

foreach (var process in processes)

{

process.Kill();

}

}

public void Restart()

{

this.Stop();

this.Start();

Log.wnmp_log_notice("Restarted " + programName, progLogSection);

}

///

/// 判断程序是否运行

///

///

public virtual bool IsRunning()

{

var processes = Process.GetProcessesByName(procName);

return (processes.Length != 0);

}

///

/// 设置初始参数

///

public abstract void Setup();

}

开启mysql代码(Mysql.cs)

class MysqlProgram : BaseProgram

{

private readonly ServiceController mysqlController = new ServiceController();

public const string ServiceName = "mysql-salamander";

public MysqlProgram()

{

mysqlController.MachineName = Environment.MachineName;

mysqlController.ServiceName = ServiceName;

}

///

/// 移除服务

///

public void RemoveService()

{

StartProcess("cmd.exe", stopArgs);

}

///

/// 安装服务

///

public void InstallService()

{

StartProcess(exeFile, startArgs);

}

///

/// 获取my.ini中mysql的端口

///

///

private static int GetIniMysqlListenPort()

{

string path = Common.APP_STARTUP_PATH + Common.Settings.MysqlDirName.Value + "/my.ini";

Regex regPort = new Regex(@"^\s*port\s*=\s*(\d+)");

Regex regMysqldSec = new Regex(@"^\s*\[mysqld\]");

using (var sr = new StreamReader(path))

{

bool isStart = false;// 是否找到了"[mysqld]"

string str = null;

while ((str = sr.ReadLine()) != null)

{

if (isStart && regPort.IsMatch(str))

{

MatchCollection matches = regPort.Matches(str);

foreach (Match match in matches)

{

GroupCollection groups = match.Groups;

if (groups.Count > 1)

{

try

{

return Int32.Parse(groups[1].Value);

}

catch

{

return -1;

}

}

}

}

// [mysqld]段开始

if (regMysqldSec.IsMatch(str))

{

isStart = true;

}

}

}

return -1;

}

///

/// 服务是否存在

///

///

public bool ServiceExists()

{

ServiceController[] services = ServiceController.GetServices();

foreach (var service in services) {

if (service.ServiceName == ServiceName)

return true;

}

return false;

}

public override void Start()

{

if (IsRunning())

return;

try {

if (!File.Exists(Common.APP_STARTUP_PATH + Common.Settings.MysqlDirName.Value + "/my.ini"))

{

Log.wnmp_log_error("my.ini file not exist", progLogSection);

return;

}

int port = GetIniMysqlListenPort();// -1表示提取出错

if (port != -1 && PortScanHelper.IsPortInUseByTCP(port))

{

Log.wnmp_log_error("Port " + port + " is used", progLogSection);

return;

}

mysqlController.Start();

Log.wnmp_log_notice("Started " + programName, progLogSection);

} catch (Exception ex) {

Log.wnmp_log_error("Start(): " + ex.Message, progLogSection);

}

}

public override void Stop()

{

if(!IsRunning())

{

return;

}

try {

mysqlController.Stop();

mysqlController.WaitForStatus(ServiceControllerStatus.Stopped);

Log.wnmp_log_notice("Stopped " + programName, progLogSection);

} catch (Exception ex) {

Log.wnmp_log_error("Stop(): " + ex.Message, progLogSection);

}

}

///

/// 通过ServiceController判断服务是否在运行

///

///

public override bool IsRunning()

{

mysqlController.Refresh();

try

{

return mysqlController.Status == ServiceControllerStatus.Running;

}

catch

{

return false;

}

}

public override void Setup()

{

this.exeFile = Common.APP_STARTUP_PATH + String.Format("{0}/bin/mysqld.exe", Common.Settings.MysqlDirName.Value);

this.procName = "mysqld";

this.programName = "MySQL";

this.workingDir = Common.APP_STARTUP_PATH + Common.Settings.MysqlDirName.Value;

this.progLogSection = Log.LogSection.WNMP_MARIADB;

this.startArgs = "--install-manual " + MysqlProgram.ServiceName + " --defaults-file=\"" +

Common.APP_STARTUP_PATH + String.Format("\\{0}\\my.ini\"", Common.Settings.MysqlDirName.Value);

this.stopArgs = "/c sc delete " + MysqlProgram.ServiceName;

this.killStop = true;

this.confDir = "/mysql/";

this.logDir = "/mysql/data/";

}

///

/// 打开MySQL Client命令行

///

public static void OpenMySQLClientCmd()

{

Process ps = new Process();

ps.StartInfo.FileName = Common.APP_STARTUP_PATH + String.Format("{0}/bin/mysql.exe", Common.Settings.MysqlDirName.Value);

ps.StartInfo.Arguments = String.Format("-u{0} -p{1}", Common.Settings.MysqlClientUser.Value, Common.Settings.MysqlClientUserPass.Value);

ps.StartInfo.UseShellExecute = false;

ps.StartInfo.CreateNoWindow = false;

ps.Start();

}

}

开启php代码(PHP.cs)

class PHPProgram : BaseProgram

{

private const string PHP_CGI_NAME = "php-cgi";

private const string PHP_MAX_REQUEST = "PHP_FCGI_MAX_REQUESTS";

private Object locker = new Object();

private uint FCGI_NUM = 0;

private bool watchPHPFCGI = true;

private Thread watchThread;

private void DecreaseFCGINum()

{

lock (locker)

{

FCGI_NUM--;

}

}

private void IncreaseFCGINum()

{

lock (locker)

{

FCGI_NUM++;

}

}

public PHPProgram()

{

if (Environment.GetEnvironmentVariable(PHP_MAX_REQUEST) == null)

Environment.SetEnvironmentVariable(PHP_MAX_REQUEST, "300");

}

public override void Start()

{

if(!IsRunning() && PortScanHelper.IsPortInUseByTCP(Common.Settings.PHP_Port.Value))

{

Log.wnmp_log_error("Port " + Common.Settings.PHP_Port.Value + " is used", progLogSection);

}

else if(!IsRunning())

{

for (int i = 0; i < Common.Settings.PHPProcesses.Value; i++)

{

StartProcess(this.exeFile, this.startArgs, (s, args) =>

{

DecreaseFCGINum();

});

IncreaseFCGINum();

}

WatchPHPFCGINum();

Log.wnmp_log_notice("Started " + programName, progLogSection);

}

}

///

/// 异步查看php-cgi数量

///

///

///

private void WatchPHPFCGINum()

{

watchPHPFCGI = true;

watchThread = new Thread(() =>

{

while (watchPHPFCGI)

{

uint delta = Common.Settings.PHPProcesses.Value - FCGI_NUM;

for (int i = 0; i < delta; i++)

{

StartProcess(this.exeFile, this.startArgs, (s, args) =>

{

DecreaseFCGINum();

});

IncreaseFCGINum();

Console.WriteLine("restart a php-cgi");

}

}

});

watchThread.Start();

}

public void StopWatchPHPFCGINum()

{

watchPHPFCGI = false;

}

public override void Stop()

{

if (!IsRunning())

{

return;

}

StopWatchPHPFCGINum();

KillProcess(PHP_CGI_NAME);

Log.wnmp_log_notice("Stopped " + programName, progLogSection);

}

public override void Setup()

{

string phpDirPath = Common.APP_STARTUP_PATH + Common.Settings.PHPDirName.Value;

this.exeFile = string.Format("{0}/php-cgi.exe", phpDirPath);

this.procName = PHP_CGI_NAME;

this.programName = "PHP";

this.workingDir = phpDirPath;

this.progLogSection = Log.LogSection.WNMP_PHP;

this.startArgs = String.Format("-b 127.0.0.1:{0} -c {1}/php.ini", Common.Settings.PHP_Port.Value, phpDirPath);

this.killStop = true;

this.confDir = "/php/";

this.logDir = "/php/logs/";

}

}

开启nginx(Nginx.cs)

这里要注意WorkingDirectory属性设置成nginx目录

class NginxProgram : BaseProgram

{

public override void Setup()

{

this.exeFile = Common.APP_STARTUP_PATH + String.Format("{0}/nginx.exe", Common.Settings.NginxDirName.Value);

this.procName = "nginx";

this.programName = "Nginx";

this.workingDir = Common.APP_STARTUP_PATH + Common.Settings.NginxDirName.Value;

this.progLogSection = Log.LogSection.WNMP_NGINX;

this.startArgs = "";

this.stopArgs = "-s stop";

this.killStop = false;

this.confDir = "/conf/";

this.logDir = "/logs/";

}

///

/// 打开命令行

///

public static void OpenNginxtCmd()

{

Process ps = new Process();

ps.StartInfo.FileName = "cmd.exe";

ps.StartInfo.Arguments = "";

ps.StartInfo.UseShellExecute = false;

ps.StartInfo.CreateNoWindow = false;

ps.StartInfo.WorkingDirectory = Common.APP_STARTUP_PATH + Common.Settings.NginxDirName.Value;

ps.Start();

}

}

其他功能

配置nginx,php,mysql目录名,管理php扩展

934a1119ade2ed7f66501d04d9fb5239.png

28386fa172f8e07465c07150a70b2bea.png

编程语言面板

3edf3cb221c273e584ef74c90b143dc4.png

注意

php 版本为7.1.12 64位版本,需要MSVC14 (Visual C++ 2015)运行库支持,下载:https://download.microsoft.co...

其实用户完全可以选择自己想要的php版本,放到集成环境的目录下即可(改一下配置,重启)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值