1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-06 11:30:08 +02:00
Files
app/phpmon/Common/PHP/ActivePhpInstallation.swift
2023-01-19 18:11:25 +01:00

170 lines
5.8 KiB
Swift
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// ActivePhpInstallation.swift
// PHP Monitor
//
// Copyright © 2023 Nico Verbruggen. All rights reserved.
//
import Foundation
/**
An installed version of PHP, that was detected by scanning the `/opt/php@version/bin` directory.
When initialized, that version's .ini files are also scanned (for active or inactive extensions).
Integrity checks can be performed to determine whether PHP-FPM is configured correctly.
- Note: Each installation has a separate version number.
Using `version.short` is advisable if you want to interact with Homebrew.
*/
class ActivePhpInstallation {
var version: VersionNumber!
var limits: Limits!
var iniFiles: [PhpConfigurationFile] = []
var hasErrorState: Bool = false
var extensions: [PhpExtension] {
return iniFiles.flatMap { initFile in
return initFile.extensions
}
}
// MARK: - Computed
var formula: String {
return (version.short == PhpEnv.brewPhpAlias) ? "php" : "php@\(version.short)"
}
// MARK: - Initializer
public static func load() -> ActivePhpInstallation? {
if !FileSystem.fileExists(Paths.phpConfig) {
return nil
}
return ActivePhpInstallation()
}
init() {
// Show information about the current version
do {
try determineVersion()
} catch {
fatalError("Could not determine or parse PHP version; aborting!")
}
// Initialize the list of ini files that are loaded
iniFiles = []
// If an error occurred, exit early
if self.hasErrorState {
limits = Limits()
return
}
// Load extension information
let mainConfigurationFileUrl = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
if let file = PhpConfigurationFile.from(filePath: mainConfigurationFileUrl.path) {
iniFiles.append(file)
}
// Get configuration values
limits = Limits(
memory_limit: getByteCount(key: "memory_limit"),
upload_max_filesize: getByteCount(key: "upload_max_filesize"),
post_max_size: getByteCount(key: "post_max_size")
)
// Return a list of .ini files parsed after php.ini
let paths = Command.execute(
path: Paths.php,
arguments: ["-r", "echo php_ini_scanned_files();"],
trimNewlines: false
)
.replacingOccurrences(of: "\n", with: "")
.split(separator: ",")
.map { String($0) }
// See if any extensions are present in said .ini files
paths.forEach { (iniFilePath) in
if let file = PhpConfigurationFile.from(filePath: iniFilePath) {
iniFiles.append(file)
}
}
}
/**
When the app tries to retrieve the version, the installation is considered broken if the output is nothing,
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
*/
private func determineVersion() throws {
let output = Command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
self.hasErrorState = (output == "" || output.contains("Warning") || output.contains("Error"))
self.version = try? VersionNumber.parse(output)
}
/**
Retrieves the display value for a specific key in the `.ini` file.
The following values are valid:
* -1: unlimited (show the infinity icon)
* 10000: an integer = amount of bytes
* 1K, 1M, 1G = shorthand for kilobytes, megabytes and gigabytes
If none of these notations are used, the _fallback_ value is used.
We'll show an emoji to indicate something has gone wrong here.
To clarify, B gets appended to valid values.
As a result, "5M" (valid) becomes "5MB", and "5MB" (invalid) becomes .
- Parameter key: The key of the `ini` value that needs to be retrieved. For example, you can use `memory_limit`.
*/
private func getByteCount(key: String) -> String {
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"], trimNewlines: false)
// Check if the value is unlimited
if value == "-1" {
return ""
}
// Check if the syntax is valid otherwise
let regex = try! NSRegularExpression(pattern: #"^([0-9]*)(K|M|G|)$"#, options: [])
let match = regex.matches(in: value, options: [], range: NSRange(location: 0, length: value.count)).first
return (match == nil) ? "⚠️" : "\(value)B"
}
/**
Determine if PHP-FPM is configured correctly.
For PHP 5.6, we'll check if `valet.sock` is included in the main `php-fpm.conf` file, but for more recent
versions of PHP, we can just check for the existence of the `valet-fpm.conf` file. If the check here fails,
that means that Valet won't work properly.
*/
func checkPhpFpmStatus() async -> Bool {
if self.version.short == "5.6" {
// The main PHP config file should contain `valet.sock` and then we're probably fine?
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
return await Shell.pipe("cat \(fileName)").out
.contains("valet.sock")
}
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
return FileSystem.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
}
// MARK: - Structs
/**
Struct containing information about the limits of the current PHP installation.
Includes: memory limit, max upload size and max post size.
*/
struct Limits {
var memory_limit = "???"
var upload_max_filesize = "???"
var post_max_size = "???"
}
}