diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 1060549..5457102 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -152,7 +152,6 @@ C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; }; C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; }; C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; - C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; }; C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */; }; C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; }; @@ -231,7 +230,6 @@ C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; }; C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; }; - C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; }; C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; }; C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; }; C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; }; @@ -1073,14 +1071,12 @@ files = ( C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */, C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */, - C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */, C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */, C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */, C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */, C4232EE52612526500158FC6 /* Credits.html in Resources */, 54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */, C473319F2470923A009A0597 /* Localizable.strings in Resources */, - C4F30B07278E195800755FCE /* brew-services.json in Resources */, C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */, C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, @@ -1520,7 +1516,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 787; + CURRENT_PROJECT_VERSION = 790; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1530,7 +1526,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.3.1; + MARKETING_VERSION = 5.3.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1546,7 +1542,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 787; + CURRENT_PROJECT_VERSION = 790; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; @@ -1556,7 +1552,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 11.0; - MARKETING_VERSION = 5.3.1; + MARKETING_VERSION = 5.3.2; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/phpmon/Common/PHP/Switcher/InternalSwitcher.swift b/phpmon/Common/PHP/Switcher/InternalSwitcher.swift index d9a6b71..ee96726 100644 --- a/phpmon/Common/PHP/Switcher/InternalSwitcher.swift +++ b/phpmon/Common/PHP/Switcher/InternalSwitcher.swift @@ -23,17 +23,7 @@ class InternalSwitcher: PhpSwitcher { func performSwitch(to version: String, completion: @escaping () -> Void) { Log.info("Switching to \(version), unlinking all versions...") - let isolated = Valet.shared.sites.filter { site in - site.isolatedPhpVersion != nil - }.map { site in - return site.isolatedPhpVersion!.versionNumber.homebrewVersion - } - - var versions: Set = [version] - - if Valet.enabled(feature: .isolatedSites) { - versions = versions.union(isolated) - } + let versions = getVersionsToBeHandled(version) let group = DispatchGroup() @@ -63,7 +53,28 @@ class InternalSwitcher: PhpSwitcher { } } - private func disableDefaultPhpFpmPool(_ version: String) { + func getVersionsToBeHandled(_ primary: String) -> Set { + let isolated = Valet.shared.sites.filter { site in + site.isolatedPhpVersion != nil + }.map { site in + return site.isolatedPhpVersion!.versionNumber.homebrewVersion + } + + var versions: Set = [primary] + + if Valet.enabled(feature: .isolatedSites) { + versions = versions.union(isolated) + } + + return versions + } + + func requiresDisablingOfDefaultPhpFpmPool(_ version: String) -> Bool { + let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf" + return FileManager.default.fileExists(atPath: pool) + } + + func disableDefaultPhpFpmPool(_ version: String) { let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf" if FileManager.default.fileExists(atPath: pool) { Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).") @@ -83,14 +94,14 @@ class InternalSwitcher: PhpSwitcher { } } - private func stopPhpVersion(_ version: String) { + func stopPhpVersion(_ version: String) { let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)" brew("unlink \(formula)") brew("services stop \(formula)", sudo: true) Log.info("Unlinked and stopped services for \(formula)") } - private func startPhpVersion(_ version: String, primary: Bool) { + func startPhpVersion(_ version: String, primary: Bool) { let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)" if primary { diff --git a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift index 844c7c5..4d4aff0 100644 --- a/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift +++ b/phpmon/Domain/Integrations/Homebrew/HomebrewDiagnostics.swift @@ -42,6 +42,35 @@ class HomebrewDiagnostics { } } + /** + It is possible to upgrade PHP, but forget running `valet install`. + This results in a scenario where a rogue www.conf file exists. + */ + public static func checkForPhpFpmPoolConflicts() { + Log.info("Checking for PHP-FPM pool conflicts...") + + // We'll need to know what the primary PHP version is + let primary = PhpEnv.shared.currentInstall.version.short + + // Versions to be handled + let switcher = InternalSwitcher() + var versions = switcher.getVersionsToBeHandled(primary) + + versions = versions.filter { version in + return switcher.requiresDisablingOfDefaultPhpFpmPool(version) + } + + if versions.isEmpty { + Log.info("No PHP-FPM pools need to be fixed. All OK.") + } + + versions.forEach { version in + switcher.disableDefaultPhpFpmPool(version) + switcher.stopPhpVersion(version) + switcher.startPhpVersion(version, primary: version == primary) + } + } + /** Check if the alias conflict as documented in `checkForCaskConflict` actually occurred. */ diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 7c8b6bd..8ea5c72 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -113,20 +113,11 @@ class Valet { } /** - Starts the preload of sites, but only if the maximum amount of sites is 30. - For users with more sites, the site list is loaded when they bring up the site list window. - (This is done to keep the startup speed as fast as possible.) + Starts the preload of sites. In order to make sure PHP Monitor can correctly + handle all PHP versions including isolation, it needs to know about all sites. */ public func startPreloadingSites() { - let maximumPreload = 50 - let foundSites = self.countPaths() - if foundSites <= maximumPreload { - // Preload the sites and their drivers - Log.info("Fewer than or \(maximumPreload) sites found, preloading list of sites...") - self.reloadSites() - } else { - Log.info("\(foundSites) sites found, exceeds \(maximumPreload) for preload at launch!") - } + self.reloadSites() } /** diff --git a/phpmon/Domain/Menu/MainMenu+Startup.swift b/phpmon/Domain/Menu/MainMenu+Startup.swift index 43badf9..be76308 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -49,35 +49,20 @@ extension MainMenu { // Check for an alias conflict HomebrewDiagnostics.checkForCaskConflict() + // Update the icon updatePhpVersionInStatusBar() - Log.info("Determining broken PHP-FPM...") // Attempt to find out if PHP-FPM is broken + Log.info("Determining broken PHP-FPM...") let installation = PhpEnv.phpInstall installation.notifyAboutBrokenPhpFpm() - // Set up the config watchers on launch - // (these are automatically updated via delegate methods if the user switches) + // Set up the config watchers on launch (updated automatically when switching) Log.info("Setting up watchers...") App.shared.handlePhpConfigWatcher() - // Detect applications (preset + custom) - Log.info("Detecting applications...") - App.shared.detectedApplications = Application.detectPresetApplications() - - let customApps = Preferences.custom.scanApps.map { appName in - return Application(appName, .user_supplied) - }.filter { app in - return app.isInstalled() - } - - App.shared.detectedApplications.append(contentsOf: customApps) - - let appNames = App.shared.detectedApplications.map { app in - return app.name - } - - Log.info("Detected applications: \(appNames)") + // Detect built-in and custom applications + detectApplications() // Load the global hotkey App.shared.loadGlobalHotkey() @@ -85,29 +70,28 @@ extension MainMenu { // Preload sites Valet.shared.startPreloadingSites() + // After preloading sites, check for PHP-FPM pool conflicts + HomebrewDiagnostics.checkForPhpFpmPoolConflicts() + // A non-default TLD is not officially supported since Valet 3.2.x Valet.notifyAboutUnsupportedTLD() + // Update the services list in the background NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil) - // Schedule a request to fetch the PHP version every 60 seconds - DispatchQueue.main.async { [self] in - App.shared.timer = Timer.scheduledTimer( - timeInterval: 60, - target: self, - selector: #selector(refreshActiveInstallation), - userInfo: nil, - repeats: true - ) - } + // Start the background refresh timer + startSharedTimer() + // Update the stats Stats.incrementSuccessfulLaunchCount() Stats.evaluateSponsorMessageShouldBeDisplayed() + // Check for updates DispatchQueue.global(qos: .utility).async { AppUpdateChecker.checkIfNewerVersionIsAvailable() } + // We are ready! Log.info("PHP Monitor is ready to serve!") } @@ -133,4 +117,43 @@ extension MainMenu { Task { await startup() } } } + + /** + Schedule a request to fetch the PHP version every 60 seconds. + */ + private func startSharedTimer() { + DispatchQueue.main.async { [self] in + App.shared.timer = Timer.scheduledTimer( + timeInterval: 60, + target: self, + selector: #selector(refreshActiveInstallation), + userInfo: nil, + repeats: true + ) + } + } + + /** + Detect which applications are installed that can be used to open a domain's source directory. + */ + private func detectApplications() { + Log.info("Detecting applications...") + + App.shared.detectedApplications = Application.detectPresetApplications() + + let customApps = Preferences.custom.scanApps.map { appName in + return Application(appName, .user_supplied) + }.filter { app in + return app.isInstalled() + } + + App.shared.detectedApplications.append(contentsOf: customApps) + + let appNames = App.shared.detectedApplications.map { app in + return app.name + } + + Log.info("Detected applications: \(appNames)") + } + }