diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index bd8d7eb..5fb5846 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -551,6 +551,10 @@ 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 */; }; C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; }; + C4BB39392981AFC700F8E797 /* PhpVersionSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */; }; + C4BB393A2981AFC700F8E797 /* PhpVersionSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */; }; + C4BB393B2981AFC700F8E797 /* PhpVersionSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */; }; + C4BB393C2981AFC700F8E797 /* PhpVersionSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */; }; C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BF56AA2949381100379603 /* FakeValetInteractor.swift */; }; C4BF56AC2949381100379603 /* FakeValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BF56AA2949381100379603 /* FakeValetInteractor.swift */; }; C4BF56AD2949381100379603 /* FakeValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BF56AA2949381100379603 /* FakeValetInteractor.swift */; }; @@ -881,6 +885,7 @@ C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = ""; }; C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = ""; }; C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = ""; }; + C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionSource.swift; sourceTree = ""; }; C4BF56AA2949381100379603 /* FakeValetInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeValetInteractor.swift; sourceTree = ""; }; C4C0E8DE27F88AEB002D32A9 /* FakeDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeDomainScanner.swift; sourceTree = ""; }; C4C0E8E127F88B13002D32A9 /* ValetDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetDomainScanner.swift; sourceTree = ""; }; @@ -1550,7 +1555,7 @@ children = ( C4E4404527C56F4700D225E1 /* ValetSite.swift */, C41C02A827E61A65009F26CB /* FakeValetSite.swift */, - C4C0E8E427F88B1F002D32A9 /* SiteScanner */, + C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */, ); path = Sites; sourceTree = ""; @@ -1572,13 +1577,6 @@ path = Nginx; sourceTree = ""; }; - C4C0E8E427F88B1F002D32A9 /* SiteScanner */ = { - isa = PBXGroup; - children = ( - ); - path = SiteScanner; - sourceTree = ""; - }; C4C1019727C65A11001FACC2 /* Parsers */ = { isa = PBXGroup; children = ( @@ -2058,6 +2056,7 @@ C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */, + C4BB39392981AFC700F8E797 /* PhpVersionSource.swift in Sources */, C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4CDA893288F1A71007CE25F /* Keys.swift in Sources */, @@ -2169,6 +2168,7 @@ C471E83028F9BB650021E251 /* Application.swift in Sources */, C471E83128F9BB650021E251 /* LocalNotification.swift in Sources */, C471E83228F9BB650021E251 /* MenuBarImageGenerator.swift in Sources */, + C4BB393B2981AFC700F8E797 /* PhpVersionSource.swift in Sources */, C471E83328F9BB650021E251 /* PMWindowController.swift in Sources */, C471E83428F9BB650021E251 /* VersionExtractor.swift in Sources */, C471E83528F9BB650021E251 /* ValetProxy.swift in Sources */, @@ -2437,6 +2437,7 @@ C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C471E7FC28F9BACE0021E251 /* HomebrewPackage.swift in Sources */, C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */, + C4BB393C2981AFC700F8E797 /* PhpVersionSource.swift in Sources */, C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */, C471E7EE28F9BAC30021E251 /* Constants.swift in Sources */, C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */, @@ -2570,6 +2571,7 @@ C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */, C4E2E85D28FC282B003B070C /* TestableConfiguration.swift in Sources */, C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */, + C4BB393A2981AFC700F8E797 /* PhpVersionSource.swift in Sources */, C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, diff --git a/phpmon/Domain/DomainList/Cells/DomainListPhpCell.swift b/phpmon/Domain/DomainList/Cells/DomainListPhpCell.swift index 8cfd95f..882ff56 100644 --- a/phpmon/Domain/DomainList/Cells/DomainListPhpCell.swift +++ b/phpmon/Domain/DomainList/Cells/DomainListPhpCell.swift @@ -28,7 +28,7 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol { imageViewPhpVersionOK.toolTip = nil - imageViewPhpVersionOK.contentTintColor = site.composerPhpCompatibleWithLinked + imageViewPhpVersionOK.contentTintColor = site.isCompatibleWithPreferredPhpVersion ? NSColor(named: "IconColorGreen") : NSColor(named: "IconColorRed") @@ -37,9 +37,9 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol { imageViewPhpVersionOK.image = NSImage(named: "Isolated") imageViewPhpVersionOK.toolTip = "domain_list.tooltips.isolated".localized(site.servingPhpVersion) } else { - imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked) + imageViewPhpVersionOK.isHidden = (site.preferredPhpVersion == "???" || !site.isCompatibleWithPreferredPhpVersion) imageViewPhpVersionOK.image = NSImage(named: "Checkmark") - imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp) + imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.preferredPhpVersion) } } @@ -57,12 +57,12 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol { if site.isolatedPhpVersion != nil { return [] } - + guard let install = PhpEnv.phpInstall else { return [] } - return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in + return PhpEnv.shared.validVersions(for: site.preferredPhpVersion).filter({ version in version.short != install.version.short }) } diff --git a/phpmon/Domain/DomainList/Cells/DomainListTypeCell.swift b/phpmon/Domain/DomainList/Cells/DomainListTypeCell.swift index cc384ab..1208b81 100644 --- a/phpmon/Domain/DomainList/Cells/DomainListTypeCell.swift +++ b/phpmon/Domain/DomainList/Cells/DomainListTypeCell.swift @@ -25,7 +25,7 @@ class DomainListTypeCell: NSTableCellView, DomainListCellProtocol { } // PHP version - labelPhpVersion.stringValue = site.composerPhp == "???" ? "Any PHP" : "PHP \(site.composerPhp)" + labelPhpVersion.stringValue = site.preferredPhpVersion == "???" ? "Any PHP" : "PHP \(site.preferredPhpVersion)" } func populateCell(with proxy: ValetProxy) { diff --git a/phpmon/Domain/Integrations/Composer/ComposerJson.swift b/phpmon/Domain/Integrations/Composer/ComposerJson.swift index deb4b6d..1e3047e 100644 --- a/phpmon/Domain/Integrations/Composer/ComposerJson.swift +++ b/phpmon/Domain/Integrations/Composer/ComposerJson.swift @@ -40,7 +40,7 @@ struct ComposerJson: Decodable { Checks what the PHP version constraint is. Returns a tuple (constraint, location of constraint). */ - public func getPhpVersion() -> (String, ValetSite.VersionSource) { + public func getPhpVersion() -> (String, PhpVersionSource) { // Check if in platform if configuration?.platform?.php != nil { return (configuration!.platform!.php!, .platform) diff --git a/phpmon/Domain/Integrations/Valet/Sites/FakeValetSite.swift b/phpmon/Domain/Integrations/Valet/Sites/FakeValetSite.swift index 0acdf18..b866f8c 100644 --- a/phpmon/Domain/Integrations/Valet/Sites/FakeValetSite.swift +++ b/phpmon/Domain/Integrations/Valet/Sites/FakeValetSite.swift @@ -19,10 +19,17 @@ class FakeValetSite: ValetSite { constraint: String = "^8.1", isolated: String? = nil ) { - self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false) + self.init( + name: name, + tld: tld, + absolutePath: path, + aliasPath: nil, + makeDeterminations: false + ) + self.secured = secure - self.composerPhp = constraint - self.composerPhpSource = constraint != "" ? .require : .unknown + self.preferredPhpVersion = constraint + self.preferredPhpVersionSource = constraint != "" ? .require : .unknown self.driver = driver self.driverDeterminedByComposer = true diff --git a/phpmon/Domain/Integrations/Valet/Sites/PhpVersionSource.swift b/phpmon/Domain/Integrations/Valet/Sites/PhpVersionSource.swift new file mode 100644 index 0000000..de4f949 --- /dev/null +++ b/phpmon/Domain/Integrations/Valet/Sites/PhpVersionSource.swift @@ -0,0 +1,17 @@ +// +// VersionSource.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 25/01/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation + +enum PhpVersionSource: String { + case unknown + case require + case platform + case valetphprc + case valetrc +} diff --git a/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift b/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift index bf97e94..6a3931f 100644 --- a/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift +++ b/phpmon/Domain/Integrations/Valet/Sites/ValetSite.swift @@ -44,14 +44,15 @@ class ValetSite: ValetListable { /// A list of notable Composer dependencies. var notableComposerDependencies: [String: String] = [:] - /// The PHP version as discovered in `composer.json` or in .valetphprc. - var composerPhp: String = "???" + /// The PHP version as discovered in `composer.json` or in .valetphprc/.valetrc. + /// This is the preferred version needed to correctly run the domain or site. + var preferredPhpVersion: String = "???" /// Check whether the PHP version is valid for the currently linked version. - var composerPhpCompatibleWithLinked: Bool = false + var isCompatibleWithPreferredPhpVersion: Bool = false /// How the PHP version was determined. - var composerPhpSource: VersionSource = .unknown + var preferredPhpVersionSource: PhpVersionSource = .unknown /// Which version of PHP is actually used to serve this site. var servingPhpVersion: String { @@ -60,14 +61,6 @@ class ValetSite: ValetListable { ?? "???" } - enum VersionSource: String { - case unknown - case require - case platform - case valetphprc - case valetrc - } - init( name: String, tld: String, @@ -140,27 +133,6 @@ class ValetSite: ValetListable { self.evaluateCompatibility() } - public func evaluateCompatibility() { - if self.composerPhp == "???" { - return - } - - guard let linked = PhpEnv.phpInstall else { - self.composerPhpCompatibleWithLinked = false - return - } - - // Split the composer list (on "|") to evaluate multiple constraints - // For example, for Laravel 8 projects the value is "^7.3|^8.0" - self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|") - .map { string in - let origin = self.isolatedPhpVersion?.versionNumber.short ?? linked.version.long - return !PhpVersionNumberCollection.make(from: [origin]) - .matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines)) - .isEmpty - }.contains(true) - } - /** Determine the driver to be displayed in the list of sites. In v5.0, this has been changed to load the "framework" or "project type" instead. @@ -201,10 +173,14 @@ class ValetSite: ValetListable { if FileSystem.fileExists(path) { let decoded = try JSONDecoder().decode( ComposerJson.self, - from: String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8).data(using: .utf8)! + from: String( + contentsOf: URL(fileURLWithPath: path), + encoding: .utf8 + ).data(using: .utf8)! ) - (self.composerPhp, self.composerPhpSource) = decoded.getPhpVersion() + (self.preferredPhpVersion, + self.preferredPhpVersionSource) = decoded.getPhpVersion() self.notableComposerDependencies = decoded.getNotableDependencies() } } catch { @@ -218,8 +194,8 @@ class ValetSite: ValetListable { */ private func determineValetPhpFileInfo() { let files = [ - (".valetrc", VersionSource.valetrc), - (".valetphprc", VersionSource.valetphprc) + (".valetrc", PhpVersionSource.valetrc), + (".valetphprc", PhpVersionSource.valetphprc) ] for (suffix, source) in files { @@ -237,40 +213,50 @@ class ValetSite: ValetListable { /** Parse a Valet file (either .valetphprc or .valetrc). */ - private func handleValetFile(_ path: String, _ source: VersionSource) throws { - let contents = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) + private func handleValetFile(_ path: String, _ source: PhpVersionSource) throws { + var versionString = "" + switch source { case .valetphprc: - if let version = VersionExtractor.from(contents) { - self.composerPhp = version - self.composerPhpSource = source - } + versionString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) case .valetrc: - self.parseValetRcFile(path, contents) + guard let valetRc = RCFile.fromPath(path) else { return } + guard let phpField = valetRc.fields["PHP"] else { return } + versionString = phpField default: return } - } - - /** - Specifically extract PHP information from a .valetrc file. - */ - private func parseValetRcFile(_ path: String, _ text: String) { - let valetRc = RCFile(path: path, contents: text) - - guard let versionString = valetRc.fields["PHP"] else { - if valetRc.path != nil { - Log.perf("\(self.name)'s .valetrc file at '\(valetRc.path!)' lacks a 'PHP' entry.") - } - return - } if let version = VersionExtractor.from(versionString) { - self.composerPhp = version - self.composerPhpSource = .valetrc + self.preferredPhpVersion = version + self.preferredPhpVersionSource = source } } + public func evaluateCompatibility() { + if self.preferredPhpVersion == "???" { + return + } + + guard let linked = PhpEnv.phpInstall else { + self.isCompatibleWithPreferredPhpVersion = false + return + } + + // Split the composer list (on "|") to evaluate multiple constraints + // For example, for Laravel 8 projects the value is "^7.3|^8.0" + self.isCompatibleWithPreferredPhpVersion = self.preferredPhpVersion.split(separator: "|").map { string in + let origin = self.isolatedPhpVersion?.versionNumber.short + ?? linked.version.long + + let normalizedPhpVersion = string.trimmingCharacters(in: .whitespacesAndNewlines) + + return !PhpVersionNumberCollection.make(from: [origin]) + .matching(constraint: normalizedPhpVersion) + .isEmpty + }.contains(true) + } + // MARK: - File Parsing public static func isolatedVersion(_ filePath: String) -> String? { diff --git a/phpmon/Domain/SwiftUI/Domains/VersionPopoverView.swift b/phpmon/Domain/SwiftUI/Domains/VersionPopoverView.swift index 39eb12d..d73a22a 100644 --- a/phpmon/Domain/SwiftUI/Domains/VersionPopoverView.swift +++ b/phpmon/Domain/SwiftUI/Domains/VersionPopoverView.swift @@ -42,14 +42,14 @@ struct VersionPopoverView: View { }.padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0)) } } else { - if site.composerPhpSource == .unknown { + if site.preferredPhpVersionSource == .unknown { // We don't know which PHP version is required DisclaimerView( iconName: "questionmark.circle.fill", message: "alert.unable_to_determine_is_fine".localized ) } else { - if site.composerPhpCompatibleWithLinked { + if site.isCompatibleWithPreferredPhpVersion { DisclaimerView( iconName: "checkmark.circle.fill", message: "alert.php_version_ideal".localized, @@ -73,7 +73,7 @@ struct VersionPopoverView: View { } func getTitleText() -> String { - if site.composerPhpSource == .unknown { + if site.preferredPhpVersionSource == .unknown { return "alert.composer_php_requirement.unable_to_determine".localized } @@ -87,7 +87,7 @@ struct VersionPopoverView: View { return "alert.composer_php_requirement.title".localized( "\(site.name).\(suffix)", - site.composerPhp + site.preferredPhpVersion ) } @@ -102,7 +102,7 @@ struct VersionPopoverView: View { information += "\n\n" } - information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)" + information += "alert.composer_php_requirement.type.\(site.preferredPhpVersionSource.rawValue)" .localized return information