diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 0532a33..37697fc 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -163,7 +163,6 @@ C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.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 */; }; @@ -239,7 +238,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 */; }; @@ -1141,13 +1139,11 @@ files = ( C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */, C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */, - C4AF9F71275445FF00D44ED0 /* valet-config.json 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 */, 54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */, C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */, 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 9e08d4c..9258b3a 100644 --- a/phpmon/Domain/Menu/MainMenu+Startup.swift +++ b/phpmon/Domain/Menu/MainMenu+Startup.swift @@ -49,20 +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) - self.loadApps() + // Detect built-in and custom applications + detectApplications() // Load the rollback preset PresetHelper.loadRollbackPresetFromFile() @@ -73,29 +73,27 @@ 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() ServicesManager.shared.loadData() - // 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!") } @@ -122,8 +120,27 @@ extension MainMenu { } } - private func loadApps() { + /** + 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