1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 20:10:08 +02:00

♻️ Separate some of the PHP config logic from the app

This commit is contained in:
2021-12-21 15:30:50 +01:00
parent 2dbf775ad6
commit a6387e96e7
18 changed files with 211 additions and 153 deletions

View File

@ -24,6 +24,9 @@
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; }; 54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; };
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; };
C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; };
C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; };
C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */; };
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; }; C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
@ -176,6 +179,7 @@
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; }; 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; }; C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; }; C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; };
C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; }; C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; }; C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -321,6 +325,16 @@
path = IAP; path = IAP;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
isa = PBXGroup;
children = (
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
C43A8A1F25D9D1D700591B77 /* brew.json */,
C4F780A725D80AE8000DBC97 /* php.ini */,
);
path = "Test Files";
sourceTree = "<group>";
};
C415D3D72770F341005EF286 /* phpmon-cli */ = { C415D3D72770F341005EF286 /* phpmon-cli */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -494,6 +508,7 @@
C4B5853D2770FE3900DA4FBE /* Command.swift */, C4B5853D2770FE3900DA4FBE /* Command.swift */,
C4B5853B2770FE3900DA4FBE /* Paths.swift */, C4B5853B2770FE3900DA4FBE /* Paths.swift */,
C4B5853C2770FE3900DA4FBE /* Shell.swift */, C4B5853C2770FE3900DA4FBE /* Shell.swift */,
C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */,
); );
path = "phpmon-common"; path = "phpmon-common";
sourceTree = "<group>"; sourceTree = "<group>";
@ -527,10 +542,8 @@
C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = { C4F7807A25D7F84B000DBC97 /* phpmon-tests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
C43A8A1F25D9D1D700591B77 /* brew.json */,
C4F780A725D80AE8000DBC97 /* php.ini */,
C4F7807D25D7F84B000DBC97 /* Info.plist */, C4F7807D25D7F84B000DBC97 /* Info.plist */,
C40C7F1C27720E1400DDDCDC /* Test Files */,
C4F7809B25D80344000DBC97 /* CommandTest.swift */, C4F7809B25D80344000DBC97 /* CommandTest.swift */,
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */, C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */, C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
@ -702,6 +715,7 @@
C4B585432770FE3900DA4FBE /* Shell.swift in Sources */, C4B585432770FE3900DA4FBE /* Shell.swift in Sources */,
C4B585462770FE3900DA4FBE /* Command.swift in Sources */, C4B585462770FE3900DA4FBE /* Command.swift in Sources */,
C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */, C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */,
C40C7F202772136000DDDCDC /* PhpSwitcher.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -750,6 +764,7 @@
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */, C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */, C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C40C7F1E2772136000DDDCDC /* PhpSwitcher.swift in Sources */,
C476FF9822B0DD830098105B /* Alert.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */,
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */, C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
@ -820,6 +835,7 @@
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */, C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */, C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */, C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
C40C7F1F2772136000DDDCDC /* PhpSwitcher.swift in Sources */,
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */, C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */,
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */, C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */,
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */, C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,

View File

@ -10,6 +10,11 @@ import Foundation
// First, let's read the initial command line argument // First, let's read the initial command line argument
// REFACTOR REQUIRED
// Information about the Homebrew linked alias
// Information about the PHP versions
// etc.: needs to be stored in a separate object we can instantiate here and in PHP Monitor.
print(CommandLine.arguments) print(CommandLine.arguments)
if CommandLine.arguments.count != 3 { if CommandLine.arguments.count != 3 {

View File

@ -1,6 +1,6 @@
// //
// Command.swift // Command.swift
// PMCommon // phpmon-common
// //
// Copyright © 2021 Nico Verbruggen. All rights reserved. // Copyright © 2021 Nico Verbruggen. All rights reserved.
// //

View File

@ -1,38 +1,24 @@
// //
// Paths.swift // Paths.swift
// PMCommon // phpmon-common
// //
// Copyright © 2021 Nico Verbruggen. All rights reserved. // Copyright © 2021 Nico Verbruggen. All rights reserved.
// //
import Foundation import Foundation
public enum HomebrewDir: String { /**
case opt = "/opt/homebrew" The `Paths` class is used to locate various binaries on the system,
case usr = "/usr/local" and provides a full
} */
public class Paths { public class Paths {
public static let shared = Paths() public static let shared = Paths()
var baseDir : HomebrewDir
private var baseDir : Paths.HomebrewDir
init() { init() {
let optBrewFound = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") baseDir = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
let usrBrewFound = Shell.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew")
if (optBrewFound) {
// This is usually the case with Homebrew installed on Apple Silicon
baseDir = .opt
} else if (usrBrewFound) {
// This is usually the case with Homebrew installed on Intel (or Rosetta 2)
baseDir = .usr
} else {
// Falling back to default "legacy" Homebrew location (for Intel)
print("Seems like we couldn't determine the Homebrew directory.")
print("This usually means we're in trouble... (no Homebrew?)")
baseDir = .usr
}
} }
// - MARK: Binaries // - MARK: Binaries
@ -71,4 +57,11 @@ public class Paths {
return "\(shared.baseDir.rawValue)/etc" return "\(shared.baseDir.rawValue)/etc"
} }
// MARK: - Enum
public enum HomebrewDir: String {
case opt = "/opt/homebrew"
case usr = "/usr/local"
}
} }

View File

@ -0,0 +1,60 @@
//
// PhpSwitcher.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 21/12/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
protocol PhpSwitcherDelegate: AnyObject {
func switcherDidStartSwitching()
func switcherDidCompleteSwitch()
}
class PhpSwitcher {
init() {
self.currentInstall = ActivePhpInstallation()
}
/** The delegate that is informed of updates. */
weak var delegate: PhpSwitcherDelegate?
/** The static app instance. Accessible at any time. */
static let shared = PhpSwitcher()
/** Whether the switcher is busy performing any actions. */
var isBusy: Bool = false
/** All available versions of PHP. */
var availablePhpVersions: [String] = []
/** Cached information about the PHP installations. */
var cachedPhpInstallations: [String: PhpInstallation] = [:]
/** Static accessor for `PhpSwitcher.shared.currentInstall`. */
static var phpInstall: ActivePhpInstallation {
return Self.shared.currentInstall
}
/** Information about the currently linked PHP installation. */
var currentInstall: ActivePhpInstallation
/**
The version that the `php` formula via Brew is aliased to on the current system.
If you're up to date, `php` will be aliased to the latest version,
but that might not be the case.
*/
var brewPhpVersion: String {
return homebrewPackage.version
}
/**
Information we were able to discern from the Homebrew info command.
*/
var homebrewPackage: HomebrewPackage! = nil
}

View File

@ -1,6 +1,6 @@
// //
// Shell.swift // Shell.swift
// PMCommon // phpmon-common
// //
// Copyright © 2021 Nico Verbruggen. All rights reserved. // Copyright © 2021 Nico Verbruggen. All rights reserved.
// //
@ -80,7 +80,7 @@ public class Shell {
public func executeSynchronously( public func executeSynchronously(
_ command: String, _ command: String,
requiresPath: Bool = false requiresPath: Bool = false
) -> ShellOutput { ) -> Shell.Output {
let outputPipe = Pipe() let outputPipe = Pipe()
let errorPipe = Pipe() let errorPipe = Pipe()
@ -91,7 +91,7 @@ public class Shell {
task.launch() task.launch()
task.waitUntilExit() task.waitUntilExit()
return ShellOutput( return Shell.Output(
standardOutput: String( standardOutput: String(
data: outputPipe.fileHandleForReading.readDataToEndOfFile(), data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8 encoding: .utf8
@ -162,18 +162,18 @@ public class Shell {
NotificationCenter.default.removeObserver(pipe.fileHandleForReading) NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
} }
} }
}
public class ShellOutput {
public let standardOutput: String
public let errorOutput: String
public let task: Process
init(standardOutput: String, public class Output {
errorOutput: String, public let standardOutput: String
task: Process) { public let errorOutput: String
self.standardOutput = standardOutput public let task: Process
self.errorOutput = errorOutput
self.task = task init(standardOutput: String,
errorOutput: String,
task: Process) {
self.standardOutput = standardOutput
self.errorOutput = errorOutput
self.task = task
}
} }
} }

View File

@ -20,7 +20,7 @@ class Actions {
// Make sure the aliased version is detected // Make sure the aliased version is detected
// The user may have `php` installed, but not e.g. `php@8.0` // The user may have `php` installed, but not e.g. `php@8.0`
// We should also detect that as a version that is installed // We should also detect that as a version that is installed
let phpAlias = App.shared.brewPhpVersion let phpAlias = PhpSwitcher.shared.brewPhpVersion
// Avoid inserting a duplicate // Avoid inserting a duplicate
if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) { if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) {
@ -29,7 +29,7 @@ class Actions {
print("The PHP versions that were detected are: \(versionsOnly)") print("The PHP versions that were detected are: \(versionsOnly)")
App.shared.availablePhpVersions = versionsOnly PhpSwitcher.shared.availablePhpVersions = versionsOnly
Actions.extractPhpLongVersions() Actions.extractPhpLongVersions()
return versionsOnly return versionsOnly
@ -42,11 +42,11 @@ class Actions {
public static func extractPhpLongVersions() public static func extractPhpLongVersions()
{ {
var mappedVersions: [String: PhpInstallation] = [:] var mappedVersions: [String: PhpInstallation] = [:]
App.shared.availablePhpVersions.forEach { version in PhpSwitcher.shared.availablePhpVersions.forEach { version in
mappedVersions[version] = PhpInstallation(version) mappedVersions[version] = PhpInstallation(version)
} }
App.shared.cachedPhpInstallations = mappedVersions PhpSwitcher.shared.cachedPhpInstallations = mappedVersions
} }
/** /**
@ -82,7 +82,7 @@ class Actions {
public static func restartPhpFpm() public static func restartPhpFpm()
{ {
brew("services restart \(App.phpInstall!.formula)", sudo: true) brew("services restart \(PhpSwitcher.phpInstall.formula)", sudo: true)
} }
public static func restartNginx() public static func restartNginx()
@ -97,7 +97,7 @@ class Actions {
public static func stopAllServices() public static func stopAllServices()
{ {
brew("services stop \(App.phpInstall!.formula)", sudo: true) brew("services stop \(PhpSwitcher.phpInstall.formula)", sudo: true)
brew("services stop nginx", sudo: true) brew("services stop nginx", sudo: true)
brew("services stop dnsmasq", sudo: true) brew("services stop dnsmasq", sudo: true)
} }
@ -137,7 +137,7 @@ class Actions {
group.enter() group.enter()
DispatchQueue.global(qos: .userInitiated).async { DispatchQueue.global(qos: .userInitiated).async {
let formula = (available == App.shared.brewPhpVersion) let formula = (available == PhpSwitcher.shared.brewPhpVersion)
? "php" : "php@\(available)" ? "php" : "php@\(available)"
brew("unlink \(formula)") brew("unlink \(formula)")
@ -151,7 +151,7 @@ class Actions {
print("All versions have been unlinked!") print("All versions have been unlinked!")
print("Linking the new version!") print("Linking the new version!")
let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)"
brew("link \(formula) --overwrite --force") brew("link \(formula) --overwrite --force")
brew("services start \(formula)", sudo: true) brew("services start \(formula)", sudo: true)
@ -205,7 +205,7 @@ class Actions {
brew("services restart dnsmasq", sudo: true) brew("services restart dnsmasq", sudo: true)
detectPhpVersions().forEach { (version) in detectPhpVersions().forEach { (version) in
let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" let formula = (version == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version)"
brew("unlink php@\(version)") brew("unlink php@\(version)")
brew("services stop \(formula)") brew("services stop \(formula)")
brew("services stop \(formula)", sudo: true) brew("services stop \(formula)", sudo: true)

View File

@ -8,13 +8,17 @@
import Cocoa import Cocoa
import HotKey import HotKey
class App { class App: PhpSwitcherDelegate {
// MARK: Static Vars // MARK: Static Vars
/** The static app instance. Accessible at any time. */ /** The static app instance. Accessible at any time. */
static let shared = App() static let shared = App()
init() {
PhpSwitcher.shared.delegate = self
}
/** Retrieve the version number from the main info dictionary, Info.plist. */ /** Retrieve the version number from the main info dictionary, Info.plist. */
static var version: String { static var version: String {
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
@ -22,14 +26,9 @@ class App {
return "\(version) (\(build))" return "\(version) (\(build))"
} }
/** Information about the currently linked PHP installation. */
static var phpInstall: ActivePhpInstallation? {
return App.shared.currentInstall
}
/** Whether the app is busy doing something. Used to determine what UI to display. */ /** Whether the app is busy doing something. Used to determine what UI to display. */
static var busy: Bool { static var busy: Bool {
return App.shared.busy return PhpSwitcher.shared.isBusy
} }
// MARK: Variables // MARK: Variables
@ -43,46 +42,13 @@ class App {
/** The window controller of the currently active site list window. */ /** The window controller of the currently active site list window. */
var siteListWindowController: SiteListWC? = nil var siteListWindowController: SiteListWC? = nil
/** Whether the application is busy switching versions. */
var busy: Bool = false
/** The currently active installation of PHP. */
var currentInstall: ActivePhpInstallation? = nil {
didSet {
handlePhpConfigWatcher()
}
}
/** All available versions of PHP. */
var availablePhpVersions: [String] = []
/** Cached information about the PHP installations. */
var cachedPhpInstallations: [String: PhpInstallation] = [:]
/** List of detected (installed) applications that PHP Monitor can work with. */ /** List of detected (installed) applications that PHP Monitor can work with. */
var detectedApplications: [Application] = [] var detectedApplications: [Application] = []
/** Timer that will periodically reload info about the user's PHP installation. */ /** Timer that will periodically reload info about the user's PHP installation. */
var timer: Timer? var timer: Timer?
/** Information we were able to discern from the Homebrew info command (as JSON). */
var brewPhpPackage: HomebrewPackage! = nil {
didSet {
brewPhpVersion = brewPhpPackage!.version
}
}
/**
The version that the `php` formula via Brew is aliased to on the current system.
If you're up to date, `php` will be aliased to the latest version,
but that might not be the case.
We'll technically default to the version in Constants.swift, but the information
should always be loaded from Homebrew itself upon startup.
*/
var brewPhpVersion: String = Constants.LatestStablePhpVersion
// MARK: - Global Hotkey // MARK: - Global Hotkey
/** /**
@ -112,4 +78,14 @@ class App {
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder. The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
*/ */
var watcher: PhpConfigWatcher! var watcher: PhpConfigWatcher!
// MARK: - PhpSwitcherDelegate
func switcherDidStartSwitching() {
}
func switcherDidCompleteSwitch() {
PhpSwitcher.shared.currentInstall = ActivePhpInstallation()
handlePhpConfigWatcher()
}
} }

View File

@ -20,6 +20,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
*/ */
let sharedShell: Shell let sharedShell: Shell
/**
The PhpSwitcher singleton that handles PHP version
detection, as well as switching.
- Note: It is important to initialize the switcher
before the `App` singleton, so that the delegate
is set correctly.
*/
let switcher: PhpSwitcher
/** /**
The App singleton contains information about the state of The App singleton contains information about the state of
the application and global variables. the application and global variables.
@ -55,6 +65,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
print("Version \(App.version)") print("Version \(App.version)")
print("==================================") print("==================================")
self.sharedShell = Shell.user self.sharedShell = Shell.user
self.switcher = PhpSwitcher.shared
self.state = App.shared self.state = App.shared
self.menu = MainMenu.shared self.menu = MainMenu.shared
self.paths = Paths.shared self.paths = Paths.shared

View File

@ -91,12 +91,12 @@ class Startup {
let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json"); let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json");
App.shared.brewPhpPackage = try! JSONDecoder().decode( PhpSwitcher.shared.homebrewPackage = try! JSONDecoder().decode(
[HomebrewPackage].self, [HomebrewPackage].self,
from: brewPhpAlias.data(using: .utf8)! from: brewPhpAlias.data(using: .utf8)!
).first! ).first!
print("When on your system, the `php` formula means version \(App.shared.brewPhpVersion)!") print("When on your system, the `php` formula means version \(PhpSwitcher.shared.brewPhpVersion)!")
} }
/** /**

View File

@ -46,12 +46,12 @@ class HomebrewDiagnostics {
from: tapAlias.data(using: .utf8)! from: tapAlias.data(using: .utf8)!
).first! ).first!
if tapPhp.version != App.shared.brewPhpVersion { if tapPhp.version != PhpSwitcher.shared.brewPhpVersion {
print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!") print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!")
print("Determining whether both of these versions are installed...") print("Determining whether both of these versions are installed...")
let bothInstalled = App.shared.availablePhpVersions.contains(tapPhp.version) let bothInstalled = PhpSwitcher.shared.availablePhpVersions.contains(tapPhp.version)
&& App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) && PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion)
if bothInstalled { if bothInstalled {
print("Both conflicting aliases seem to be installed, warning the user!") print("Both conflicting aliases seem to be installed, warning the user!")

View File

@ -57,9 +57,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
print("Determining broken PHP-FPM...") print("Determining broken PHP-FPM...")
// Attempt to find out if PHP-FPM is broken // Attempt to find out if PHP-FPM is broken
let installation = App.phpInstall! let installation = PhpSwitcher.phpInstall
installation.notifyAboutBrokenPhpFpm() installation.notifyAboutBrokenPhpFpm()
// Set up the config watchers on launch (these are automatically updated via delegate methods if the user switches)
print("Setting up watchers...")
App.shared.handlePhpConfigWatcher()
print("Detecting applications...") print("Detecting applications...")
// Attempt to load list of applications // Attempt to load list of applications
App.shared.detectedApplications = Application.detectPresetApplications() App.shared.detectedApplications = Application.detectPresetApplications()
@ -82,7 +86,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
App.shared.timer = Timer.scheduledTimer( App.shared.timer = Timer.scheduledTimer(
timeInterval: 60, timeInterval: 60,
target: self, target: self,
selector: #selector(updatePhpVersionInStatusBar), selector: #selector(refreshActiveInstallation),
userInfo: nil, userInfo: nil,
repeats: true repeats: true
) )
@ -181,12 +185,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
*/ */
private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
{ {
App.shared.busy = true PhpSwitcher.shared.isBusy = true
setBusyImage() setBusyImage()
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
update() update()
execute() execute()
App.shared.busy = false PhpSwitcher.shared.isBusy = false
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
updatePhpVersionInStatusBar() updatePhpVersionInStatusBar()
@ -198,8 +202,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
// MARK: - User Interface // MARK: - User Interface
@objc func refreshActiveInstallation() {
PhpSwitcher.shared.currentInstall = ActivePhpInstallation()
updatePhpVersionInStatusBar()
}
@objc func updatePhpVersionInStatusBar() { @objc func updatePhpVersionInStatusBar() {
App.shared.currentInstall = ActivePhpInstallation()
refreshIcon() refreshIcon()
update() update()
} }
@ -215,7 +223,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
} else { } else {
// The dynamic icon has been requested // The dynamic icon has been requested
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
setStatusBarImage(version: long ? App.phpInstall!.version.long : App.phpInstall!.version.short) setStatusBarImage(version: long ? PhpSwitcher.phpInstall.version.long : PhpSwitcher.phpInstall.version.short)
} }
} }
} }
@ -335,12 +343,12 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
} }
func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) { func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
App.shared.busy = true PhpSwitcher.shared.isBusy = true
setBusyImage() setBusyImage()
self.update() self.update()
let noLongerBusy = { let noLongerBusy = {
App.shared.busy = false PhpSwitcher.shared.isBusy = false
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
self.updatePhpVersionInStatusBar() self.updatePhpVersionInStatusBar()
self.update() self.update()
@ -411,14 +419,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
} }
@objc func openActiveConfigFolder() { @objc func openActiveConfigFolder() {
if (App.phpInstall!.version.error) { if (PhpSwitcher.phpInstall.version.error) {
// php version was not identified // php version was not identified
Actions.openGenericPhpConfigFolder() Actions.openGenericPhpConfigFolder()
return return
} }
// php version was identified // php version was identified
Actions.openPhpConfigFolder(version: App.phpInstall!.version.short) Actions.openPhpConfigFolder(version: PhpSwitcher.phpInstall.version.short)
} }
@objc func openGlobalComposerFolder() { @objc func openGlobalComposerFolder() {
@ -431,7 +439,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
@objc func switchToPhpVersion(sender: PhpMenuItem) { @objc func switchToPhpVersion(sender: PhpMenuItem) {
setBusyImage() setBusyImage()
App.shared.busy = true PhpSwitcher.shared.isBusy = true
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
// Update the PHP version in the status bar // Update the PHP version in the status bar
@ -441,8 +449,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
update() update()
let completion = { let completion = {
PhpSwitcher.shared.delegate?.switcherDidCompleteSwitch()
// Mark as no longer busy // Mark as no longer busy
App.shared.busy = false PhpSwitcher.shared.isBusy = false
// Perform UI updates on main thread // Perform UI updates on main thread
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
@ -454,7 +464,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
title: String(format: "notification.version_changed_title".localized, sender.version), title: String(format: "notification.version_changed_title".localized, sender.version),
subtitle: String(format: "notification.version_changed_desc".localized, sender.version) subtitle: String(format: "notification.version_changed_desc".localized, sender.version)
) )
App.phpInstall?.notifyAboutBrokenPhpFpm() PhpSwitcher.phpInstall.notifyAboutBrokenPhpFpm()
} }
// Run composer updates // Run composer updates
@ -482,7 +492,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
// Will cause more issues with Homebrew and is faster // Will cause more issues with Homebrew and is faster
Actions.switchToPhpVersion( Actions.switchToPhpVersion(
version: sender.version, version: sender.version,
availableVersions: App.shared.availablePhpVersions, availableVersions: PhpSwitcher.shared.availablePhpVersions,
completed: completion completed: completion
) )
/* } */ /* } */

View File

@ -9,18 +9,14 @@ import Cocoa
class StatusMenu : NSMenu { class StatusMenu : NSMenu {
func addPhpVersionMenuItems() { func addPhpVersionMenuItems() {
if App.shared.currentInstall == nil { if PhpSwitcher.phpInstall.version.error {
return
}
if App.phpInstall!.version.error {
for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] { for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] {
addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: "")) addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: ""))
} }
return return
} }
let phpVersionText = "\("mi_php_version".localized) \(App.phpInstall!.version.long)" let phpVersionText = "\("mi_php_version".localized) \(PhpSwitcher.phpInstall.version.long)"
addItem(HeaderView.asMenuItem(text: phpVersionText)) addItem(HeaderView.asMenuItem(text: phpVersionText))
} }
@ -30,7 +26,7 @@ class StatusMenu : NSMenu {
return return
} }
if App.shared.availablePhpVersions.count == 0 { if PhpSwitcher.shared.availablePhpVersions.count == 0 {
return return
} }
@ -44,14 +40,14 @@ class StatusMenu : NSMenu {
servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: "")) servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: ""))
if !App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion) { if !PhpSwitcher.shared.availablePhpVersions.contains(PhpSwitcher.shared.brewPhpVersion) {
servicesMenu.addItem(NSMenuItem( servicesMenu.addItem(NSMenuItem(
title: "mi_force_load_latest_unavailable".localized(App.shared.brewPhpVersion), title: "mi_force_load_latest_unavailable".localized(PhpSwitcher.shared.brewPhpVersion),
action: nil, keyEquivalent: "f" action: nil, keyEquivalent: "f"
)) ))
} else { } else {
servicesMenu.addItem(NSMenuItem( servicesMenu.addItem(NSMenuItem(
title: "mi_force_load_latest".localized(App.shared.brewPhpVersion), title: "mi_force_load_latest".localized(PhpSwitcher.shared.brewPhpVersion),
action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f")) action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f"))
} }
@ -75,8 +71,6 @@ class StatusMenu : NSMenu {
item.target = MainMenu.shared item.target = MainMenu.shared
} }
self.setSubmenu(servicesMenu, for: services) self.setSubmenu(servicesMenu, for: services)
self.addItem(services) self.addItem(services)
} }
@ -89,10 +83,6 @@ class StatusMenu : NSMenu {
} }
func addPhpConfigurationMenuItems() { func addPhpConfigurationMenuItems() {
if App.shared.currentInstall == nil {
return
}
// Configuration // Configuration
self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized)) self.addItem(HeaderView.asMenuItem(text: "mi_configuration".localized))
self.addItem(NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c")) self.addItem(NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c"))
@ -102,13 +92,13 @@ class StatusMenu : NSMenu {
self.addItem(NSMenuItem.separator()) self.addItem(NSMenuItem.separator())
self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized)) self.addItem(HeaderView.asMenuItem(text: "mi_composer".localized))
self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g")) self.addItem(NSMenuItem(title: "mi_global_composer".localized, action: #selector(MainMenu.openGlobalComposerFolder), keyEquivalent: "g"))
self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: App.shared.busy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: "")) self.addItem(NSMenuItem(title: "mi_update_global_composer".localized, action: PhpSwitcher.shared.isBusy ? nil : #selector(MainMenu.updateComposerDependencies), keyEquivalent: ""))
if (App.shared.busy) { if (PhpSwitcher.shared.isBusy) {
return return
} }
let stats = App.phpInstall!.limits let stats = PhpSwitcher.phpInstall.limits
// Stats // Stats
self.addItem(NSMenuItem.separator()) self.addItem(NSMenuItem.separator())
@ -122,12 +112,12 @@ class StatusMenu : NSMenu {
self.addItem(NSMenuItem.separator()) self.addItem(NSMenuItem.separator())
self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized)) self.addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized))
if (App.phpInstall!.extensions.count == 0) { if (PhpSwitcher.phpInstall.extensions.count == 0) {
self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: ""))
} }
var shortcutKey = 1 var shortcutKey = 1
for phpExtension in App.phpInstall!.extensions { for phpExtension in PhpSwitcher.phpInstall.extensions {
self.addExtensionItem(phpExtension, shortcutKey) self.addExtensionItem(phpExtension, shortcutKey)
shortcutKey += 1 shortcutKey += 1
} }
@ -140,20 +130,20 @@ class StatusMenu : NSMenu {
private func addSwitchToPhpMenuItems() { private func addSwitchToPhpMenuItems() {
var shortcutKey = 1 var shortcutKey = 1
for index in (0..<App.shared.availablePhpVersions.count).reversed() { for index in (0..<PhpSwitcher.shared.availablePhpVersions.count).reversed() {
// Get the short and long version // Get the short and long version
let shortVersion = App.shared.availablePhpVersions[index] let shortVersion = PhpSwitcher.shared.availablePhpVersions[index]
let longVersion = App.shared.cachedPhpInstallations[shortVersion]!.longVersion let longVersion = PhpSwitcher.shared.cachedPhpInstallations[shortVersion]!.longVersion
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
let versionString = long ? longVersion : shortVersion let versionString = long ? longVersion : shortVersion
let action = #selector(MainMenu.switchToPhpVersion(sender:)) let action = #selector(MainMenu.switchToPhpVersion(sender:))
let brew = (shortVersion == App.shared.brewPhpVersion) ? "php" : "php@\(shortVersion)" let brew = (shortVersion == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(shortVersion)"
let menuItem = PhpMenuItem( let menuItem = PhpMenuItem(
title: "\("mi_php_switch".localized) \(versionString) (\(brew))", title: "\("mi_php_switch".localized) \(versionString) (\(brew))",
action: (shortVersion == App.phpInstall?.version.short) ? nil : action, keyEquivalent: "\(shortcutKey)" action: (shortVersion == PhpSwitcher.phpInstall.version.short) ? nil : action, keyEquivalent: "\(shortcutKey)"
) )
menuItem.version = shortVersion menuItem.version = shortVersion

View File

@ -25,7 +25,7 @@ class ActivePhpInstallation {
// MARK: - Computed // MARK: - Computed
var formula: String { var formula: String {
return (version.short == App.shared.brewPhpVersion) ? "php" : "php@\(version.short)" return (version.short == PhpSwitcher.shared.brewPhpVersion) ? "php" : "php@\(version.short)"
} }
// MARK: - Initializer // MARK: - Initializer

View File

@ -28,22 +28,19 @@ extension App {
} }
func handlePhpConfigWatcher(forceReload: Bool = false) { func handlePhpConfigWatcher(forceReload: Bool = false) {
if self.currentInstall != nil { let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(PhpSwitcher.phpInstall.version.short)")
// Determine the path of the config folder
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(self.currentInstall!.version.short)") // Watcher needs to be created
if self.watcher == nil {
// Watcher needs to be created startWatcher(url)
if self.watcher == nil { }
startWatcher(url)
} // Watcher needs to be updated
if self.watcher.url != url || forceReload {
// Watcher needs to be updated self.watcher.disable()
if self.watcher.url != url || forceReload { self.watcher = nil
self.watcher.disable() print("Watcher has stopped watching files. Starting new one...")
self.watcher = nil startWatcher(url)
print("Watcher has stopped watching files. Starting new one...")
startWatcher(url)
}
} }
} }