1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-08 12:29:54 +02:00

♻️ Cleanup

This commit is contained in:
2023-01-26 20:29:56 +01:00
parent 66393094b0
commit f60ecb877c
8 changed files with 89 additions and 77 deletions

View File

@@ -547,6 +547,10 @@
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; }; 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 */; }; 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 */; }; 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 */; }; C4BF56AB2949381100379603 /* FakeValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BF56AA2949381100379603 /* FakeValetInteractor.swift */; };
C4BF56AC2949381100379603 /* 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 */; }; C4BF56AD2949381100379603 /* FakeValetInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BF56AA2949381100379603 /* FakeValetInteractor.swift */; };
@@ -876,6 +880,7 @@
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; }; C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; }; C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; }; C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionSource.swift; sourceTree = "<group>"; };
C4BF56AA2949381100379603 /* FakeValetInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeValetInteractor.swift; sourceTree = "<group>"; }; C4BF56AA2949381100379603 /* FakeValetInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeValetInteractor.swift; sourceTree = "<group>"; };
C4C0E8DE27F88AEB002D32A9 /* FakeDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeDomainScanner.swift; sourceTree = "<group>"; }; C4C0E8DE27F88AEB002D32A9 /* FakeDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeDomainScanner.swift; sourceTree = "<group>"; };
C4C0E8E127F88B13002D32A9 /* ValetDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetDomainScanner.swift; sourceTree = "<group>"; }; C4C0E8E127F88B13002D32A9 /* ValetDomainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetDomainScanner.swift; sourceTree = "<group>"; };
@@ -1544,7 +1549,7 @@
children = ( children = (
C4E4404527C56F4700D225E1 /* ValetSite.swift */, C4E4404527C56F4700D225E1 /* ValetSite.swift */,
C41C02A827E61A65009F26CB /* FakeValetSite.swift */, C41C02A827E61A65009F26CB /* FakeValetSite.swift */,
C4C0E8E427F88B1F002D32A9 /* SiteScanner */, C4BB39382981AFC700F8E797 /* PhpVersionSource.swift */,
); );
path = Sites; path = Sites;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1566,13 +1571,6 @@
path = Nginx; path = Nginx;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C4C0E8E427F88B1F002D32A9 /* SiteScanner */ = {
isa = PBXGroup;
children = (
);
path = SiteScanner;
sourceTree = "<group>";
};
C4C1019727C65A11001FACC2 /* Parsers */ = { C4C1019727C65A11001FACC2 /* Parsers */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -2051,6 +2049,7 @@
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */, C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */,
C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */, C4FACE80288F1C0D00FC478F /* PreferencesWindowController+Hotkey.swift in Sources */,
C415D3B72770F294005EF286 /* Actions.swift in Sources */, C415D3B72770F294005EF286 /* Actions.swift in Sources */,
C4BB39392981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, C4C3643928AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */, C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */,
C4CDA893288F1A71007CE25F /* Keys.swift in Sources */, C4CDA893288F1A71007CE25F /* Keys.swift in Sources */,
@@ -2162,6 +2161,7 @@
C471E83028F9BB650021E251 /* Application.swift in Sources */, C471E83028F9BB650021E251 /* Application.swift in Sources */,
C471E83128F9BB650021E251 /* LocalNotification.swift in Sources */, C471E83128F9BB650021E251 /* LocalNotification.swift in Sources */,
C471E83228F9BB650021E251 /* MenuBarImageGenerator.swift in Sources */, C471E83228F9BB650021E251 /* MenuBarImageGenerator.swift in Sources */,
C4BB393B2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
C471E83328F9BB650021E251 /* PMWindowController.swift in Sources */, C471E83328F9BB650021E251 /* PMWindowController.swift in Sources */,
C471E83428F9BB650021E251 /* VersionExtractor.swift in Sources */, C471E83428F9BB650021E251 /* VersionExtractor.swift in Sources */,
C471E83528F9BB650021E251 /* ValetProxy.swift in Sources */, C471E83528F9BB650021E251 /* ValetProxy.swift in Sources */,
@@ -2428,6 +2428,7 @@
C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */, C4CE7F9929683B43000102CF /* PhpVersionNumberCollection.swift in Sources */,
C471E7FC28F9BACE0021E251 /* HomebrewPackage.swift in Sources */, C471E7FC28F9BACE0021E251 /* HomebrewPackage.swift in Sources */,
C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */, C471E7CF28F9BA600021E251 /* ActiveShell.swift in Sources */,
C4BB393C2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */, C471E7F628F9BAC80021E251 /* PhpHelper.swift in Sources */,
C471E7EE28F9BAC30021E251 /* Constants.swift in Sources */, C471E7EE28F9BAC30021E251 /* Constants.swift in Sources */,
C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */, C471E80E28F9BAE80021E251 /* DateExtension.swift in Sources */,
@@ -2560,6 +2561,7 @@
C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */, C485707B28BF458900539B36 /* VersionPopoverView.swift in Sources */,
C4E2E85D28FC282B003B070C /* TestableConfiguration.swift in Sources */, C4E2E85D28FC282B003B070C /* TestableConfiguration.swift in Sources */,
C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */, C485706E28BF451C00539B36 /* OnboardingWindowController.swift in Sources */,
C4BB393A2981AFC700F8E797 /* PhpVersionSource.swift in Sources */,
C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */, C4CB6E66292C362C002E9027 /* Homebrew.swift in Sources */,
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */, C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */, C4C3643A28AE4FCE00C0770E /* StatusMenu+Items.swift in Sources */,

View File

@@ -28,7 +28,7 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
imageViewPhpVersionOK.toolTip = nil imageViewPhpVersionOK.toolTip = nil
imageViewPhpVersionOK.contentTintColor = site.composerPhpCompatibleWithLinked imageViewPhpVersionOK.contentTintColor = site.isCompatibleWithPreferredPhpVersion
? NSColor(named: "IconColorGreen") ? NSColor(named: "IconColorGreen")
: NSColor(named: "IconColorRed") : NSColor(named: "IconColorRed")
@@ -37,9 +37,9 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
imageViewPhpVersionOK.image = NSImage(named: "Isolated") imageViewPhpVersionOK.image = NSImage(named: "Isolated")
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.isolated".localized(site.servingPhpVersion) imageViewPhpVersionOK.toolTip = "domain_list.tooltips.isolated".localized(site.servingPhpVersion)
} else { } else {
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked) imageViewPhpVersionOK.isHidden = (site.preferredPhpVersion == "???" || !site.isCompatibleWithPreferredPhpVersion)
imageViewPhpVersionOK.image = NSImage(named: "Checkmark") imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp) imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.preferredPhpVersion)
} }
} }
@@ -58,7 +58,7 @@ class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
return [] return []
} }
return PhpEnv.shared.validVersions(for: site.composerPhp).filter({ version in return PhpEnv.shared.validVersions(for: site.preferredPhpVersion).filter({ version in
version.short != PhpEnv.phpInstall.version.short version.short != PhpEnv.phpInstall.version.short
}) })
} }

