mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
👌 Improve PHP Doctor w/ async code
- This fixes an issue with PHP Doctor's "Scan Again" button not working. - This also adds a new check which verifies if "php.ini", "php-fpm.conf" and "php-fpm.d/valet-fpm.conf" exist (all required files).
This commit is contained in:
@ -132,6 +132,7 @@
|
|||||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
||||||
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; };
|
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; };
|
||||||
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; };
|
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; };
|
||||||
|
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */; };
|
||||||
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
||||||
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
||||||
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
||||||
@ -834,6 +835,7 @@
|
|||||||
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
||||||
C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; };
|
C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; };
|
||||||
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; };
|
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; };
|
||||||
|
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigChecker.swift; sourceTree = "<group>"; };
|
||||||
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; };
|
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; };
|
||||||
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
|
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
|
||||||
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
@ -1263,6 +1265,7 @@
|
|||||||
C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
|
C422DDAB28A2DAA100CEAC97 /* Warnings */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C43FDBE729A9329A003D85EC /* Services */,
|
||||||
C47699F028A2F3150060FEB8 /* Warning.swift */,
|
C47699F028A2F3150060FEB8 /* Warning.swift */,
|
||||||
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
|
C47699EE28A2F2A30060FEB8 /* WarningManager.swift */,
|
||||||
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
|
C422DDAC28A2DAC600CEAC97 /* WarningsWindowController.swift */,
|
||||||
@ -1288,6 +1291,14 @@
|
|||||||
path = Warning;
|
path = Warning;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C43FDBE729A9329A003D85EC /* Services */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C43FDBE829A932B0003D85EC /* PhpConfigChecker.swift */,
|
||||||
|
);
|
||||||
|
path = Services;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44067F327E256560045BD4E /* Cells */ = {
|
C44067F327E256560045BD4E /* Cells */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -2238,6 +2249,7 @@
|
|||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
|
C4FACE83288F1F9700FC478F /* OnboardingWindowController.swift in Sources */,
|
||||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
|
C43FDBE929A932B0003D85EC /* PhpConfigChecker.swift in Sources */,
|
||||||
C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */,
|
C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */,
|
||||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */,
|
C451AFF62969E40F0078E617 /* HelpButton.swift in Sources */,
|
||||||
|
@ -9,10 +9,14 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct WarningListView: View {
|
struct WarningListView: View {
|
||||||
@State var warnings: [Warning]
|
@ObservedObject var warningManager: WarningManager
|
||||||
|
|
||||||
init(empty: Bool = false) {
|
init(empty: Bool = false) {
|
||||||
self.warnings = empty ? [] : WarningManager.shared.warnings
|
if empty {
|
||||||
|
WarningManager.shared.warnings = []
|
||||||
|
}
|
||||||
|
|
||||||
|
warningManager = WarningManager.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@ -40,7 +44,6 @@ struct WarningListView: View {
|
|||||||
Button("warnings.refresh.button".localizedForSwiftUI) {
|
Button("warnings.refresh.button".localizedForSwiftUI) {
|
||||||
Task { // Reload warnings
|
Task { // Reload warnings
|
||||||
await WarningManager.shared.checkEnvironment()
|
await WarningManager.shared.checkEnvironment()
|
||||||
self.warnings = WarningManager.shared.warnings
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text("warnings.refresh.button.description".localizedForSwiftUI)
|
Text("warnings.refresh.button.description".localizedForSwiftUI)
|
||||||
@ -51,14 +54,14 @@ struct WarningListView: View {
|
|||||||
|
|
||||||
List {
|
List {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
if warnings.isEmpty {
|
if warningManager.warnings.isEmpty {
|
||||||
NoWarningsView()
|
NoWarningsView()
|
||||||
} else {
|
} else {
|
||||||
ForEach(warnings) { warning in
|
ForEach(warningManager.warnings) { warning in
|
||||||
Group {
|
Group {
|
||||||
WarningView(
|
WarningView(
|
||||||
title: warning.title,
|
title: warning.title,
|
||||||
paragraphs: warning.paragraphs,
|
paragraphs: warning.paragraphs(),
|
||||||
documentationUrl: warning.url
|
documentationUrl: warning.url
|
||||||
)
|
)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
@ -67,7 +70,8 @@ struct WarningListView: View {
|
|||||||
}.padding(5)
|
}.padding(5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.frame(minHeight: 0, maxHeight: .infinity).padding(5)
|
}
|
||||||
|
.frame(minHeight: 0, maxHeight: .infinity).padding(5)
|
||||||
}
|
}
|
||||||
.listRowInsets(EdgeInsets())
|
.listRowInsets(EdgeInsets())
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
|
@ -26,7 +26,7 @@ struct WarningView: View {
|
|||||||
Text(title.localizedForSwiftUI)
|
Text(title.localizedForSwiftUI)
|
||||||
.fontWeight(.bold)
|
.fontWeight(.bold)
|
||||||
ForEach(paragraphs, id: \.self) { paragraph in
|
ForEach(paragraphs, id: \.self) { paragraph in
|
||||||
Text(paragraph.localizedForSwiftUI)
|
Text(paragraph)
|
||||||
.font(.system(size: 13))
|
.font(.system(size: 13))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
39
phpmon/Domain/Warnings/Services/PhpConfigChecker.swift
Normal file
39
phpmon/Domain/Warnings/Services/PhpConfigChecker.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// PhpConfigChecker.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 24/02/2023.
|
||||||
|
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpConfigChecker {
|
||||||
|
|
||||||
|
public static var shared = PhpConfigChecker()
|
||||||
|
|
||||||
|
var missing: [String] = []
|
||||||
|
|
||||||
|
public func check() {
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
let shouldExist = [
|
||||||
|
"php.ini",
|
||||||
|
"php-fpm.conf",
|
||||||
|
"php-fpm.d/valet-fpm.conf"
|
||||||
|
]
|
||||||
|
|
||||||
|
for version in PhpEnv.shared.availablePhpVersions {
|
||||||
|
for file in shouldExist {
|
||||||
|
let fullFilePath = Paths.etcPath.appending("/php/\(version)/\(file)")
|
||||||
|
if !FileSystem.fileExists(fullFilePath) {
|
||||||
|
missing.append(fullFilePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !missing.isEmpty {
|
||||||
|
Log.warn("The following config file(s) were missing: \(missing)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,19 +8,27 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Warning: Identifiable {
|
struct Warning: Identifiable, Hashable {
|
||||||
var id = UUID()
|
var id = UUID()
|
||||||
let command: () async -> Bool
|
let command: () async -> Bool
|
||||||
let name: String
|
let name: String
|
||||||
let title: String
|
let title: String
|
||||||
let paragraphs: [String]
|
let paragraphs: () -> [String]
|
||||||
let url: String?
|
let url: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
- Parameters:
|
||||||
|
- command: The command that, if it returns true, means that a warning applies
|
||||||
|
- name: The internal name or description for this warning
|
||||||
|
- title: The title displayed for the user
|
||||||
|
- paragraphs: The main body of text displayed for the user
|
||||||
|
- url: The URL that one can navigate to for more information (if applicable)
|
||||||
|
*/
|
||||||
init(
|
init(
|
||||||
command: @escaping () async -> Bool,
|
command: @escaping () async -> Bool,
|
||||||
name: String,
|
name: String,
|
||||||
title: String,
|
title: String,
|
||||||
paragraphs: [String],
|
paragraphs: @escaping () -> [String],
|
||||||
url: String?
|
url: String?
|
||||||
) {
|
) {
|
||||||
self.command = command
|
self.command = command
|
||||||
@ -33,4 +41,12 @@ struct Warning: Identifiable {
|
|||||||
public func applies() async -> Bool {
|
public func applies() async -> Bool {
|
||||||
return await self.command()
|
return await self.command()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func == (lhs: Warning, rhs: Warning) -> Bool {
|
||||||
|
return lhs.hashValue == rhs.hashValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(self.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class WarningManager {
|
class WarningManager: ObservableObject {
|
||||||
|
|
||||||
static var shared = WarningManager()
|
static var shared: WarningManager = WarningManager()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if isRunningSwiftUIPreview {
|
if isRunningSwiftUIPreview {
|
||||||
@ -26,8 +26,8 @@ class WarningManager {
|
|||||||
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
.trimmingCharacters(in: .whitespacesAndNewlines) == "1"
|
||||||
},
|
},
|
||||||
name: "Running PHP Monitor with Rosetta on M1",
|
name: "Running PHP Monitor with Rosetta on M1",
|
||||||
title: "warnings.arm_compatibility.title",
|
title: "warnings.arm_compatibility.title".localized,
|
||||||
paragraphs: ["warnings.arm_compatibility.description"],
|
paragraphs: { return ["warnings.arm_compatibility.description".localized] },
|
||||||
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon"
|
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-and-Apple-Silicon"
|
||||||
),
|
),
|
||||||
Warning(
|
Warning(
|
||||||
@ -36,13 +36,27 @@ class WarningManager {
|
|||||||
!FileSystem.isWriteableFile("/usr/local/bin/")
|
!FileSystem.isWriteableFile("/usr/local/bin/")
|
||||||
},
|
},
|
||||||
name: "Helpers cannot be symlinked and not in PATH",
|
name: "Helpers cannot be symlinked and not in PATH",
|
||||||
title: "warnings.helper_permissions.title",
|
title: "warnings.helper_permissions.title".localized,
|
||||||
paragraphs: [
|
paragraphs: { return [
|
||||||
"warnings.helper_permissions.description",
|
"warnings.helper_permissions.description".localized,
|
||||||
"warnings.helper_permissions.unavailable",
|
"warnings.helper_permissions.unavailable".localized,
|
||||||
"warnings.helper_permissions.symlink"
|
"warnings.helper_permissions.symlink".localized
|
||||||
],
|
] },
|
||||||
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
|
url: "https://github.com/nicoverbruggen/phpmon/wiki/PHP-Monitor-helper-binaries"
|
||||||
|
),
|
||||||
|
Warning(
|
||||||
|
command: {
|
||||||
|
PhpConfigChecker.shared.check()
|
||||||
|
return !PhpConfigChecker.shared.missing.isEmpty
|
||||||
|
},
|
||||||
|
name: "Your PHP installation is missing configuration files",
|
||||||
|
title: "warnings.files_missing.title".localized,
|
||||||
|
paragraphs: { return [
|
||||||
|
"warnings.files_missing.description".localized(
|
||||||
|
PhpConfigChecker.shared.missing.joined(separator: "\n• ")
|
||||||
|
)
|
||||||
|
] },
|
||||||
|
url: nil
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -60,11 +74,11 @@ class WarningManager {
|
|||||||
Checks the user's environment and checks if any special warnings apply.
|
Checks the user's environment and checks if any special warnings apply.
|
||||||
*/
|
*/
|
||||||
func checkEnvironment() async {
|
func checkEnvironment() async {
|
||||||
self.warnings = []
|
|
||||||
|
|
||||||
if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil {
|
if ProcessInfo.processInfo.environment["EXTREME_DOCTOR_MODE"] != nil {
|
||||||
// For debugging purposes, we may wish to see all possible evaluations listed
|
// For debugging purposes, we may wish to see all possible evaluations listed
|
||||||
self.warnings = self.evaluations
|
Task { @MainActor in
|
||||||
|
self.warnings = self.evaluations
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, loop over the actual evaluations and list the warnings
|
// Otherwise, loop over the actual evaluations and list the warnings
|
||||||
await loopOverEvaluations()
|
await loopOverEvaluations()
|
||||||
@ -74,9 +88,14 @@ class WarningManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func loopOverEvaluations() async {
|
private func loopOverEvaluations() async {
|
||||||
|
Task { @MainActor in
|
||||||
|
self.warnings = []
|
||||||
|
}
|
||||||
for check in self.evaluations where await check.applies() {
|
for check in self.evaluations where await check.applies() {
|
||||||
Log.info("[DOCTOR] \(check.name) (!)")
|
Log.info("[DOCTOR] \(check.name) (!)")
|
||||||
self.warnings.append(check)
|
Task { @MainActor in
|
||||||
|
self.warnings.append(check)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -664,6 +664,13 @@ COMMON TROUBLESHOOTING TIPS
|
|||||||
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
|
"warnings.arm_compatibility.title" = "You are running PHP Monitor using Rosetta on Apple Silicon, which means your PHP environment is also running via Rosetta.";
|
||||||
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
|
"warnings.arm_compatibility.description" = "You appear to be running an ARM-compatible version of macOS, but you are currently running PHP Monitor using Rosetta. While this will work correctly, it is recommended that you use the native version of Homebrew.";
|
||||||
|
|
||||||
|
"warnings.files_missing.title" = "Your PHP installation is lacking required configuration files";
|
||||||
|
"warnings.files_missing.description" = "The following files normally exist on a functional system:
|
||||||
|
|
||||||
|
• %@
|
||||||
|
|
||||||
|
When files like these are missing, it's recommended to reinstall the appropriate PHP version(s) via Homebrew again, which should restore the configuration files that are missing. Missing configuration files can be the reason why you get '502 Bad Gateway' errors, even after running Fix My Valet.";
|
||||||
|
|
||||||
"warnings.none" = "There are no recommendations available for you right now. You're all good!";
|
"warnings.none" = "There are no recommendations available for you right now. You're all good!";
|
||||||
|
|
||||||
// ONBOARDING
|
// ONBOARDING
|
||||||
|
Reference in New Issue
Block a user