@@ -2950,6 +2950,108 @@ func (s *Server) handleSetPHPDefaultConfig(ctx context.Context, params json.RawM
29502950 return map [string ]string {"status" : "ok" }, nil
29512951}
29522952
2953+ func runAptCommand (args ... string ) ([]byte , error ) {
2954+ cmd := exec .Command ("apt-get" , args ... )
2955+ cmd .Env = append (os .Environ (), "DEBIAN_FRONTEND=noninteractive" , "NEEDRESTART_SUSPEND=1" )
2956+ return cmd .CombinedOutput ()
2957+ }
2958+
2959+ func phpPackageExists (pkg string ) bool {
2960+ return exec .Command ("apt-cache" , "show" , pkg ).Run () == nil
2961+ }
2962+
2963+ func resolveInstallablePHPPackages (version string ) ([]string , error ) {
2964+ base := []string {
2965+ "php" + version ,
2966+ "php" + version + "-fpm" ,
2967+ }
2968+ modules := []string {
2969+ "bcmath" , "bz2" , "cli" , "common" , "curl" , "gd" , "gmp" , "igbinary" , "imagick" ,
2970+ "imap" , "intl" , "mbstring" , "mysql" , "opcache" , "readline" , "redis" , "soap" ,
2971+ "sqlite3" , "xml" , "xmlrpc" , "zip" ,
2972+ }
2973+ pkgs := make ([]string , 0 , len (base )+ len (modules ))
2974+ for _ , pkg := range base {
2975+ if phpPackageExists (pkg ) {
2976+ pkgs = append (pkgs , pkg )
2977+ }
2978+ }
2979+ hasFPM := false
2980+ for _ , p := range pkgs {
2981+ if p == "php" + version + "-fpm" {
2982+ hasFPM = true
2983+ break
2984+ }
2985+ }
2986+ if ! hasFPM {
2987+ return nil , fmt .Errorf ("php%s-fpm is not available in apt repositories" , version )
2988+ }
2989+ for _ , m := range modules {
2990+ pkg := "php" + version + "-" + m
2991+ if phpPackageExists (pkg ) {
2992+ pkgs = append (pkgs , pkg )
2993+ }
2994+ }
2995+ return pkgs , nil
2996+ }
2997+
2998+ func (s * Server ) startOrRestartPHPFPM (version string ) error {
2999+ service := "php" + version + "-fpm"
3000+ if s .hasSystemd () {
3001+ _ = s .runSystemctl ("enable" , service )
3002+ return s .serviceReloadOrRestart (service )
3003+ }
3004+ if output , err := exec .Command ("service" , service , "restart" ).CombinedOutput (); err == nil {
3005+ return nil
3006+ } else {
3007+ return fmt .Errorf ("failed to restart %s: %w: %s" , service , err , strings .TrimSpace (string (output )))
3008+ }
3009+ }
3010+
3011+ func (s * Server ) handleInstallPHPVersion (ctx context.Context , params json.RawMessage ) (any , error ) {
3012+ var req PHPVersionInstallRequest
3013+ if err := json .Unmarshal (params , & req ); err != nil {
3014+ return nil , fmt .Errorf ("invalid params: %w" , err )
3015+ }
3016+ version := normalizePHPVersion (req .Version )
3017+ if version != strings .TrimSpace (req .Version ) {
3018+ return nil , fmt .Errorf ("unsupported php version %q" , req .Version )
3019+ }
3020+
3021+ for _ , v := range detectAvailablePHPVersions () {
3022+ if v == version {
3023+ return map [string ]string {"status" : "ok" , "message" : "php version already installed" }, nil
3024+ }
3025+ }
3026+
3027+ if output , err := runAptCommand ("update" , "-qq" ); err != nil {
3028+ return nil , fmt .Errorf ("failed to update apt indexes: %w: %s" , err , strings .TrimSpace (string (output )))
3029+ }
3030+ pkgs , err := resolveInstallablePHPPackages (version )
3031+ if err != nil {
3032+ return nil , err
3033+ }
3034+ args := append ([]string {"install" , "-y" , "-qq" }, pkgs ... )
3035+ if output , err := runAptCommand (args ... ); err != nil {
3036+ return nil , fmt .Errorf ("failed to install php%s packages: %w: %s" , version , err , strings .TrimSpace (string (output )))
3037+ }
3038+ if err := s .startOrRestartPHPFPM (version ); err != nil {
3039+ return nil , err
3040+ }
3041+ if err := s .generateCaddyfile (); err != nil {
3042+ return nil , fmt .Errorf ("failed to regenerate Caddyfile after php install: %w" , err )
3043+ }
3044+ if ! s .isCaddyRunning () {
3045+ if err := s .startCaddy (); err != nil {
3046+ return nil , fmt .Errorf ("failed to start Caddy after php install: %w" , err )
3047+ }
3048+ } else if err := s .reloadCaddy (); err != nil {
3049+ return nil , fmt .Errorf ("failed to reload Caddy after php install: %w" , err )
3050+ }
3051+ slog .Info ("installed php runtime on demand" , "version" , version )
3052+ return map [string ]string {"status" : "ok" , "message" : "php version installed" }, nil
3053+ }
3054+
29533055func (s * Server ) handleGetMySQLConfig (ctx context.Context , params json.RawMessage ) (any , error ) {
29543056 cfg := & MySQLConfig {BufferPoolMB : 128 , MaxConnections : 30 , PerfSchema : false }
29553057
0 commit comments