diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index c4d5736..32055c8 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -572,6 +572,10 @@ C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; }; C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; }; + C4CE7F9629683B43000102CF /* PhpVersionNumberCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */; }; + C4CE7F9729683B43000102CF /* PhpVersionNumberCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */; }; + C4CE7F9829683B43000102CF /* PhpVersionNumberCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */; }; + C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */; }; C4D36601291132B7006BD146 /* ValetScanners.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D36600291132B7006BD146 /* ValetScanners.swift */; }; C4D36602291132B7006BD146 /* ValetScanners.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D36600291132B7006BD146 /* ValetScanners.swift */; }; C4D36603291132B7006BD146 /* ValetScanners.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D36600291132B7006BD146 /* ValetScanners.swift */; }; @@ -874,6 +878,7 @@ C4CDA892288F1A71007CE25F /* Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keys.swift; sourceTree = ""; }; C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = ""; }; C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = ""; }; + C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberCollection.swift; sourceTree = ""; }; C4D36600291132B7006BD146 /* ValetScanners.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetScanners.swift; sourceTree = ""; }; C4D3660A29113F20006BD146 /* System.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = ""; }; C4D3660F291140BE006BD146 /* TestableFileSystemTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestableFileSystemTest.swift; sourceTree = ""; }; @@ -1400,8 +1405,9 @@ C48D6C6E279CD29C00F26D7E /* PHP Version */ = { isa = PBXGroup; children = ( - C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, C48D6C6F279CD2AC00F26D7E /* VersionNumber.swift */, + C4CE7F9529683B43000102CF /* PhpVersionNumberCollection.swift */, + C40C7F1D2772136000DDDCDC /* PhpEnv.swift */, C4D936C827E3EB6100BD69FE /* PhpHelper.swift */, ); path = "PHP Version"; @@ -2067,6 +2073,7 @@ C4C8900528F0E3D100CE5E97 /* RealFileSystem.swift in Sources */, C4A81CA428C67101008DD9D1 /* PMTableView.swift in Sources */, C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */, + C4CE7F9629683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */, C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */, @@ -2190,6 +2197,7 @@ C4E2E84C28FC1E70003B070C /* DataExtension.swift in Sources */, C471E86E28F9BB650021E251 /* MenuBarIcons.swift in Sources */, C471E86F28F9BB650021E251 /* Stats.swift in Sources */, + C4CE7F9829683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C471E87028F9BB650021E251 /* GlobalKeybindPreference.swift in Sources */, C471E87228F9BB650021E251 /* CheckboxPreferenceView.swift in Sources */, C471E87428F9BB650021E251 /* SelectPreferenceView.swift in Sources */, @@ -2381,6 +2389,7 @@ C471E8F128F9BB8F0021E251 /* KeyCombo.swift in Sources */, C471E8F228F9BB8F0021E251 /* ModifierFlagsExtension.swift in Sources */, C471E7F028F9BAC30021E251 /* Paths.swift in Sources */, + C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C471E7FC28F9BACE0021E251 /* HomebrewPackage.swift in Sources */, C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */, C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */, @@ -2529,6 +2538,7 @@ C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */, C4AD38B328ECD9D300FA8D83 /* TestableFileSystem.swift in Sources */, C40C5C9D2846A40600E28255 /* Preset.swift in Sources */, + C4CE7F9729683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */, C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */, C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */, diff --git a/phpmon/Common/PHP/PHP Version/PhpVersionNumberCollection.swift b/phpmon/Common/PHP/PHP Version/PhpVersionNumberCollection.swift new file mode 100644 index 0000000..029e262 --- /dev/null +++ b/phpmon/Common/PHP/PHP Version/PhpVersionNumberCollection.swift @@ -0,0 +1,100 @@ +// +// PhpVersionNumberCollection.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 06/01/2023. +// Copyright © 2023 Nico Verbruggen. All rights reserved. +// + +import Foundation + +public struct PhpVersionNumberCollection: Equatable { + let versions: [VersionNumber] + + public static func make(from versions: [String]) -> Self { + return PhpVersionNumberCollection( + versions: versions.map { try! VersionNumber.parse($0) } + ) + } + + public var first: VersionNumber? { + return self.versions.first + } + + public var all: [VersionNumber] { + return self.versions + } + + /** + Checks if any versions of PHP are valid for the constraint provided. + Due to the complexity of evaluating these, a important test is maintained. + More information on these constraints can be found here: + https://getcomposer.org/doc/articles/versions.md#writing-version-constraints + + - Parameter constraint: The full constraint as a string (e.g. "^7.0") + - Parameter strict: Whether the patch version check is strict. See more below. + + The strict mode does not matter if a patch version is provided for all versions in the collection. + + Strict mode assumes that any PHP version lacking precise patch information, e.g. inferred + from Homebrew corresponds to the .0 patch version of that version. The default, which is imprecise, + assumes that the patch version is .999, which means that in all cases the patch version check is + always going to pass. + + **STRICT MODE (= patch precision on)** + + Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in strict mode only 8.1.? will + be considered valid (8.0 translates to 8.0.0 and as such is older than 8.0.1, 8.1.0 is OK). + When checking against actual PHP versions installed by the user (with patch precision), use + strict mode. + + **NON-STRICT MODE (= patch precision off)** + + Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in non-strict mode version 8.0 + is assumed to be equal to version 8.0.999, which is actually fine if 8.0.1 is the required version. + In non-strict mode, the patch version is ignored for regular version checks (no caret / tilde). + If checking compatibility with general Homebrew versions of PHP, do NOT use strict mode, since + the patch version there is not used. (The formula php@8.0 suffices for ^8.0.1.) + */ + public func matching(constraint: String, strict: Bool = false) -> [VersionNumber] { + if let version = VersionNumber.make(from: constraint, type: .versionOnly) { + // Strict constraint (e.g. "7.0") -> returns specific version + return self.versions.filter { $0.isSameAs(version, strict) } + } + + if let version = VersionNumber.make(from: constraint, type: .caretVersionRange) { + // Caret range means that the major version is never higher but minor version can be higher + // ^7.2 will be compatible with all versions between 7.2 and 8.0 + return self.versions.filter { $0.hasNewerMinorVersionOrPatch(version, strict) } + } + + if let version = VersionNumber.make(from: constraint, type: .tildeVersionRange) { + // Tilde range means that most specific digit is used as the basis. + return self.versions.filter { + version.patch != nil + // If a patch is provided then the minor version cannot be bumped. + ? $0.hasSameMajorAndMinorButNewerOrSamePatch(version, strict) + // If a patch is not provided then the major version cannot be bumped. + : $0.hasSameMajorButNewerOrSameMinor(version, strict) + } + } + + if let version = VersionNumber.make(from: constraint, type: .greaterThanOrEqual) { + return self.versions.filter { $0.isSameAs(version, strict) || $0.isNewerThan(version, strict) } + } + + if let version = VersionNumber.make(from: constraint, type: .greaterThan) { + return self.versions.filter { $0.isNewerThan(version, strict) } + } + + if let version = VersionNumber.make(from: constraint, type: .smallerThanOrEqual) { + return self.versions.filter { $0.isSameAs(version, strict) || $0.isOlderThan(version, strict)} + } + + if let version = VersionNumber.make(from: constraint, type: .smallerThan) { + return self.versions.filter { $0.isOlderThan(version, strict)} + } + + return [] + } +} diff --git a/phpmon/Common/PHP/PHP Version/VersionNumber.swift b/phpmon/Common/PHP/PHP Version/VersionNumber.swift index b3c4cc3..3b07c52 100644 --- a/phpmon/Common/PHP/PHP Version/VersionNumber.swift +++ b/phpmon/Common/PHP/PHP Version/VersionNumber.swift @@ -8,97 +8,12 @@ import Foundation -public struct PhpVersionNumberCollection: Equatable { - let versions: [VersionNumber] - - public static func make(from versions: [String]) -> Self { - return PhpVersionNumberCollection( - versions: versions.map { try! VersionNumber.parse($0) } - ) - } - - public var first: VersionNumber? { - return self.versions.first - } - - public var all: [VersionNumber] { - return self.versions - } - - /** - Checks if any versions of PHP are valid for the constraint provided. - Due to the complexity of evaluating these, a important test is maintained. - More information on these constraints can be found here: - https://getcomposer.org/doc/articles/versions.md#writing-version-constraints - - - Parameter constraint: The full constraint as a string (e.g. "^7.0") - - Parameter strict: Whether the patch version check is strict. See more below. - - The strict mode does not matter if a patch version is provided for all versions in the collection. - - Strict mode assumes that any PHP version lacking precise patch information, e.g. inferred - from Homebrew corresponds to the .0 patch version of that version. The default, which is imprecise, - assumes that the patch version is .999, which means that in all cases the patch version check is - always going to pass. - - **STRICT MODE (= patch precision on)** - - Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in strict mode only 8.1.? will - be considered valid (8.0 translates to 8.0.0 and as such is older than 8.0.1, 8.1.0 is OK). - When checking against actual PHP versions installed by the user (with patch precision), use - strict mode. - - **NON-STRICT MODE (= patch precision off)** - - Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in non-strict mode version 8.0 - is assumed to be equal to version 8.0.999, which is actually fine if 8.0.1 is the required version. - In non-strict mode, the patch version is ignored for regular version checks (no caret / tilde). - If checking compatibility with general Homebrew versions of PHP, do NOT use strict mode, since - the patch version there is not used. (The formula php@8.0 suffices for ^8.0.1.) - */ - public func matching(constraint: String, strict: Bool = false) -> [VersionNumber] { - if let version = VersionNumber.make(from: constraint, type: .versionOnly) { - // Strict constraint (e.g. "7.0") -> returns specific version - return self.versions.filter { $0.isSameAs(version, strict) } - } - - if let version = VersionNumber.make(from: constraint, type: .caretVersionRange) { - // Caret range means that the major version is never higher but minor version can be higher - // ^7.2 will be compatible with all versions between 7.2 and 8.0 - return self.versions.filter { $0.hasNewerMinorVersionOrPatch(version, strict) } - } - - if let version = VersionNumber.make(from: constraint, type: .tildeVersionRange) { - // Tilde range means that most specific digit is used as the basis. - return self.versions.filter { - version.patch != nil - // If a patch is provided then the minor version cannot be bumped. - ? $0.hasSameMajorAndMinorButNewerOrSamePatch(version, strict) - // If a patch is not provided then the major version cannot be bumped. - : $0.hasSameMajorButNewerOrSameMinor(version, strict) - } - } - - if let version = VersionNumber.make(from: constraint, type: .greaterThanOrEqual) { - return self.versions.filter { $0.isSameAs(version, strict) || $0.isNewerThan(version, strict) } - } - - if let version = VersionNumber.make(from: constraint, type: .greaterThan) { - return self.versions.filter { $0.isNewerThan(version, strict) } - } - - if let version = VersionNumber.make(from: constraint, type: .smallerThanOrEqual) { - return self.versions.filter { $0.isSameAs(version, strict) || $0.isOlderThan(version, strict)} - } - - if let version = VersionNumber.make(from: constraint, type: .smallerThan) { - return self.versions.filter { $0.isOlderThan(version, strict)} - } - - return [] - } -} +/** + A version number that is (mostly) compatible with the semantic versioning standard. + For more information about semantic versioning, see: https://semver.org/ + - Note: If you want to check version constraints for PHP versions, please see `PhpVersionNumberCollection`. + */ public struct VersionNumber: Equatable, Hashable { let major: Int let minor: Int diff --git a/phpmon/Domain/Integrations/Valet/Valet.swift b/phpmon/Domain/Integrations/Valet/Valet.swift index 50c8ca7..f3689df 100644 --- a/phpmon/Domain/Integrations/Valet/Valet.swift +++ b/phpmon/Domain/Integrations/Valet/Valet.swift @@ -8,6 +8,11 @@ import Foundation +/** + This class is responsible for handling the state of Valet throughout PHP Monitor. A singleton instance is created + and accessible throughout the lifecycle of the app, unless the user has decided to not use Valet. In that case, + only a restricted subset of functionality is available in the app. + */ class Valet { enum FeatureFlag { @@ -189,6 +194,9 @@ class Valet { } } + /** + Determine if any platform issues are detected when running `valet --version`. + */ public func hasPlatformIssues() async -> Bool { return await Shell.pipe("valet --version") .out.contains("Composer detected issues in your platform")