View File

@@ -25,7 +25,7 @@ class DomainListTypeCell: NSTableCellView, DomainListCellProtocol {
} }
// PHP version // 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) { func populateCell(with proxy: ValetProxy) {

View File

@@ -40,7 +40,7 @@ struct ComposerJson: Decodable {
Checks what the PHP version constraint is. Checks what the PHP version constraint is.
Returns a tuple (constraint, location of constraint). Returns a tuple (constraint, location of constraint).
*/ */
public func getPhpVersion() -> (String, ValetSite.VersionSource) { public func getPhpVersion() -> (String, PhpVersionSource) {
// Check if in platform // Check if in platform
if configuration?.platform?.php != nil { if configuration?.platform?.php != nil {
return (configuration!.platform!.php!, .platform) return (configuration!.platform!.php!, .platform)

View File

@@ -19,10 +19,17 @@ class FakeValetSite: ValetSite {
constraint: String = "^8.1", constraint: String = "^8.1",
isolated: String? = nil 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.secured = secure
self.composerPhp = constraint self.preferredPhpVersion = constraint
self.composerPhpSource = constraint != "" ? .require : .unknown self.preferredPhpVersionSource = constraint != "" ? .require : .unknown
self.driver = driver self.driver = driver
self.driverDeterminedByComposer = true self.driverDeterminedByComposer = true

View File

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

View File

@@ -44,14 +44,15 @@ class ValetSite: ValetListable {
/// A list of notable Composer dependencies. /// A list of notable Composer dependencies.
var notableComposerDependencies: [String: String] = [:] var notableComposerDependencies: [String: String] = [:]
/// The PHP version as discovered in `composer.json` or in .valetphprc. /// The PHP version as discovered in `composer.json` or in .valetphprc/.valetrc.
var composerPhp: String = "???" /// 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. /// 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. /// 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. /// Which version of PHP is actually used to serve this site.
var servingPhpVersion: String { var servingPhpVersion: String {
@@ -59,14 +60,6 @@ class ValetSite: ValetListable {
?? PhpEnv.phpInstall.version.short ?? PhpEnv.phpInstall.version.short
} }
enum VersionSource: String {
case unknown
case require
case platform
case valetphprc
case valetrc
}
init( init(
name: String, name: String,
tld: String, tld: String,
@@ -139,22 +132,6 @@ class ValetSite: ValetListable {
self.evaluateCompatibility() self.evaluateCompatibility()
} }
public func evaluateCompatibility() {
if self.composerPhp == "???" {
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 ?? PhpEnv.phpInstall.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 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. to load the "framework" or "project type" instead.
@@ -195,10 +172,14 @@ class ValetSite: ValetListable {
if FileSystem.fileExists(path) { if FileSystem.fileExists(path) {
let decoded = try JSONDecoder().decode( let decoded = try JSONDecoder().decode(
ComposerJson.self, 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() self.notableComposerDependencies = decoded.getNotableDependencies()
} }
} catch { } catch {
@@ -212,8 +193,8 @@ class ValetSite: ValetListable {
*/ */
private func determineValetPhpFileInfo() { private func determineValetPhpFileInfo() {
let files = [ let files = [
(".valetrc", VersionSource.valetrc), (".valetrc", PhpVersionSource.valetrc),
(".valetphprc", VersionSource.valetphprc) (".valetphprc", PhpVersionSource.valetphprc)
] ]
for (suffix, source) in files { for (suffix, source) in files {
@@ -231,40 +212,45 @@ class ValetSite: ValetListable {
/** /**
Parse a Valet file (either .valetphprc or .valetrc). Parse a Valet file (either .valetphprc or .valetrc).
*/ */
private func handleValetFile(_ path: String, _ source: VersionSource) throws { private func handleValetFile(_ path: String, _ source: PhpVersionSource) throws {
let contents = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8) var versionString = ""
switch source { switch source {
case .valetphprc: case .valetphprc:
if let version = VersionExtractor.from(contents) { versionString = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
self.composerPhp = version
self.composerPhpSource = source
}
case .valetrc: 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: default:
return 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) { if let version = VersionExtractor.from(versionString) {
self.composerPhp = version self.preferredPhpVersion = version
self.composerPhpSource = .valetrc self.preferredPhpVersionSource = source
} }
} }
public func evaluateCompatibility() {
if self.preferredPhpVersion == "???" {
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
?? PhpEnv.phpInstall.version.long
let normalizedPhpVersion = string.trimmingCharacters(in: .whitespacesAndNewlines)
return !PhpVersionNumberCollection.make(from: [origin])
.matching(constraint: normalizedPhpVersion)
.isEmpty
}.contains(true)
}
// MARK: - File Parsing // MARK: - File Parsing
public static func isolatedVersion(_ filePath: String) -> String? { public static func isolatedVersion(_ filePath: String) -> String? {

View File

@@ -42,14 +42,14 @@ struct VersionPopoverView: View {
}.padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0)) }.padding(EdgeInsets(top: 10, leading: 0, bottom: 0, trailing: 0))
} }
} else { } else {
if site.composerPhpSource == .unknown { if site.preferredPhpVersionSource == .unknown {
// We don't know which PHP version is required // We don't know which PHP version is required
DisclaimerView( DisclaimerView(
iconName: "questionmark.circle.fill", iconName: "questionmark.circle.fill",
message: "alert.unable_to_determine_is_fine".localized message: "alert.unable_to_determine_is_fine".localized
) )
} else { } else {
if site.composerPhpCompatibleWithLinked { if site.isCompatibleWithPreferredPhpVersion {
DisclaimerView( DisclaimerView(
iconName: "checkmark.circle.fill", iconName: "checkmark.circle.fill",
message: "alert.php_version_ideal".localized, message: "alert.php_version_ideal".localized,
@@ -73,7 +73,7 @@ struct VersionPopoverView: View {
} }
func getTitleText() -> String { func getTitleText() -> String {
if site.composerPhpSource == .unknown { if site.preferredPhpVersionSource == .unknown {
return "alert.composer_php_requirement.unable_to_determine".localized return "alert.composer_php_requirement.unable_to_determine".localized
} }
@@ -87,7 +87,7 @@ struct VersionPopoverView: View {
return "alert.composer_php_requirement.title".localized( return "alert.composer_php_requirement.title".localized(
"\(site.name).\(suffix)", "\(site.name).\(suffix)",
site.composerPhp site.preferredPhpVersion
) )
} }
@@ -102,7 +102,7 @@ struct VersionPopoverView: View {
information += "\n\n" information += "\n\n"
} }
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)" information += "alert.composer_php_requirement.type.\(site.preferredPhpVersionSource.rawValue)"
.localized .localized
return information return information