mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-07 12:00:09 +02:00
✨ Version constraint checks (#84)
The version constraint checks will also be used in the future to evaluate whether any given site's PHP constraint (if set) is valid for the currently linked version of PHP. For example, assuming you have PHP 8.1.2 linked, we could evaluate: * A site requires "8.0" -> invalid * A site requires "^8.0" -> valid * A site requires "^8.0.0" -> valid * A site requires "~8.0" -> valid * A site requires "~8.0.0" -> invalid Currently, this constraint check is used to determine which versions that are currently installed are good suggestions to switch to. If you have a site with constraint "^8.0" for example, and you have PHP 8.0 and 8.1 installed (with 8.1 linked), then you will get a suggestion to switch back to 8.0.
This commit is contained in:
@ -92,6 +92,10 @@
|
||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
|
||||
C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C9925CC888B00CC7490 /* HeaderView.xib */; };
|
||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||
@ -257,6 +261,8 @@
|
||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = "<group>"; };
|
||||
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
||||
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
|
||||
@ -343,9 +349,9 @@
|
||||
54B20EDF263AA22C00D3250E /* PHP */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C48D6C6E279CD29C00F26D7E /* PHP Version */,
|
||||
C4D9ADC2277610E4007277F4 /* Switcher */,
|
||||
C4F30B01278E169B00755FCE /* Homebrew */,
|
||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
||||
@ -523,6 +529,15 @@
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C48D6C6E279CD29C00F26D7E /* PHP Version */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */,
|
||||
);
|
||||
path = "PHP Version";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C4AF9F6A275445C900D44ED0 /* Valet */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -643,6 +658,7 @@
|
||||
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
||||
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */,
|
||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */,
|
||||
C43A8A1925D9CD1000591B77 /* Utility.swift */,
|
||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
||||
C4AF9F7C275454A900D44ED0 /* ValetTest.swift */,
|
||||
@ -818,6 +834,7 @@
|
||||
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */,
|
||||
C4B585462770FE3900DA4FBE /* Command.swift in Sources */,
|
||||
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */,
|
||||
C417DC76277614690015E6EE /* Helpers.swift in Sources */,
|
||||
C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
@ -833,6 +850,7 @@
|
||||
files = (
|
||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
|
||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
||||
@ -928,6 +946,7 @@
|
||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
||||
C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */,
|
||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||
@ -958,6 +977,7 @@
|
||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
|
||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
||||
|
@ -149,4 +149,10 @@ class PhpEnv {
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
public func validVersions(for constraint: String) -> [PhpVersionNumber] {
|
||||
return PhpVersionNumberCollection
|
||||
.make(from: self.availablePhpVersions)
|
||||
.matching(constraint: constraint)
|
||||
}
|
||||
}
|
141
phpmon-common/PHP/PHP Version/PhpVersionNumber.swift
Normal file
141
phpmon-common/PHP/PHP Version/PhpVersionNumber.swift
Normal file
@ -0,0 +1,141 @@
|
||||
//
|
||||
// PhpVersionNumber.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/01/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct PhpVersionNumberCollection: Equatable {
|
||||
let versions: [PhpVersionNumber]
|
||||
|
||||
public static func make(from versions: [String]) -> Self {
|
||||
return PhpVersionNumberCollection(
|
||||
versions: versions.map { PhpVersionNumber.make(from: $0)! }
|
||||
)
|
||||
}
|
||||
|
||||
public var first: PhpVersionNumber? {
|
||||
return self.versions.first
|
||||
}
|
||||
|
||||
public var all: [PhpVersionNumber] {
|
||||
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 minor 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, 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) -> [PhpVersionNumber] {
|
||||
if let version = PhpVersionNumber.make(from: constraint, type: .versionOnly) {
|
||||
// Strict constraint (e.g. "7.0") -> returns specific version
|
||||
return self.versions.filter {
|
||||
$0.major == version.major
|
||||
&& $0.minor == version.minor
|
||||
&& (strict ? $0.patch(strict, version) == version.patch(strict) : true)
|
||||
}
|
||||
}
|
||||
|
||||
if let version = PhpVersionNumber.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.major == version.major &&
|
||||
(
|
||||
// Either the minor version is the same and the patch is higher or equal
|
||||
$0.minor == version.minor && $0.patch(strict) >= version.patch(strict, $0)
|
||||
// or the minor version number has been bumped
|
||||
|| $0.minor > version.minor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let version = PhpVersionNumber.make(from: constraint, type: .tildeVersionRange) {
|
||||
// Tilde range means that most specific digit is used as the basis.
|
||||
if version.patch != nil {
|
||||
// If a patch is provided then the minor version cannot be bumped.
|
||||
return self.versions.filter {
|
||||
$0.major == version.major && $0.minor == version.minor
|
||||
&& $0.patch(strict, version) >= version.patch!
|
||||
}
|
||||
} else {
|
||||
// If a patch is not provided then the major version cannot be bumped.
|
||||
return self.versions.filter {
|
||||
$0.major == version.major && $0.minor >= version.minor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public struct PhpVersionNumber: Equatable {
|
||||
let major: Int
|
||||
let minor: Int
|
||||
let patch: Int?
|
||||
|
||||
public func patch(_ strictFallback: Bool, _ constraint: PhpVersionNumber? = nil) -> Int {
|
||||
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
||||
}
|
||||
|
||||
public var homebrewVersion: String {
|
||||
return "\(major).\(minor)"
|
||||
}
|
||||
|
||||
public enum MatchType: String {
|
||||
case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
}
|
||||
|
||||
public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? {
|
||||
let regex = try! NSRegularExpression(pattern: type.rawValue, options: [])
|
||||
let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first
|
||||
|
||||
if match != nil {
|
||||
let major = Int(
|
||||
versionString[Range(match!.range(withName: "major"), in: versionString)!]
|
||||
)!
|
||||
let minor = Int(
|
||||
versionString[Range(match!.range(withName: "minor"), in: versionString)!]
|
||||
)!
|
||||
var patch: Int? = nil
|
||||
if let minorRange = Range(match!.range(withName: "patch"), in: versionString) {
|
||||
patch = Int(versionString[minorRange])
|
||||
}
|
||||
return Self(major: major, minor: minor, patch: patch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -10,20 +10,26 @@ import Foundation
|
||||
|
||||
class PhpInstallation {
|
||||
|
||||
var longVersion: String
|
||||
var longVersion: PhpVersionNumber
|
||||
|
||||
/**
|
||||
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
||||
in the relevant directory.
|
||||
*/
|
||||
init(_ version: String) {
|
||||
|
||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||
self.longVersion = version
|
||||
self.longVersion = PhpVersionNumber.make(from: version)!
|
||||
|
||||
if Shell.fileExists(phpConfigExecutablePath) {
|
||||
self.longVersion = Command.execute(
|
||||
let longVersionString = Command.execute(
|
||||
path: phpConfigExecutablePath,
|
||||
arguments: ["--version"]
|
||||
)
|
||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
self.longVersion = PhpVersionNumber.make(
|
||||
from: String(longVersionString.split(separator: "-")[0])
|
||||
)!
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,5 +26,4 @@ class PhpVersionDetectionTest: XCTestCase {
|
||||
|
||||
XCTAssertEqual(outcome, ["8.0", "7.0", "5.6"])
|
||||
}
|
||||
|
||||
}
|
||||
|
194
phpmon-tests/PhpVersionNumberTest.swift
Normal file
194
phpmon-tests/PhpVersionNumberTest.swift
Normal file
@ -0,0 +1,194 @@
|
||||
//
|
||||
// PhpVersionNumberTest.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/01/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class PhpVersionNumberTest: XCTestCase {
|
||||
|
||||
func testCanDeconstructPhpVersion() throws {
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "8.0.11"),
|
||||
PhpVersionNumber(major: 8, minor: 0, patch: 11)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7.4.2"),
|
||||
PhpVersionNumber(major: 7, minor: 4, patch: 2)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7.4"),
|
||||
PhpVersionNumber(major: 7, minor: 4, patch: nil)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7"),
|
||||
nil
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckFixedConstraints() throws {
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0"),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.3", "7.3.3", "7.2.3", "7.1.3", "7.0.3"])
|
||||
.matching(constraint: "7.0.3"),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0.3"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0.3", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0.3", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: []).all
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckCaretConstraints() throws {
|
||||
// 1. Imprecise checks
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0.1", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 3. Imprecise check with precise constraint (strict mode)
|
||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0.1", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
||||
)
|
||||
|
||||
// 4. Precise members and constraint all around
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0.1", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 5. Precise members but imprecise constraint (strict mode)
|
||||
// In strict mode the constraint's patch version is assumed to be 0
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 6. Precise members but imprecise constraint (lenient mode)
|
||||
// In lenient mode the constraint's patch version is assumed to be equal
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckTildeConstraints() throws {
|
||||
// 1. Imprecise checks
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0.1", strict: false),
|
||||
// One result because 7.0.1 to 7.0.x is expected.
|
||||
// 7.0.999 (assumed due to no strictness) is valid.
|
||||
// 7.1.0 and up are not valid (minor version is too high).
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
// 3. Imprecise check with precise constraint (strict mode)
|
||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0.1", strict: true),
|
||||
// No results because 7.0.1 to 7.0.x is expected.
|
||||
// 7.0.0 (assumed due to strictness) is not valid.
|
||||
// 7.1.0 and up are also not valid (minor version is too high).
|
||||
PhpVersionNumberCollection
|
||||
.make(from: []).all
|
||||
)
|
||||
|
||||
// 4. Precise members and constraint all around
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0.1", strict: true),
|
||||
// Only 7.0 with a patch version of .1 or higher is OK.
|
||||
// In this example, 7.0.10 is OK but all other versions are too new.
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0.10"]).all
|
||||
)
|
||||
|
||||
// 5. Precise members but imprecise constraint (strict mode)
|
||||
// In strict mode the constraint's patch version is assumed to be 0.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 6. Precise members but imprecise constraint (lenient mode)
|
||||
// In lenient mode the constraint's patch version is assumed to be equal.
|
||||
// (Strictness does not make any difference here, but both should be tested.)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
}
|
||||
}
|
@ -282,6 +282,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
|
||||
@objc func switchToPhpVersion(sender: PhpMenuItem) {
|
||||
self.switchToPhpVersion(sender.version)
|
||||
}
|
||||
|
||||
@objc func switchToPhpVersion(_ version: String) {
|
||||
setBusyImage()
|
||||
PhpEnv.shared.isBusy = true
|
||||
|
||||
@ -305,8 +309,8 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
|
||||
let sendLocalNotification = {
|
||||
LocalNotification.send(
|
||||
title: String(format: "notification.version_changed_title".localized, sender.version),
|
||||
subtitle: String(format: "notification.version_changed_desc".localized, sender.version)
|
||||
title: String(format: "notification.version_changed_title".localized, version),
|
||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
||||
)
|
||||
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
||||
}
|
||||
@ -321,7 +325,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
||||
}
|
||||
|
||||
PhpEnv.switcher.performSwitch(
|
||||
to: sender.version,
|
||||
to: version,
|
||||
completion: completion
|
||||
)
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class StatusMenu : NSMenu {
|
||||
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.longVersion
|
||||
|
||||
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
||||
let versionString = long ? longVersion : shortVersion
|
||||
let versionString = long ? longVersion.homebrewVersion : shortVersion
|
||||
|
||||
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
||||
let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)"
|
||||
|
@ -80,12 +80,13 @@ class SiteListCell: NSTableCellView
|
||||
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
|
||||
var map: [Int: String] = [:]
|
||||
|
||||
var versions: [String] = []
|
||||
// TODO: Based on the version constraint, insert the desired version to switch to
|
||||
|
||||
versions.forEach { version in
|
||||
alert.addButton(withTitle: "Switch to PHP \(version)")
|
||||
map[mapIndex] = version
|
||||
// Determine which installed versions would be ideal to switch to,
|
||||
// but make sure to exclude the currently linked version
|
||||
PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in
|
||||
version.homebrewVersion != PhpEnv.phpInstall.version.short
|
||||
}).forEach { version in
|
||||
alert.addButton(withTitle: "Switch to PHP \(version.homebrewVersion)")
|
||||
map[mapIndex] = version.homebrewVersion
|
||||
mapIndex += 1
|
||||
}
|
||||
|
||||
@ -94,6 +95,7 @@ class SiteListCell: NSTableCellView
|
||||
if map.keys.contains(response.rawValue) {
|
||||
let version = map[response.rawValue]!
|
||||
print("Pressed button to switch to \(version)")
|
||||
MainMenu.shared.switchToPhpVersion(version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ problem manually, using your own Terminal app (this just shows you the output)."
|
||||
|
||||
// Composer Version
|
||||
"alert.composer_php_requirement.title" = "`%@` has the following PHP requirement: \"php\": \"%@\".";
|
||||
"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json`.\n\nPlease note that the lockfile is not checked and this reflects the current Composer configuration file as it existed at the time of refreshing the site list.";
|
||||
"alert.composer_php_requirement.info" = "This required PHP version was determined by checking the %@ in `composer.json` when the list was last refreshed.";
|
||||
|
||||
// Force Reload Started
|
||||
"alert.force_reload.title" = "PHP Monitor will force reload the latest version of PHP";
|
||||
|
Reference in New Issue
Block a user