From 5594130ccd8c17295b311aef982b5d7f71b877ce Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Tue, 24 Oct 2023 18:24:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20Cleanup=20filesystem=20watcher?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 44 +++-- phpmon/Common/PHP/PhpConfigurationFile.swift | 4 +- phpmon/Domain/App/App.swift | 13 +- phpmon/Domain/Watcher/App+BrewWatch.swift | 17 +- phpmon/Domain/Watcher/App+ConfigWatch.swift | 32 ++-- phpmon/Domain/Watcher/ConfigFSNotifier.swift | 77 +++++++++ .../Domain/Watcher/ConfigWatchManager.swift | 82 +++++++++ .../Watcher}/FSNotifier.swift | 6 +- phpmon/Domain/Watcher/PhpConfigWatcher.swift | 155 ------------------ 9 files changed, 223 insertions(+), 207 deletions(-) create mode 100644 phpmon/Domain/Watcher/ConfigFSNotifier.swift create mode 100644 phpmon/Domain/Watcher/ConfigWatchManager.swift rename phpmon/{Common/Helpers => Domain/Watcher}/FSNotifier.swift (95%) delete mode 100644 phpmon/Domain/Watcher/PhpConfigWatcher.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 7ee0407..f45a157 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -162,6 +162,10 @@ C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; }; C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; }; C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; }; + C441CC562AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; }; + C441CC572AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; }; + C441CC582AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; }; + C441CC592AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */; }; C44264BE2850B86C007400F1 /* SwiftUIHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */; }; C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */; }; C4463FCC29804BCB007B93D5 /* RCFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4463FCB29804BCB007B93D5 /* RCFile.swift */; }; @@ -424,7 +428,7 @@ C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; }; C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; C471E87B28F9BB650021E251 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; - C471E87C28F9BB650021E251 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; + C471E87C28F9BB650021E251 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; }; C471E87D28F9BB650021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; }; C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C471E87F28F9BB650021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; }; @@ -512,7 +516,7 @@ C471E8DB28F9BB8F0021E251 /* TerminalProgressWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* TerminalProgressWindowController.swift */; }; C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44A874728905BB000498BC4 /* ProgressVC.swift */; }; C471E8DE28F9BB8F0021E251 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; - C471E8DF28F9BB8F0021E251 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; + C471E8DF28F9BB8F0021E251 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; }; C471E8E028F9BB8F0021E251 /* Preset.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C5C9B2846A40600E28255 /* Preset.swift */; }; C471E8E128F9BB8F0021E251 /* PresetHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463E37F284930EE00422731 /* PresetHelper.swift */; }; C471E8E228F9BB8F0021E251 /* WarningView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4297F7928970D59004C4630 /* WarningView.swift */; }; @@ -678,8 +682,8 @@ C4C8900728F0E3EF00CE5E97 /* ActiveFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */; }; C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; }; - C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; - C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; }; + C4C8E81B276F54E5003AC782 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; }; + C4C8E81C276F54E5003AC782 /* ConfigWatchManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */; }; C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB250429B28BB800CA4492 /* MainMenuTest.swift */; }; C4CB6E65292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; }; C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CB6E64292C362C002E9027 /* Homebrew.swift */; }; @@ -946,6 +950,7 @@ C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = ""; }; C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = ""; }; C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.swift; sourceTree = ""; }; + C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigFSNotifier.swift; sourceTree = ""; }; C44264BD2850B86C007400F1 /* SwiftUIHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIHelper.swift; sourceTree = ""; }; C44264BF2850BD2A007400F1 /* VersionPopoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionPopoverView.swift; sourceTree = ""; }; C4463FCB29804BCB007B93D5 /* RCFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RCFile.swift; sourceTree = ""; }; @@ -1054,7 +1059,7 @@ C4C8900428F0E3D100CE5E97 /* RealFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealFileSystem.swift; sourceTree = ""; }; C4C8900628F0E3EF00CE5E97 /* ActiveFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveFileSystem.swift; sourceTree = ""; }; C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = ""; }; - C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = ""; }; + C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigWatchManager.swift; sourceTree = ""; }; C4CB250429B28BB800CA4492 /* MainMenuTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuTest.swift; sourceTree = ""; }; C4CB6E64292C362C002E9027 /* Homebrew.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Homebrew.swift; sourceTree = ""; }; C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = ""; }; @@ -1745,7 +1750,6 @@ C4B5635D276AB09000F12CCB /* VersionExtractor.swift */, C4D3660A29113F20006BD146 /* System.swift */, C4D36614291160A1006BD146 /* WIP.swift */, - C41ADCE72970CCC700120423 /* FSNotifier.swift */, C47DF1AE299D5A3B0007055D /* LoginItemManager.swift */, C49EAA5129B12A5A00AB28FC /* Measurements.swift */, ); @@ -1964,9 +1968,11 @@ C4C8E81D276F5686003AC782 /* Watcher */ = { isa = PBXGroup; children = ( + C4C8E81A276F54E5003AC782 /* ConfigWatchManager.swift */, + C441CC552AE8249400DDFACD /* ConfigFSNotifier.swift */, + C41ADCE72970CCC700120423 /* FSNotifier.swift */, C49EAA5629B1689200AB28FC /* App+BrewWatch.swift */, C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */, - C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */, ); path = Watcher; sourceTree = ""; @@ -2506,8 +2512,9 @@ C4D4CB3729C109CF00DB9F93 /* InternalSwitcher+Valet.swift in Sources */, C46EBC4728DB9644007ACC74 /* RealShell.swift in Sources */, C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, + C441CC562AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */, C44264C02850BD2A007400F1 /* VersionPopoverView.swift in Sources */, - C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, + C4C8E81B276F54E5003AC782 /* ConfigWatchManager.swift in Sources */, C417DC74277614690015E6EE /* Helpers.swift in Sources */, C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, @@ -2683,7 +2690,7 @@ C471E87828F9BB650021E251 /* TerminalProgressWindowController.swift in Sources */, C471E87928F9BB650021E251 /* ProgressVC.swift in Sources */, C471E87B28F9BB650021E251 /* App+ConfigWatch.swift in Sources */, - C471E87C28F9BB650021E251 /* PhpConfigWatcher.swift in Sources */, + C471E87C28F9BB650021E251 /* ConfigWatchManager.swift in Sources */, C471E87D28F9BB650021E251 /* Preset.swift in Sources */, C471E87E28F9BB650021E251 /* PresetHelper.swift in Sources */, C471E87F28F9BB650021E251 /* WarningView.swift in Sources */, @@ -2728,6 +2735,7 @@ C471E7E828F9BAC20021E251 /* Actions.swift in Sources */, C40D72612A018AE30054A067 /* BrewFormula+UI.swift in Sources */, C471E82528F9BB2E0021E251 /* ComposerWindow.swift in Sources */, + C441CC582AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */, C471E80828F9BAD40021E251 /* PhpExtension.swift in Sources */, C471E7F928F9BACB0021E251 /* PhpSwitcher.swift in Sources */, C471E82A28F9BB330021E251 /* ValetListable.swift in Sources */, @@ -2785,6 +2793,7 @@ C471E89228F9BB8F0021E251 /* Alert.swift in Sources */, C471E89328F9BB8F0021E251 /* Application.swift in Sources */, C471E89428F9BB8F0021E251 /* LocalNotification.swift in Sources */, + C441CC592AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */, C40934A5298EEB2C00D25014 /* CaskFile.swift in Sources */, C471E89528F9BB8F0021E251 /* MenuBarImageGenerator.swift in Sources */, C40D725D2A018ACC0054A067 /* PhpFormulaeStatus.swift in Sources */, @@ -2871,7 +2880,7 @@ C471E8DC28F9BB8F0021E251 /* ProgressVC.swift in Sources */, C490E3BF29BCA376006D2DE6 /* Measurements.swift in Sources */, C471E8DE28F9BB8F0021E251 /* App+ConfigWatch.swift in Sources */, - C471E8DF28F9BB8F0021E251 /* PhpConfigWatcher.swift in Sources */, + C471E8DF28F9BB8F0021E251 /* ConfigWatchManager.swift in Sources */, C4CB250529B28BB800CA4492 /* MainMenuTest.swift in Sources */, C40D72622A018AE30054A067 /* BrewFormula+UI.swift in Sources */, C4B79ECE29CA475900A483EE /* RemovePhpVersionCommand.swift in Sources */, @@ -3018,7 +3027,7 @@ C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */, C485707928BF456C00539B36 /* ArrayExtension.swift in Sources */, C4F780CA25D80B75000DBC97 /* HomebrewDecodable.swift in Sources */, - C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */, + C4C8E81C276F54E5003AC782 /* ConfigWatchManager.swift in Sources */, C4F319C927B034A500AFF46F /* Stats.swift in Sources */, C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */, 54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */, @@ -3034,6 +3043,7 @@ C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */, C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */, C463E381284930EE00422731 /* PresetHelper.swift in Sources */, + C441CC572AE8249400DDFACD /* ConfigFSNotifier.swift in Sources */, C46FA98C2822F08F00D78807 /* PhpConfigurationFileTest.swift in Sources */, C4D5576529C77CC5001A44CD /* PhpVersionManagerWindowController.swift in Sources */, C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */, @@ -3492,7 +3502,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; @@ -3523,7 +3533,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; @@ -3763,7 +3773,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; @@ -3879,7 +3889,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; @@ -3995,7 +4005,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = YES; DEVELOPMENT_TEAM = 8M54J5J787; @@ -4176,7 +4186,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1335; + CURRENT_PROJECT_VERSION = 1336; DEAD_CODE_STRIPPING = YES; DEBUG = NO; DEVELOPMENT_TEAM = 8M54J5J787; diff --git a/phpmon/Common/PHP/PhpConfigurationFile.swift b/phpmon/Common/PHP/PhpConfigurationFile.swift index 8ba0dfa..3658615 100644 --- a/phpmon/Common/PHP/PhpConfigurationFile.swift +++ b/phpmon/Common/PHP/PhpConfigurationFile.swift @@ -97,14 +97,14 @@ class PhpConfigurationFile: CreatedFromFile { self.lines[item.lineIndex] = components.joined(separator: "=") // Ensure the watchers aren't tripped up by config changes - PhpConfigWatcher.ignoresModificationsToConfigValues = true + ConfigWatchManager.ignoresModificationsToConfigValues = true // Finally, join the string and save the file atomatically again try self.lines.joined(separator: "\n") .write(toFile: self.filePath, atomically: true, encoding: .utf8) // Ensure watcher behaviour is reverted - PhpConfigWatcher.ignoresModificationsToConfigValues = false + ConfigWatchManager.ignoresModificationsToConfigValues = false // Reload the original file self.reload() diff --git a/phpmon/Domain/App/App.swift b/phpmon/Domain/App/App.swift index 3c89866..52bff0d 100644 --- a/phpmon/Domain/App/App.swift +++ b/phpmon/Domain/App/App.swift @@ -89,9 +89,6 @@ class App { /** The warning manager, responsible for keeping track of warnings. */ var warnings = WarningManager.shared - /** The filesystem watchers, responsible for keeping track of changes to the PHP installation. */ - var watchers: [FSNotifier.Kind: FSNotifier] = [:] - /** Timer that will periodically reload info about the user's PHP installation. */ var timer: Timer? @@ -120,8 +117,12 @@ class App { // MARK: - App Watchers - /** - The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder. + /** Individual filesystem watchers, which are, i.e. responsible for watching the Homebrew folders. */ + var watchers: [String: FSNotifier] = [:] + + /** + The `ConfigWatchManager` is responsible for watching the `.ini` files and the `.conf.d` folder. + This manager object can immediately start or stop all watchers (or pause them) all at once. */ - var watcher: PhpConfigWatcher! + var watchManager: ConfigWatchManager! } diff --git a/phpmon/Domain/Watcher/App+BrewWatch.swift b/phpmon/Domain/Watcher/App+BrewWatch.swift index 6986b7b..deda43f 100644 --- a/phpmon/Domain/Watcher/App+BrewWatch.swift +++ b/phpmon/Domain/Watcher/App+BrewWatch.swift @@ -17,13 +17,13 @@ extension App { onChange: { Task { await self.onHomebrewPhpModification() } } ) - App.shared.watchers[.homebrewBinaries] = notifier + App.shared.watchers["homebrewBinaries"] = notifier } public func destroyHomebrewWatchers() { // Removing requires termination and then removing reference - self.watchers[.homebrewBinaries]?.terminate() - self.watchers[.homebrewBinaries] = nil + self.watchers["homebrewBinaries"]?.terminate() + self.watchers["homebrewBinaries"] = nil } public func onHomebrewPhpModification() async { @@ -31,10 +31,13 @@ extension App { Log.info("Something changed in the Homebrew binary directory...") await PhpEnvironments.detectPhpVersions() await MainMenu.shared.refreshActiveInstallation() - // let new = PhpEnvironments.shared.currentInstall?.version.text - // TODO: - // Check if the new and previous version are different - // if so, we can show a notification if needed + // + // TODO: PHP Guard 2.0 + // Check if the new and previous version of PHP are different + // if so, we can show a notification if needed or alert the user + // + // let new = PhpEnvironments.shared.currentInstall?.version.text + // } } diff --git a/phpmon/Domain/Watcher/App+ConfigWatch.swift b/phpmon/Domain/Watcher/App+ConfigWatch.swift index 9588b80..cc2098c 100644 --- a/phpmon/Domain/Watcher/App+ConfigWatch.swift +++ b/phpmon/Domain/Watcher/App+ConfigWatch.swift @@ -10,52 +10,52 @@ import Foundation extension App { - func startWatcher(_ url: URL) { - Log.perf("No watcher currently active...") - self.watcher = PhpConfigWatcher(for: url) + func startWatchManager(_ url: URL) { + Log.perf("Starting config watch manager...") + self.watchManager = ConfigWatchManager(for: url) - self.watcher.didChange = { url in + self.watchManager.didChange = { url in Log.perf("Something has changed in: \(url)") // Check if the watcher has last updated the menu less than 0.75s ago - let distance = self.watcher.lastUpdate?.distance(to: Date().timeIntervalSince1970) + let distance = self.watchManager.lastUpdate?.distance(to: Date().timeIntervalSince1970) if distance == nil || distance != nil && distance! > 0.75 { Log.perf("Refreshing menu...") Task { @MainActor in MainMenu.shared.reloadPhpMonitorMenuInBackground() } - self.watcher.lastUpdate = Date().timeIntervalSince1970 + self.watchManager.lastUpdate = Date().timeIntervalSince1970 } } } func handlePhpConfigWatcher(forceReload: Bool = false) { if ActiveFileSystem.shared is TestableFileSystem { - Log.warn("FS watcher is disabled when using testable filesystem.") + Log.warn("Config watch manager is disabled when using testable filesystem.") return } guard let install = PhpEnvironments.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.") + Log.info("The config watch manager 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 manager exists and schedule on the main thread // if we don't consistently do this, the app will create duplicate watchers - // due to timing issues, which creates retain cycles. + // due to timing issues, which creates retain cycles Task { @MainActor in // Watcher needs to be created - if self.watcher == nil { - self.startWatcher(url) + if self.watchManager == nil { + self.startWatchManager(url) } // Watcher needs to be updated - if self.watcher.url != url || forceReload { - self.watcher.disable() - self.watcher = nil + if self.watchManager.url != url || forceReload { + self.watchManager.disable() + self.watchManager = nil Log.perf("Watcher has stopped watching files. Starting new one...") - self.startWatcher(url) + self.startWatchManager(url) } } } diff --git a/phpmon/Domain/Watcher/ConfigFSNotifier.swift b/phpmon/Domain/Watcher/ConfigFSNotifier.swift new file mode 100644 index 0000000..786ab47 --- /dev/null +++ b/phpmon/Domain/Watcher/ConfigFSNotifier.swift @@ -0,0 +1,77 @@ +// +// ConfigFSNotifier.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 24/10/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class ConfigFSNotifier { + + enum Behaviour { + case reloadsMenu + case reloadsWatchers + } + + private var parent: ConfigWatchManager! + + private var monitoredFolderFileDescriptor: CInt = -1 + + private var folderMonitorSource: DispatchSourceFileSystemObject? + + let url: URL + + init( + for url: URL, + eventMask: DispatchSource.FileSystemEvent, + parent: ConfigWatchManager, + behaviour: ConfigFSNotifier.Behaviour = .reloadsMenu + ) { + self.url = url + self.parent = parent + self.startMonitoring(eventMask, behaviour: behaviour) + } + + func startMonitoring( + _ eventMask: DispatchSource.FileSystemEvent, + behaviour: ConfigFSNotifier.Behaviour + ) { + guard folderMonitorSource == nil && monitoredFolderFileDescriptor == -1 else { + return + } + + monitoredFolderFileDescriptor = open(url.path, O_EVTONLY) + folderMonitorSource = DispatchSource.makeFileSystemObjectSource( + fileDescriptor: monitoredFolderFileDescriptor, + eventMask: eventMask, + queue: parent.folderMonitorQueue + ) + + folderMonitorSource?.setEventHandler { [weak self] in + if behaviour == .reloadsWatchers + && !ConfigWatchManager.ignoresModificationsToConfigValues { + // Reload all configuration watchers + return App.shared.handlePhpConfigWatcher(forceReload: true) + } + + + self?.parent.didChange?(self!.url) + } + + folderMonitorSource?.setCancelHandler { [weak self] in + guard let self = self else { return } + close(self.monitoredFolderFileDescriptor) + self.monitoredFolderFileDescriptor = -1 + self.folderMonitorSource = nil + } + + folderMonitorSource?.resume() + } + + func stopMonitoring() { + folderMonitorSource?.cancel() + self.parent = nil + } +} diff --git a/phpmon/Domain/Watcher/ConfigWatchManager.swift b/phpmon/Domain/Watcher/ConfigWatchManager.swift new file mode 100644 index 0000000..7b780ab --- /dev/null +++ b/phpmon/Domain/Watcher/ConfigWatchManager.swift @@ -0,0 +1,82 @@ +// +// ConfigWatchManager.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 30/03/2021. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class ConfigWatchManager { + + static var ignoresModificationsToConfigValues: Bool = false + + let folderMonitorQueue = DispatchQueue(label: "FolderMonitorQueue", attributes: .concurrent) + + let url: URL + var didChange: ((URL) -> Void)? + var lastUpdate: TimeInterval? + + var watchers: [ConfigFSNotifier] = [] + + init(for url: URL) { + if FileSystem is TestableFileSystem { + fatalError(""" + PhpConfigWatcher is not compatible with testable FS!" + You are not allowed to instantiate these while using a testable FS. + """) + } + + self.url = url + + // Add a watcher for php.ini + self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write) + + // Add a watcher for conf.d (in case a new file is added or a file is deleted) + // This watcher, when triggered, will restart all watchers + self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all, behaviour: .reloadsWatchers) + + // Scan the conf.d folder for .ini files, and add a watcher for each file + let filePaths = FileManager.default.enumerator( + atPath: self.url.appendingPathComponent("conf.d").path + )?.allObjects as! [String] + + // Loop over the .ini files that we discovered + filePaths.filter { $0.contains(".ini") }.forEach { (file) in + // Add a watcher for each file we have discovered + self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write) + } + + Log.perf("A watcher exists for the following config paths:") + Log.perf(self.watchers.map({ watcher in + return watcher.url.relativePath + })) + } + + func addWatcher( + for url: URL, + eventMask: DispatchSource.FileSystemEvent, + behaviour: ConfigFSNotifier.Behaviour = .reloadsMenu + ) { + if !FileSystem.anyExists(url.path) { + Log.warn("No watcher was created for \(url.path) because the requested file does not exist.") + return + } + + let watcher = ConfigFSNotifier(for: url, eventMask: eventMask, parent: self, behaviour: behaviour) + self.watchers.append(watcher) + } + + func disable() { + Log.perf("Turning off all individual existing watchers...") + self.watchers.forEach { (watcher) in + watcher.stopMonitoring() + } + } + + deinit { + Log.perf("deinit: \(String(describing: self)).\(#function)") + } + +} diff --git a/phpmon/Common/Helpers/FSNotifier.swift b/phpmon/Domain/Watcher/FSNotifier.swift similarity index 95% rename from phpmon/Common/Helpers/FSNotifier.swift rename to phpmon/Domain/Watcher/FSNotifier.swift index 349d78e..01332c8 100644 --- a/phpmon/Common/Helpers/FSNotifier.swift +++ b/phpmon/Domain/Watcher/FSNotifier.swift @@ -6,12 +6,9 @@ // Copyright © 2023 Nico Verbruggen. All rights reserved. // -import Cocoa +import Foundation class FSNotifier { - enum Kind { - case homebrewLocks, homebrewBinaries - } public static var shared: FSNotifier! = nil @@ -66,4 +63,5 @@ class FSNotifier { deinit { Log.perf("FSNotifier for \(self.url) will be deinitialized.") } + } diff --git a/phpmon/Domain/Watcher/PhpConfigWatcher.swift b/phpmon/Domain/Watcher/PhpConfigWatcher.swift deleted file mode 100644 index 9a585f5..0000000 --- a/phpmon/Domain/Watcher/PhpConfigWatcher.swift +++ /dev/null @@ -1,155 +0,0 @@ -// -// FolderWatcher.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 30/03/2021. -// Copyright © 2023 Nico Verbruggen. All rights reserved. -// - -import Foundation - -class PhpConfigWatcher { - - static var ignoresModificationsToConfigValues: Bool = false - - let folderMonitorQueue = DispatchQueue(label: "FolderMonitorQueue", attributes: .concurrent) - - let url: URL - - var didChange: ((URL) -> Void)? - - var lastUpdate: TimeInterval? - - var watchers: [FSWatcher] = [] - - init(for url: URL) { - if FileSystem is TestableFileSystem { - fatalError(""" - PhpConfigWatcher is not compatible with testable FS!" - You are not allowed to instantiate these while using a testable FS. - """) - } - - self.url = url - - // Add a watcher for php.ini - self.addWatcher(for: self.url.appendingPathComponent("php.ini"), eventMask: .write) - - // Add a watcher for conf.d (in case a new file is added or a file is deleted) - // This watcher, when triggered, will restart all watchers - self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all, behaviour: .reloadsWatchers) - - // Scan the conf.d folder for .ini files, and add a watcher for each file - let enumerator = FileManager.default.enumerator(atPath: self.url.appendingPathComponent("conf.d").path) - let filePaths = enumerator?.allObjects as! [String] - - // Loop over the .ini files that we discovered - filePaths.filter { $0.contains(".ini") }.forEach { (file) in - // Add a watcher for each file we have discovered - self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write) - } - - Log.perf("A watcher exists for the following config paths:") - Log.perf(self.watchers.map({ watcher in - return watcher.url.relativePath - })) - } - - func addWatcher( - for url: URL, - eventMask: DispatchSource.FileSystemEvent, - behaviour: FSWatcherBehaviour = .reloadsMenu - ) { - if !FileSystem.anyExists(url.path) { - Log.warn("No watcher was created for \(url.path) because the requested file does not exist.") - return - } - - let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self, behaviour: behaviour) - self.watchers.append(watcher) - } - - func disable() { - Log.perf("Turning off all individual existing watchers...") - self.watchers.forEach { (watcher) in - watcher.stopMonitoring() - } - } - - deinit { - Log.perf("deinit: \(String(describing: self)).\(#function)") - } - -} - -enum FSWatcherBehaviour { - case reloadsMenu - case reloadsWatchers -} - -class FSWatcher { - - private var parent: PhpConfigWatcher! - - private var monitoredFolderFileDescriptor: CInt = -1 - - private var folderMonitorSource: DispatchSourceFileSystemObject? - - let url: URL - - init( - for url: URL, - eventMask: DispatchSource.FileSystemEvent, - parent: PhpConfigWatcher, - behaviour: FSWatcherBehaviour = .reloadsMenu - ) { - self.url = url - self.parent = parent - self.startMonitoring(eventMask, behaviour: behaviour) - } - - func startMonitoring( - _ eventMask: DispatchSource.FileSystemEvent, - behaviour: FSWatcherBehaviour - ) { - guard folderMonitorSource == nil && monitoredFolderFileDescriptor == -1 else { - return - } - - // Open the file or folder referenced by URL for monitoring only. - monitoredFolderFileDescriptor = open(url.path, O_EVTONLY) - folderMonitorSource = DispatchSource.makeFileSystemObjectSource( - fileDescriptor: monitoredFolderFileDescriptor, - eventMask: eventMask, - queue: parent.folderMonitorQueue - ) - - // Define the block to call when a file change is detected. - folderMonitorSource?.setEventHandler { [weak self] in - if behaviour == .reloadsWatchers - && !PhpConfigWatcher.ignoresModificationsToConfigValues { - // Reload all configuration watchers - return App.shared.handlePhpConfigWatcher(forceReload: true) - } - - // The default behaviour is to reload the menu - self?.parent.didChange?(self!.url) - } - - // Define a cancel handler to ensure the directory is closed when the source is cancelled. - folderMonitorSource?.setCancelHandler { [weak self] in - guard let self = self else { return } - close(self.monitoredFolderFileDescriptor) - self.monitoredFolderFileDescriptor = -1 - self.folderMonitorSource = nil - } - - // Start monitoring the directory via the source. - folderMonitorSource?.resume() - } - - func stopMonitoring() { - folderMonitorSource?.cancel() - self.parent = nil - } -}