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

🏗 WIP: Allow unlinked PHP version

This commit is contained in:
2023-01-11 22:22:52 +01:00
parent e6d2c873a5
commit 44800a03a1
19 changed files with 120 additions and 47 deletions

View File

@@ -9,19 +9,18 @@
import Foundation import Foundation
class Homebrew { class Homebrew {
static var fake: Bool = false
struct Formulae { struct Formulae {
static var php: HomebrewFormula { static var php: HomebrewFormula {
if Homebrew.fake {
return HomebrewFormula("php", elevated: true)
}
if PhpEnv.shared.homebrewPackage == nil { if PhpEnv.shared.homebrewPackage == nil {
fatalError("You must either load the HomebrewPackage object or call `fake` on the Homebrew class.") fatalError("You must either load the HomebrewPackage object or call `fake` on the Homebrew class.")
} }
return HomebrewFormula(PhpEnv.phpInstall.formula, elevated: true) guard let install = PhpEnv.phpInstall else {
Log.info("Assuming the formula is `php` since none seems to be linked.")
return HomebrewFormula("php", elevated: true)
}
return HomebrewFormula(install.formula, elevated: true)
} }
static var nginx: HomebrewFormula { static var nginx: HomebrewFormula {

View File

@@ -45,7 +45,7 @@ class PhpEnv {
var cachedPhpInstallations: [String: PhpInstallation] = [:] var cachedPhpInstallations: [String: PhpInstallation] = [:]
/** Information about the currently linked PHP installation. */ /** Information about the currently linked PHP installation. */
var currentInstall: ActivePhpInstallation! var currentInstall: ActivePhpInstallation?
/** /**
The version that the `php` formula via Brew is aliased to on the current system. The version that the `php` formula via Brew is aliased to on the current system.
@@ -57,15 +57,15 @@ class PhpEnv {
As such, we take that information from Homebrew. As such, we take that information from Homebrew.
*/ */
static var brewPhpAlias: String { static var brewPhpAlias: String {
if Homebrew.fake { return "8.2" } if PhpEnv.shared.homebrewPackage == nil { return "8.2" }
return Self.shared.homebrewPackage.version return PhpEnv.shared.homebrewPackage.version
} }
/** /**
The currently linked and active PHP installation. The currently linked and active PHP installation.
*/ */
static var phpInstall: ActivePhpInstallation { static var phpInstall: ActivePhpInstallation? {
return Self.shared.currentInstall return Self.shared.currentInstall
} }
@@ -170,7 +170,12 @@ class PhpEnv {
Validates whether the currently running version matches the provided version. Validates whether the currently running version matches the provided version.
*/ */
public func validate(_ version: String) -> Bool { public func validate(_ version: String) -> Bool {
if self.currentInstall.version.short == version { guard let install = PhpEnv.phpInstall else {
Log.info("It appears as if no PHP installation is currently active.")
return false
}
if install.version.short == version {
Log.info("Switching to version \(version) seems to have succeeded. Validation passed.") Log.info("Switching to version \(version) seems to have succeeded. Validation passed.")
Log.info("Keeping track that this is the new version!") Log.info("Keeping track that this is the new version!")
Stats.persistCurrentGlobalPhpVersion(version: version) Stats.persistCurrentGlobalPhpVersion(version: version)
@@ -186,7 +191,11 @@ class PhpEnv {
You can then use the configuration file instance to change values. You can then use the configuration file instance to change values.
*/ */
public func getConfigFile(forKey key: String) -> PhpConfigurationFile? { public func getConfigFile(forKey key: String) -> PhpConfigurationFile? {
return PhpEnv.phpInstall.iniFiles guard let install = PhpEnv.phpInstall else {
return nil
}
return install.iniFiles
.reversed() .reversed()
.first(where: { $0.has(key: key) }) .first(where: { $0.has(key: key) })
} }

View File

@@ -17,7 +17,6 @@ public struct TestableConfiguration: Codable {
func apply() { func apply() {
Log.separator() Log.separator()
Log.info("USING TESTABLE CONFIGURATION...") Log.info("USING TESTABLE CONFIGURATION...")
Homebrew.fake = true
Log.separator() Log.separator()
Log.info("Applying fake shell...") Log.info("Applying fake shell...")
ActiveShell.useTestable(shellOutput) ActiveShell.useTestable(shellOutput)

View File

@@ -261,7 +261,7 @@ class Startup {
}, },
name: "valet version is supported", name: "valet version is supported",
titleText: "startup.errors.valet_version_not_supported.title".localized, titleText: "startup.errors.valet_version_not_supported.title".localized,
subtitleText: "startup.errors.valet_version_not_supported.subtitle".localized(Valet.shared.version.text), subtitleText: "startup.errors.valet_version_not_supported.subtitle".localized,
descriptionText: "startup.errors.valet_version_not_supported.desc".localized descriptionText: "startup.errors.valet_version_not_supported.desc".localized
) )
] ]

View File

@@ -58,8 +58,12 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
return [] return []
} }
guard let install = PhpEnv.phpInstall else {
return []
}
return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
version.short != PhpEnv.phpInstall.version.short version.short != install.version.short
}) })
} }

View File

@@ -133,7 +133,7 @@ extension DomainListVC {
if site.isolatedPhpVersion != nil { if site.isolatedPhpVersion != nil {
menu.addItem(NSMenuItem( menu.addItem(NSMenuItem(
title: "domain_list.use_in_terminal".localized(site.servingPhpVersion), title: "domain_list.use_in_terminal".localized(site.isolatedPhpVersion!.versionNumber.text),
action: #selector(self.useInTerminal) action: #selector(self.useInTerminal)
)) ))
} }

View File

@@ -65,8 +65,13 @@ class HomebrewDiagnostics {
public static func checkForPhpFpmPoolConflicts() { public static func checkForPhpFpmPoolConflicts() {
Log.info("Checking for PHP-FPM pool conflicts...") Log.info("Checking for PHP-FPM pool conflicts...")
guard let install = PhpEnv.phpInstall else {
Log.info("Will skip check for conflicts if no PHP version is linked.")
return
}
// We'll need to know what the primary PHP version is // We'll need to know what the primary PHP version is
let primary = PhpEnv.shared.currentInstall.version.short let primary = install.version.short
// Versions to be handled // Versions to be handled
let switcher = InternalSwitcher() let switcher = InternalSwitcher()

View File

@@ -56,7 +56,8 @@ class ValetSite: ValetListable {
/// Which version of PHP is actually used to serve this site. /// Which version of PHP is actually used to serve this site.
var servingPhpVersion: String { var servingPhpVersion: String {
return self.isolatedPhpVersion?.versionNumber.short return self.isolatedPhpVersion?.versionNumber.short
?? PhpEnv.phpInstall.version.short ?? PhpEnv.phpInstall?.version.short
?? "???"
} }
enum VersionSource: String { enum VersionSource: String {
@@ -143,11 +144,16 @@ class ValetSite: ValetListable {
return return
} }
guard let linked = PhpEnv.phpInstall else {
self.composerPhpCompatibleWithLinked = false
return
}
// Split the composer list (on "|") to evaluate multiple constraints // Split the composer list (on "|") to evaluate multiple constraints
// For example, for Laravel 8 projects the value is "^7.3|^8.0" // For example, for Laravel 8 projects the value is "^7.3|^8.0"
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|") self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
.map { string in .map { string in
let origin = self.isolatedPhpVersion?.versionNumber.short ?? PhpEnv.phpInstall.version.long let origin = self.isolatedPhpVersion?.versionNumber.short ?? linked.version.long
return !PhpVersionNumberCollection.make(from: [origin]) return !PhpVersionNumberCollection.make(from: [origin])
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines)) .matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
.isEmpty .isEmpty
@@ -251,7 +257,7 @@ class ValetSite: ValetListable {
} }
func getListablePhpVersion() -> String { func getListablePhpVersion() -> String {
return self.servingPhpVersion return self.servingPhpVersion ?? ""
} }
func getListableKind() -> String { func getListableKind() -> String {

View File

@@ -207,12 +207,17 @@ extension MainMenu {
} }
@objc func openActiveConfigFolder() { @objc func openActiveConfigFolder() {
if PhpEnv.phpInstall.hasErrorState { guard let install = PhpEnv.phpInstall else {
// TODO: Can't open the config if no PHP version is active
return
}
if install.hasErrorState {
Actions.openGenericPhpConfigFolder() Actions.openGenericPhpConfigFolder()
return return
} }
Actions.openPhpConfigFolder(version: PhpEnv.phpInstall.version.short) Actions.openPhpConfigFolder(version: install.version.short)
} }
@objc func openPhpMonitorConfigurationFile() { @objc func openPhpMonitorConfigurationFile() {

View File

@@ -12,7 +12,7 @@ import AppKit
extension MainMenu { extension MainMenu {
@MainActor @objc func fixMyValet() { @MainActor @objc func fixMyValet() {
let previousVersion = PhpEnv.phpInstall.version.short let previousVersion = PhpEnv.phpInstall?.version.short
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpAlias) { if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpAlias) {
presentAlertForMissingFormula() presentAlertForMissingFormula()
@@ -34,10 +34,10 @@ extension MainMenu {
Task { @MainActor in Task { @MainActor in
await Actions.fixMyValet() await Actions.fixMyValet()
if previousVersion == PhpEnv.brewPhpAlias { if previousVersion == PhpEnv.brewPhpAlias || previousVersion == nil {
self.presentAlertForSameVersion() self.presentAlertForSameVersion()
} else { } else {
self.presentAlertForDifferentVersion(version: previousVersion) self.presentAlertForDifferentVersion(version: previousVersion!)
} }
} }
} }

View File

@@ -68,7 +68,7 @@ extension MainMenu {
// Attempt to find out if PHP-FPM is broken // Attempt to find out if PHP-FPM is broken
Log.info("Determining broken PHP-FPM...") Log.info("Determining broken PHP-FPM...")
let installation = PhpEnv.phpInstall let installation = PhpEnv.phpInstall
installation.notifyAboutBrokenPhpFpm() installation?.notifyAboutBrokenPhpFpm()
// Check for other problems // Check for other problems
WarningManager.shared.evaluateWarnings() WarningManager.shared.evaluateWarnings()

View File

@@ -118,6 +118,11 @@ extension MainMenu {
preference: .notifyAboutVersionChange preference: .notifyAboutVersionChange
) )
Task { PhpEnv.phpInstall.notifyAboutBrokenPhpFpm() } guard let install = PhpEnv.phpInstall else {
Log.err("Cannot notify about version change if PHP is unlinked")
return
}
Task { install.notifyAboutBrokenPhpFpm() }
} }
} }

View File

@@ -134,7 +134,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
} 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 ? PhpEnv.phpInstall.version.long : PhpEnv.phpInstall.version.short)
guard let install = PhpEnv.phpInstall else {
setStatusBarImage(version: "???")
return
}
setStatusBarImage(version: long ? install.version.long : install.version.short)
} }
} }
} }

View File

@@ -13,13 +13,13 @@ import Cocoa
extension StatusMenu { extension StatusMenu {
func addPhpVersionMenuItems() { func addPhpVersionMenuItems() {
if PhpEnv.phpInstall.hasErrorState { if PhpEnv.phpInstall == nil || PhpEnv.phpInstall!.hasErrorState {
let brokenMenuItems = ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] let brokenMenuItems = ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"]
return addItems(brokenMenuItems.map { NSMenuItem(title: $0.localized) }) return addItems(brokenMenuItems.map { NSMenuItem(title: $0.localized) })
} }
addItem(HeaderView.asMenuItem( addItem(HeaderView.asMenuItem(
text: "\("mi_php_version".localized) \(PhpEnv.phpInstall.version.long)", text: "\("mi_php_version".localized) \(PhpEnv.phpInstall!.version.long)",
minimumWidth: 280 // this ensures the menu is at least wide enough not to cause clipping minimumWidth: 280 // this ensures the menu is at least wide enough not to cause clipping
)) ))
} }
@@ -60,9 +60,10 @@ extension StatusMenu {
let action = #selector(MainMenu.switchToPhpVersion(sender:)) let action = #selector(MainMenu.switchToPhpVersion(sender:))
let brew = (shortVersion == PhpEnv.brewPhpAlias) ? "php" : "php@\(shortVersion)" let brew = (shortVersion == PhpEnv.brewPhpAlias) ? "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 == PhpEnv.phpInstall.version.short) action: (shortVersion == PhpEnv.phpInstall?.version.short)
? nil ? nil
: action, keyEquivalent: "\(shortcutKey)" : action, keyEquivalent: "\(shortcutKey)"
) )
@@ -145,7 +146,12 @@ extension StatusMenu {
// MARK: - Stats // MARK: - Stats
func addStatsMenuItem() { func addStatsMenuItem() {
guard let stats = PhpEnv.phpInstall.limits else { return } guard let install = PhpEnv.phpInstall else {
Log.info("Not showing stats menu item if no PHP version is linked.")
return
}
guard let stats = install.limits else { return }
addItem(StatsView.asMenuItem( addItem(StatsView.asMenuItem(
memory: stats.memory_limit, memory: stats.memory_limit,
@@ -157,14 +163,19 @@ extension StatusMenu {
// MARK: - Extensions // MARK: - Extensions
func addExtensionsMenuItems() { func addExtensionsMenuItems() {
guard let install = PhpEnv.phpInstall else {
Log.info("Not showing extensions menu items if no PHP version is linked.")
return
}
addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized)) addItem(HeaderView.asMenuItem(text: "mi_detected_extensions".localized))
if PhpEnv.phpInstall.extensions.isEmpty { if install.extensions.isEmpty {
addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: ""))
} }
var shortcutKey = 1 var shortcutKey = 1
for phpExtension in PhpEnv.phpInstall.extensions { for phpExtension in install.extensions {
addExtensionItem(phpExtension, shortcutKey) addExtensionItem(phpExtension, shortcutKey)
shortcutKey += 1 shortcutKey += 1
} }

View File

@@ -101,8 +101,8 @@ class Stats {
*/ */
public static func evaluateSponsorMessageShouldBeDisplayed() { public static func evaluateSponsorMessageShouldBeDisplayed() {
if Homebrew.fake { if Shell is TestableShell {
return Log.info("A fake environment is in use, skipping sponsor alert.") return Log.info("A fake shell is in use, skipping sponsor alert.")
} }
if Bundle.main.bundleIdentifier?.contains("beta") ?? false { if Bundle.main.bundleIdentifier?.contains("beta") ?? false {
@@ -142,7 +142,13 @@ class Stats {
} }
public static func evaluateLastLinkedPhpVersion() { public static func evaluateLastLinkedPhpVersion() {
let currentVersion = PhpEnv.phpInstall.version.short guard let linked = PhpEnv.phpInstall else {
// TODO: Actually notify the user that no version is linked.
Log.info("No version is currently linked.")
return
}
let currentVersion = linked.version.short
let previousVersion = Stats.lastGlobalPhpVersion let previousVersion = Stats.lastGlobalPhpVersion
// Save the PHP version that is currently in use (only if unknown) // Save the PHP version that is currently in use (only if unknown)

View File

@@ -88,10 +88,14 @@ struct Preset: Codable, Equatable {
applyConfigurationValue(key: conf.key, value: conf.value ?? "") applyConfigurationValue(key: conf.key, value: conf.value ?? "")
} }
guard let install = PhpEnv.phpInstall else {
Log.info("Cannot toggle extensions if no PHP version is linked.")
return
}
// Apply the extension changes in-place afterward // Apply the extension changes in-place afterward
for ext in extensions { for ext in extensions {
for foundExt in PhpEnv.phpInstall.extensions for foundExt in install.extensions where foundExt.name == ext.key && foundExt.enabled != ext.value {
where foundExt.name == ext.key && foundExt.enabled != ext.value {
Log.info("Toggling extension \(foundExt.name) in \(foundExt.file)") Log.info("Toggling extension \(foundExt.name) in \(foundExt.file)")
await foundExt.toggle() await foundExt.toggle()
break break
@@ -125,7 +129,7 @@ struct Preset: Codable, Equatable {
// MARK: - Apply Functionality // MARK: - Apply Functionality
private func switchToPhpVersionIfValid() async -> Bool { private func switchToPhpVersionIfValid() async -> Bool {
if PhpEnv.shared.currentInstall.version.short == self.version! { if PhpEnv.shared.currentInstall?.version.short == self.version! {
Log.info("The version we are supposed to switch to is already active.") Log.info("The version we are supposed to switch to is already active.")
return true return true
} }
@@ -213,8 +217,12 @@ struct Preset: Codable, Equatable {
return nil return nil
} }
if PhpEnv.shared.currentInstall.version.short != version { guard let install = PhpEnv.phpInstall else {
return PhpEnv.shared.currentInstall.version.short return nil
}
if install.version.short != version {
return install.version.short
} else { } else {
return nil return nil
} }
@@ -226,8 +234,12 @@ struct Preset: Codable, Equatable {
private func diffExtensions() -> [String: Bool] { private func diffExtensions() -> [String: Bool] {
var items: [String: Bool] = [:] var items: [String: Bool] = [:]
guard let install = PhpEnv.phpInstall else {
fatalError("If no PHP version is linked, diffing extensions is not possible.")
}
for (key, value) in self.extensions { for (key, value) in self.extensions {
for foundExt in PhpEnv.phpInstall.extensions for foundExt in install.extensions
where foundExt.name == key && foundExt.enabled != value { where foundExt.name == key && foundExt.enabled != value {
// Save the original value of the extension // Save the original value of the extension
items[foundExt.name] = foundExt.enabled items[foundExt.name] = foundExt.enabled

View File

@@ -97,7 +97,7 @@ struct VersionPopoverView: View {
if site.isolatedPhpVersion != nil { if site.isolatedPhpVersion != nil {
information += "alert.composer_php_isolated.desc".localized( information += "alert.composer_php_isolated.desc".localized(
site.isolatedPhpVersion!.versionNumber.short, site.isolatedPhpVersion!.versionNumber.short,
PhpEnv.phpInstall.version.short PhpEnv.phpInstall?.version.short ?? "???"
) )
information += "\n\n" information += "\n\n"
} }

View File

@@ -33,7 +33,13 @@ extension App {
return return
} }
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(PhpEnv.phpInstall.version.short)") guard let install = PhpEnv.phpInstall else {
Log.info("It appears as if no PHP installation is currently active.")
Log.info("The FS watcher will be disabled until a PHP install is active.")
return
}
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(install.version.short)")
// Check whether the watcher exists and schedule on the main thread // Check whether the watcher exists and schedule on the main thread
// if we don't consistently do this, the app will create duplicate watchers // if we don't consistently do this, the app will create duplicate watchers

View File

@@ -525,7 +525,7 @@ If you are seeing this message but are confused why this folder has gone missing
// Valet version too new or old // Valet version too new or old
"startup.errors.valet_version_not_supported.title" = "This version of Valet is not supported"; "startup.errors.valet_version_not_supported.title" = "This version of Valet is not supported";
"startup.errors.valet_version_not_supported.subtitle" = "You are running a version of Valet that is currently not supported (%@). PHP Monitor currently works with Valet v2, v3 and v4. In order to avoid causing issues on your system, PHP Monitor cannot start."; "startup.errors.valet_version_not_supported.subtitle" = "You are running a version of Valet that is currently not supported. PHP Monitor currently works with Valet v2, v3 and v4. In order to avoid causing issues on your system, PHP Monitor cannot start.";
"startup.errors.valet_version_not_supported.desc" = "You must install a version of Valet that is compatible with PHP Monitor, or you may need to upgrade to a newer version of PHP Monitor which may include compatibility for this version of Valet (consult the latest release notes for more info)."; "startup.errors.valet_version_not_supported.desc" = "You must install a version of Valet that is compatible with PHP Monitor, or you may need to upgrade to a newer version of PHP Monitor which may include compatibility for this version of Valet (consult the latest release notes for more info).";
/// Brew & sudoers /// Brew & sudoers