diff --git a/app/http/controllers/plugins/php83_controller.go b/app/http/controllers/plugins/php83_controller.go new file mode 100644 index 00000000..40e7fc46 --- /dev/null +++ b/app/http/controllers/plugins/php83_controller.go @@ -0,0 +1,274 @@ +package plugins + +import ( + "fmt" + "regexp" + "strings" + "time" + + "github.com/goravel/framework/contracts/http" + "github.com/goravel/framework/facades" + "github.com/imroc/req/v3" + + "panel/app/http/controllers" + "panel/app/models" + "panel/app/services" + "panel/pkg/tools" +) + +type Php83Controller struct { + setting services.Setting + task services.Task + version string +} + +func NewPhp83Controller() *Php83Controller { + return &Php83Controller{ + setting: services.NewSettingImpl(), + task: services.NewTaskImpl(), + version: "83", + } +} + +func (r *Php83Controller) Status(ctx http.Context) http.Response { + status, err := tools.ServiceStatus("php-fpm-" + r.version) + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"运行状态失败") + } + + return controllers.Success(ctx, status) +} + +func (r *Php83Controller) Reload(ctx http.Context) http.Response { + if err := tools.ServiceReload("php-fpm-" + r.version); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "重载PHP-"+r.version+"失败") + } + + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) Start(ctx http.Context) http.Response { + if err := tools.ServiceStart("php-fpm-" + r.version); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "启动PHP-"+r.version+"失败") + } + + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) Stop(ctx http.Context) http.Response { + if err := tools.ServiceStop("php-fpm-" + r.version); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "停止PHP-"+r.version+"失败") + } + + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) Restart(ctx http.Context) http.Response { + if err := tools.ServiceRestart("php-fpm-" + r.version); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "重启PHP-"+r.version+"失败") + } + + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) GetConfig(ctx http.Context) http.Response { + config, err := tools.Read("/www/server/php/" + r.version + "/etc/php.ini") + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, "获取PHP-"+r.version+"配置失败") + } + + return controllers.Success(ctx, config) +} + +func (r *Php83Controller) SaveConfig(ctx http.Context) http.Response { + config := ctx.Request().Input("config") + if err := tools.Write("/www/server/php/"+r.version+"/etc/php.ini", config, 0644); err != nil { + return nil + } + return r.Reload(ctx) +} + +func (r *Php83Controller) Load(ctx http.Context) http.Response { + client := req.C().SetTimeout(10 * time.Second) + resp, err := client.R().Get("http://127.0.0.1/phpfpm_status/" + r.version) + if err != nil || !resp.IsSuccessState() { + facades.Log().Info("获取PHP-" + r.version + "运行状态失败") + return controllers.Error(ctx, http.StatusInternalServerError, "[PHP-"+r.version+"] 获取运行状态失败") + } + + raw := resp.String() + dataKeys := []string{"应用池", "工作模式", "启动时间", "接受连接", "监听队列", "最大监听队列", "监听队列长度", "空闲进程数量", "活动进程数量", "总进程数量", "最大活跃进程数量", "达到进程上限次数", "慢请求"} + regexKeys := []string{"pool", "process manager", "start time", "accepted conn", "listen queue", "max listen queue", "listen queue len", "idle processes", "active processes", "total processes", "max active processes", "max children reached", "slow requests"} + + type Data struct { + Name string `json:"name"` + Value string `json:"value"` + } + data := make([]Data, len(dataKeys)) + for i := range dataKeys { + data[i].Name = dataKeys[i] + + r := regexp.MustCompile(fmt.Sprintf("%s:\\s+(.*)", regexKeys[i])) + match := r.FindStringSubmatch(raw) + + if len(match) > 1 { + data[i].Value = strings.TrimSpace(match[1]) + } + } + + return controllers.Success(ctx, data) +} + +func (r *Php83Controller) ErrorLog(ctx http.Context) http.Response { + log, err := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/php-fpm.log") + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, log) + } + + return controllers.Success(ctx, log) +} + +func (r *Php83Controller) SlowLog(ctx http.Context) http.Response { + log, err := tools.Exec("tail -n 100 /www/server/php/" + r.version + "/var/log/slow.log") + if err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, log) + } + + return controllers.Success(ctx, log) +} + +func (r *Php83Controller) ClearErrorLog(ctx http.Context) http.Response { + if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/php-fpm.log"); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, out) + } + + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) ClearSlowLog(ctx http.Context) http.Response { + if out, err := tools.Exec("echo '' > /www/server/php/" + r.version + "/var/log/slow.log"); err != nil { + return controllers.Error(ctx, http.StatusInternalServerError, out) + } + return controllers.Success(ctx, nil) +} + +func (r *Php83Controller) GetExtensionList(ctx http.Context) http.Response { + extensions := r.GetExtensions() + return controllers.Success(ctx, extensions) +} + +func (r *Php83Controller) InstallExtension(ctx http.Context) http.Response { + slug := ctx.Request().Input("slug") + if len(slug) == 0 { + return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") + } + + extensions := r.GetExtensions() + for _, item := range extensions { + if item.Slug == slug { + if item.Installed { + return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展已安装") + } + + var task models.Task + task.Name = "安装PHP-" + r.version + "扩展-" + item.Name + task.Status = models.TaskStatusWaiting + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' install ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Log = "/tmp/" + item.Slug + ".log" + if err := facades.Orm().Query().Create(&task); err != nil { + facades.Log().Info("[PHP-" + r.version + "] 创建安装拓展任务失败:" + err.Error()) + return controllers.ErrorSystem(ctx) + } + + r.task.Process(task.ID) + + return controllers.Success(ctx, true) + } + } + + return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") +} + +func (r *Php83Controller) UninstallExtension(ctx http.Context) http.Response { + slug := ctx.Request().Input("slug") + if len(slug) == 0 { + return controllers.Error(ctx, http.StatusUnprocessableEntity, "参数错误") + } + + extensions := r.GetExtensions() + for _, item := range extensions { + if item.Slug == slug { + if !item.Installed { + return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展未安装") + } + + var task models.Task + task.Name = "卸载PHP-" + r.version + "扩展-" + item.Name + task.Status = models.TaskStatusWaiting + task.Shell = `bash '/www/panel/scripts/php_extensions/` + item.Slug + `.sh' uninstall ` + r.version + ` >> /tmp/` + item.Slug + `.log 2>&1` + task.Log = "/tmp/" + item.Slug + ".log" + if err := facades.Orm().Query().Create(&task); err != nil { + facades.Log().Info("[PHP-" + r.version + "] 创建卸载拓展任务失败:" + err.Error()) + return controllers.ErrorSystem(ctx) + } + + r.task.Process(task.ID) + + return controllers.Success(ctx, true) + } + } + + return controllers.Error(ctx, http.StatusUnprocessableEntity, "扩展不存在") +} + +func (r *Php83Controller) GetExtensions() []PHPExtension { + var extensions []PHPExtension + extensions = append(extensions, PHPExtension{ + Name: "OPcache", + Slug: "Zend OPcache", + Description: "OPcache 通过将 PHP 脚本预编译的字节码存储到共享内存中来提升 PHP 的性能,存储预编译字节码可以省去每次加载和解析 PHP 脚本的开销。", + Installed: false, + }) + extensions = append(extensions, PHPExtension{ + Name: "PhpRedis", + Slug: "redis", + Description: "PhpRedis 是一个用C语言编写的PHP模块,用来连接并操作 Redis 数据库上的数据。", + Installed: false, + }) + extensions = append(extensions, PHPExtension{ + Name: "ImageMagick", + Slug: "imagick", + Description: "ImageMagick 是一个免费的创建、编辑、合成图片的软件。", + Installed: false, + }) + extensions = append(extensions, PHPExtension{ + Name: "Exif", + Slug: "exif", + Description: "通过 exif 扩展,你可以操作图像元数据。", + Installed: false, + }) + extensions = append(extensions, PHPExtension{ + Name: "pdo_pgsql", + Slug: "pdo_pgsql", + Description: "(需先安装PostgreSQL)pdo_pgsql 是一个驱动程序,它实现了 PHP 数据对象(PDO)接口以启用从 PHP 到 PostgreSQL 数据库的访问。", + Installed: false, + }) + + raw, err := tools.Exec("/www/server/php/" + r.version + "/bin/php -m") + if err != nil { + return extensions + } + + rawExtensionList := strings.Split(raw, "\n") + for _, item := range rawExtensionList { + if !strings.Contains(item, "[") && item != "" { + for i := range extensions { + if extensions[i].Slug == item { + extensions[i].Installed = true + } + } + } + } + + return extensions +} diff --git a/app/plugins/php81/php81.go b/app/plugins/php81/php81.go index 950183a3..f87ba573 100644 --- a/app/plugins/php81/php81.go +++ b/app/plugins/php81/php81.go @@ -4,7 +4,7 @@ var ( Name = "PHP-8.1" Description = "PHP 是世界上最好的语言!" Slug = "php81" - Version = "8.1.25" + Version = "8.1.26" Requires = []string{} Excludes = []string{} Install = `bash /www/panel/scripts/php/install.sh 81` diff --git a/app/plugins/php82/php82.go b/app/plugins/php82/php82.go index 5c62c1dc..931f02f7 100644 --- a/app/plugins/php82/php82.go +++ b/app/plugins/php82/php82.go @@ -4,7 +4,7 @@ var ( Name = "PHP-8.2" Description = "PHP 是世界上最好的语言!" Slug = "php82" - Version = "8.2.12" + Version = "8.2.13" Requires = []string{} Excludes = []string{} Install = `bash /www/panel/scripts/php/install.sh 82` diff --git a/app/plugins/php83/php83.go b/app/plugins/php83/php83.go new file mode 100644 index 00000000..df222ff3 --- /dev/null +++ b/app/plugins/php83/php83.go @@ -0,0 +1,13 @@ +package php83 + +var ( + Name = "PHP-8.3" + Description = "PHP 是世界上最好的语言!" + Slug = "php83" + Version = "8.3.0" + Requires = []string{} + Excludes = []string{} + Install = `bash /www/panel/scripts/php/install.sh 83` + Uninstall = `bash /www/panel/scripts/php/uninstall.sh 83` + Update = `bash /www/panel/scripts/php/install.sh 83` +) diff --git a/app/services/plugin.go b/app/services/plugin.go index 15e0fc20..2cc5c770 100644 --- a/app/services/plugin.go +++ b/app/services/plugin.go @@ -13,6 +13,7 @@ import ( "panel/app/plugins/php80" "panel/app/plugins/php81" "panel/app/plugins/php82" + "panel/app/plugins/php83" "panel/app/plugins/phpmyadmin" "panel/app/plugins/postgresql15" "panel/app/plugins/postgresql16" @@ -164,6 +165,17 @@ func (r *PluginImpl) All() []PanelPlugin { Uninstall: php82.Uninstall, Update: php82.Update, }) + p = append(p, PanelPlugin{ + Name: php83.Name, + Description: php83.Description, + Slug: php83.Slug, + Version: php83.Version, + Requires: php83.Requires, + Excludes: php83.Excludes, + Install: php83.Install, + Uninstall: php83.Uninstall, + Update: php83.Update, + }) p = append(p, PanelPlugin{ Name: phpmyadmin.Name, Description: phpmyadmin.Description, diff --git a/routes/plugin.go b/routes/plugin.go index bed104d0..4a14decc 100644 --- a/routes/plugin.go +++ b/routes/plugin.go @@ -210,6 +210,24 @@ func Plugin() { route.Post("extensions", php82Controller.InstallExtension) route.Delete("extensions", php82Controller.UninstallExtension) }) + r.Prefix("php83").Group(func(route route.Router) { + php83Controller := plugins.NewPhp83Controller() + route.Get("status", php83Controller.Status) + route.Post("reload", php83Controller.Reload) + route.Post("start", php83Controller.Start) + route.Post("stop", php83Controller.Stop) + route.Post("restart", php83Controller.Restart) + route.Get("load", php83Controller.Load) + route.Get("config", php83Controller.GetConfig) + route.Post("config", php83Controller.SaveConfig) + route.Get("errorLog", php83Controller.ErrorLog) + route.Get("slowLog", php83Controller.SlowLog) + route.Post("clearErrorLog", php83Controller.ClearErrorLog) + route.Post("clearSlowLog", php83Controller.ClearSlowLog) + route.Get("extensions", php83Controller.GetExtensionList) + route.Post("extensions", php83Controller.InstallExtension) + route.Delete("extensions", php83Controller.UninstallExtension) + }) r.Prefix("phpmyadmin").Group(func(route route.Router) { phpMyAdminController := plugins.NewPhpMyAdminController() route.Get("info", phpMyAdminController.Info) diff --git a/scripts/php/install.sh b/scripts/php/install.sh index f5ba7270..516cbce6 100644 --- a/scripts/php/install.sh +++ b/scripts/php/install.sh @@ -65,9 +65,11 @@ if [ "${phpVersion}" == "74" ]; then elif [ "${phpVersion}" == "80" ]; then phpVersionCode="8.0.30" elif [ "${phpVersion}" == "81" ]; then - phpVersionCode="8.1.25" + phpVersionCode="8.1.26" elif [ "${phpVersion}" == "82" ]; then - phpVersionCode="8.2.12" + phpVersionCode="8.2.13" +elif [ "${phpVersion}" == "83" ]; then + phpVersionCode="8.3.0" else echo -e $HR echo "错误:PHP-${phpVersion}不支持,请检查版本号是否正确。" @@ -119,8 +121,8 @@ fi # 配置 cd src -if [ "${phpVersion}" == "81" ] || [ "${phpVersion}" == "82" ]; then - ./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp --with-avif +if [ "${phpVersion}" == "81" ] || [ "${phpVersion}" == "82" ] || [ "${phpVersion}" == "83" ]; then + ./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp --with-avif else ./configure --prefix=${phpPath} --with-config-file-path=${phpPath}/etc --enable-fpm --with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-freetype --with-jpeg --with-zlib --with-libxml-dir=/usr --enable-xml --disable-rpath --enable-bcmath --enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --enable-pcntl --enable-ftp --enable-gd --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --with-gettext --enable-fileinfo --enable-opcache --with-sodium --with-webp fi