1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 20:10:08 +02:00

Allow replacing of config values

This commit is contained in:
2022-05-19 00:08:26 +02:00
parent 2e61479c75
commit 990152d77d
2 changed files with 102 additions and 19 deletions

View File

@ -1,5 +1,5 @@
// //
// PhpIniTest.swift // PhpConfigurationTest.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 04/05/2022. // Created by Nico Verbruggen on 04/05/2022.
@ -40,12 +40,45 @@ class PhpConfigurationTest: XCTestCase {
XCTAssert(iniFile.get(for: "display_errors") == "On") XCTAssert(iniFile.get(for: "display_errors") == "On")
} }
func testCanSwapConfigurationValue() throws { func testCanCustomizeConfigurationValue() throws {
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")! let destination = Utility
.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
let configurationFile = PhpConfigurationFile.from(filePath: destination.path) let configurationFile = PhpConfigurationFile
.from(filePath: destination.path)!
XCTAssertNotNil(configurationFile) // 0. Verify the original value
XCTAssertEqual(configurationFile.get(for: "error_reporting"), "E_ALL")
// 1. Change the value
try! configurationFile.replace(
key: "error_reporting",
value: "E_ALL & ~E_DEPRECATED & ~E_STRICT"
)
XCTAssertEqual(
configurationFile.get(for: "error_reporting"),
"E_ALL & ~E_DEPRECATED & ~E_STRICT"
)
// 2. Ensure that same key and value doesn't break subsequent saves
try! configurationFile.replace(
key: "error_reporting",
value: "error_reporting"
)
XCTAssertEqual(
configurationFile.get(for: "error_reporting"),
"error_reporting"
)
// 3. Verify subsequent saves weren't broken
try! configurationFile.replace(
key: "error_reporting",
value: "E_ALL"
)
XCTAssertEqual(
configurationFile.get(for: "error_reporting"),
"E_ALL"
)
} }
} }

View File

@ -1,5 +1,5 @@
// //
// PhpInitFile.swift // PhpConfigurationFile.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 04/05/2022. // Created by Nico Verbruggen on 04/05/2022.
@ -10,18 +10,26 @@ import Foundation
class PhpConfigurationFile: CreatedFromFile { class PhpConfigurationFile: CreatedFromFile {
typealias Section = [String: String] struct ConfigValue {
let lineIndex: Int
let value: String
}
typealias Section = [String: ConfigValue]
typealias Config = [String: Section] typealias Config = [String: Section]
/// The file where this configuration file was located. /// The file where this configuration file was located.
let file: String let filePath: String
/// The extensions found in this .ini file. /// The extensions found in this .ini file.
let extensions: [PhpExtension] var extensions: [PhpExtension]
/// The actual content of the configuration file. /// The actual, structured content of the configuration file.
var content: Config var content: Config
/// The original lines of the file.
var lines: [String]
/** Resolves a PHP configuration file (.ini) */ /** Resolves a PHP configuration file (.ini) */
static func from(filePath: String) -> Self? { static func from(filePath: String) -> Self? {
let path = filePath.replacingOccurrences( let path = filePath.replacingOccurrences(
@ -43,10 +51,8 @@ class PhpConfigurationFile: CreatedFromFile {
} }
required init(path: String, contents: String) { required init(path: String, contents: String) {
self.file = path self.filePath = path
self.lines = contents.components(separatedBy: "\n")
let lines = contents.components(separatedBy: "\n")
self.extensions = PhpExtension.from(lines, filePath: path) self.extensions = PhpExtension.from(lines, filePath: path)
self.content = Self.parseConfig(lines: lines) self.content = Self.parseConfig(lines: lines)
} }
@ -60,16 +66,57 @@ class PhpConfigurationFile: CreatedFromFile {
} }
public func get(for key: String) -> String? { public func get(for key: String) -> String? {
return getConfig(for: key)?.value
}
public func getConfig(for key: String) -> ConfigValue? {
for (_, section) in self.content { for (_, section) in self.content {
if section.keys.contains(key) { if section.keys.contains(key) {
return section[key] return section[key]!
} }
} }
return nil return nil
} }
public func replace(key: String, value: String) { enum ReplacementErrors: Error {
// TODO case missingKey
}
/**
Replaces the value for a specific (existing) key with a new value.
The key must exist for this to work.
*/
public func replace(key: String, value: String) throws {
// Ensure that the key exists
guard let item = getConfig(for: key) else {
throw ReplacementErrors.missingKey
}
// Figure out what comes after the assignment
var components = self
.lines[item.lineIndex]
.components(separatedBy: "=")
// Replace the value with the new one
components[1] = components[1]
.replacingOccurrences(of: item.value, with: value)
// Replace the specific line
self.lines[item.lineIndex] = components.joined(separator: "=")
// Finally, join the string and save the file atomatically again
try self.lines.joined(separator: "\n")
.write(toFile: self.filePath, atomically: true, encoding: .utf8)
// Reload the original file
self.reload()
}
public func reload() {
self.lines = try! String(contentsOfFile: self.filePath)
.components(separatedBy: "\n")
self.extensions = PhpExtension.from(lines, filePath: self.filePath)
self.content = Self.parseConfig(lines: lines)
} }
// MARK: Parsing Logic // MARK: Parsing Logic
@ -84,14 +131,17 @@ class PhpConfigurationFile: CreatedFromFile {
var currentSectionName = "main" var currentSectionName = "main"
for line in lines { for (index, line) in lines.enumerated() {
let line = trim(line) let line = trim(line)
if line.hasPrefix("[") && line.hasSuffix("]") { if line.hasPrefix("[") && line.hasSuffix("]") {
currentSectionName = parseSectionHeader(line) currentSectionName = parseSectionHeader(line)
} else if let (key, value) = parseLine(line) { } else if let (key, value) = parseLine(line) {
var section = config[currentSectionName] ?? [:] var section = config[currentSectionName] ?? [:]
section[key] = value section[key] = ConfigValue(
lineIndex: index,
value: value
)
config[currentSectionName] = section config[currentSectionName] = section
} }
} }