1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-11-09 13:10:24 +01:00

🔀 Merge WIP changes from feature/config-watcher into 5.x

This commit is contained in:
2021-12-19 12:56:16 +01:00
parent 267a1dac94
commit 0d75e4c3b2
6 changed files with 189 additions and 3 deletions

View 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)
}
}
}
}

View File

@@ -45,9 +45,13 @@ class App {
/** Whether the application is busy switching versions. */
var busy: Bool = false
/** The currently active installation of PHP. */
var currentInstall: ActivePhpInstallation? = nil
var currentInstall: ActivePhpInstallation? = nil {
didSet {
handlePhpConfigWatcher()
}
}
/** All available versions of PHP. */
var availablePhpVersions: [String] = []
@@ -102,4 +106,10 @@ class App {
*/
var openWindows: [String] = []
// MARK: - App Watchers
/**
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
*/
var watcher: PhpConfigWatcher!
}

View 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
}
}

View File

@@ -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() {
waitAndExecute {
// This automatically reloads the menu

View File

@@ -189,4 +189,4 @@ class ExtensionMenuItem: NSMenuItem {
class EditorMenuItem: NSMenuItem {
var editor: Application? = nil
}
}