mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
🔀 Merge WIP changes from feature/config-watcher into 5.x
This commit is contained in:
@ -90,6 +90,10 @@
|
|||||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
||||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.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 */; };
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
||||||
@ -197,6 +201,8 @@
|
|||||||
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
||||||
|
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||||
|
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
||||||
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
||||||
@ -421,7 +427,9 @@
|
|||||||
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */,
|
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */,
|
||||||
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
||||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */,
|
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */,
|
||||||
|
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */,
|
||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||||
|
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
C41C1B4C22B0215A00E7CF16 /* Actions.swift */,
|
C41C1B4C22B0215A00E7CF16 /* Actions.swift */,
|
||||||
);
|
);
|
||||||
@ -597,6 +605,7 @@
|
|||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
||||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
|
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
|
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
|
||||||
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
||||||
@ -618,6 +627,7 @@
|
|||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
||||||
|
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
||||||
@ -654,6 +664,7 @@
|
|||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||||
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
@ -661,6 +672,7 @@
|
|||||||
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
||||||
C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */,
|
C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */,
|
||||||
|
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
|
44
phpmon/Domain/Core/App+ConfigWatch.swift
Normal file
44
phpmon/Domain/Core/App+ConfigWatch.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// App+ConfigWatch.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 30/03/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension App {
|
||||||
|
|
||||||
|
func startWatcher(_ url: URL) {
|
||||||
|
print("No watcher currently active...")
|
||||||
|
self.watcher = PhpConfigWatcher(for: url)
|
||||||
|
|
||||||
|
self.watcher.didChange = { url in
|
||||||
|
// TODO: Make sure this is debounced, because a single process may update the config file many times; this occurs when installing Xdebug, for example
|
||||||
|
print("Something has changed in: \(url)")
|
||||||
|
MainMenu.shared.reloadPhpMonitorMenuInBackground()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePhpConfigWatcher() {
|
||||||
|
if self.currentInstall != nil {
|
||||||
|
// 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 {
|
||||||
|
startWatcher(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watcher needs to be updated
|
||||||
|
if self.watcher.url != url {
|
||||||
|
self.watcher.disable()
|
||||||
|
self.watcher = nil
|
||||||
|
print("Watcher has stopped watching files. Starting new one...")
|
||||||
|
startWatcher(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -47,7 +47,11 @@ class App {
|
|||||||
var busy: Bool = false
|
var busy: Bool = false
|
||||||
|
|
||||||
/** The currently active installation of PHP. */
|
/** The currently active installation of PHP. */
|
||||||
var currentInstall: ActivePhpInstallation? = nil
|
var currentInstall: ActivePhpInstallation? = nil {
|
||||||
|
didSet {
|
||||||
|
handlePhpConfigWatcher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** All available versions of PHP. */
|
/** All available versions of PHP. */
|
||||||
var availablePhpVersions: [String] = []
|
var availablePhpVersions: [String] = []
|
||||||
@ -102,4 +106,10 @@ class App {
|
|||||||
*/
|
*/
|
||||||
var openWindows: [String] = []
|
var openWindows: [String] = []
|
||||||
|
|
||||||
|
// MARK: - App Watchers
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
||||||
|
*/
|
||||||
|
var watcher: PhpConfigWatcher!
|
||||||
}
|
}
|
||||||
|
113
phpmon/Domain/Core/PhpConfigWatcher.swift
Normal file
113
phpmon/Domain/Core/PhpConfigWatcher.swift
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
//
|
||||||
|
// FolderWatcher.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 30/03/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpConfigWatcher {
|
||||||
|
|
||||||
|
let folderMonitorQueue = DispatchQueue(label: "FolderMonitorQueue", attributes: .concurrent)
|
||||||
|
|
||||||
|
let url: URL
|
||||||
|
|
||||||
|
var didChange: ((URL) -> Void)?
|
||||||
|
|
||||||
|
var watchers: [FSWatcher] = []
|
||||||
|
|
||||||
|
init(for url: URL) {
|
||||||
|
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)
|
||||||
|
// TODO: Make sure that the contents of the conf.d folder is checked each time... this might mean
|
||||||
|
// that watchers are due for deletion / need to be created
|
||||||
|
self.addWatcher(for: self.url.appendingPathComponent("conf.d"), eventMask: .all)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
print("A watcher exists for the following config paths:")
|
||||||
|
for watcher in self.watchers {
|
||||||
|
print(watcher.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent) {
|
||||||
|
let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self)
|
||||||
|
self.watchers.append(watcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disable() {
|
||||||
|
print("Turning off existing watchers...")
|
||||||
|
self.watchers.forEach { (watcher) in
|
||||||
|
watcher.stopMonitoring()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("An existing config watcher has been deinitialized.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
self.url = url
|
||||||
|
self.parent = parent
|
||||||
|
self.startMonitoring(eventMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startMonitoring(_ eventMask: DispatchSource.FileSystemEvent) {
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -221,6 +221,13 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func reloadPhpMonitorMenuInBackground() {
|
||||||
|
waitAndExecute {
|
||||||
|
// This automatically reloads the menu
|
||||||
|
print("Reloading information about the PHP installation (in the background)...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func reloadPhpMonitorMenu() {
|
@objc func reloadPhpMonitorMenu() {
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
// This automatically reloads the menu
|
// This automatically reloads the menu
|
||||||
|
Reference in New Issue
Block a user