diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 185f08e..4c53cfb 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -118,8 +118,8 @@ C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; }; C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; }; C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; }; - C46FA9882822EFDC00D78807 /* PhpInitializationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpInitializationFile.swift */; }; - C46FA9892822EFDC00D78807 /* PhpInitializationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpInitializationFile.swift */; }; + C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; }; + C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */; }; C46FA98C2822F08F00D78807 /* PhpIniTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA98A2822F08F00D78807 /* PhpIniTest.swift */; }; C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; }; C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; }; @@ -341,7 +341,7 @@ C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = ""; }; C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = ""; }; C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; - C46FA9872822EFDC00D78807 /* PhpInitializationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInitializationFile.swift; sourceTree = ""; }; + C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpConfigurationFile.swift; sourceTree = ""; }; C46FA98A2822F08F00D78807 /* PhpIniTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpIniTest.swift; sourceTree = ""; }; C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = ""; }; @@ -466,7 +466,7 @@ C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */, C4F2E4392752F7D00020E974 /* PhpInstallation.swift */, C4ACA38E25C754C100060C66 /* PhpExtension.swift */, - C46FA9872822EFDC00D78807 /* PhpInitializationFile.swift */, + C46FA9872822EFDC00D78807 /* PhpConfigurationFile.swift */, ); path = PHP; sourceTree = ""; @@ -1132,7 +1132,7 @@ C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, C4B585412770FE3900DA4FBE /* Shell.swift in Sources */, C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */, - C46FA9882822EFDC00D78807 /* PhpInitializationFile.swift in Sources */, + C46FA9882822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */, C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */, @@ -1340,7 +1340,7 @@ C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */, C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */, C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */, - C46FA9892822EFDC00D78807 /* PhpInitializationFile.swift in Sources */, + C46FA9892822EFDC00D78807 /* PhpConfigurationFile.swift in Sources */, C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */, C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */, C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */, diff --git a/phpmon/Common/PHP/ActivePhpInstallation.swift b/phpmon/Common/PHP/ActivePhpInstallation.swift index 4eb125d..a423fbf 100644 --- a/phpmon/Common/PHP/ActivePhpInstallation.swift +++ b/phpmon/Common/PHP/ActivePhpInstallation.swift @@ -20,7 +20,7 @@ class ActivePhpInstallation { var version: Version! var limits: Limits! - var iniFiles: [PhpInitializationFile] = [] + var iniFiles: [PhpConfigurationFile] = [] var extensions: [PhpExtension] { return iniFiles.flatMap { initFile in @@ -53,7 +53,7 @@ class ActivePhpInstallation { let mainConfigurationFileUrl = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini") iniFiles.append( - PhpInitializationFile(fileUrl: mainConfigurationFileUrl) + PhpConfigurationFile(fileUrl: mainConfigurationFileUrl) ) // extensions.append(contentsOf: PhpExtension.load(from: mainConfigurationFileUrl)) @@ -76,7 +76,7 @@ class ActivePhpInstallation { let fileUrl = URL(fileURLWithPath: iniFilePath) iniFiles.append( - PhpInitializationFile(fileUrl: fileUrl) + PhpConfigurationFile(fileUrl: fileUrl) ) } } diff --git a/phpmon/Common/PHP/PhpConfigurationFile.swift b/phpmon/Common/PHP/PhpConfigurationFile.swift new file mode 100644 index 0000000..6a73250 --- /dev/null +++ b/phpmon/Common/PHP/PhpConfigurationFile.swift @@ -0,0 +1,128 @@ +// +// PhpInitFile.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 04/05/2022. +// Copyright © 2022 Nico Verbruggen. All rights reserved. +// + +import Foundation + +class PhpConfigurationFile { + + typealias Section = [String: String] + typealias Config = [String: Section] + + /// The file where this configuration file was located. + let file: String + + /// The extensions found in this .ini file. + let extensions: [PhpExtension] + + /// The actual content of the configuration file. + var content: Config + + init(fileUrl: URL) { + self.file = fileUrl.path + + let rawString = (try? String(contentsOf: fileUrl, encoding: .utf8)) ?? "" + + self.extensions = PhpExtension.load(from: fileUrl) + + self.content = Self.parseConfig(from: rawString.components(separatedBy: "\n")) + + dump(self) + } + + // MARK: Parsing Logic + // Slightly modified from: https://gist.github.com/jetmind/f776c0d223e4ac6aec1ff9389e874553 + + /** + Attempts to parse the configuration file, based on an array of strings. + Each string is a line from the configuration file. + */ + private static func parseConfig(from lines: [String]) -> Config { + var config = Config() + var currentSectionName = "main" + for line in lines { + let line = trim(line) + if line.hasPrefix("[") && line.hasSuffix("]") { + currentSectionName = parseSectionHeader(line) + } else if let (key, value) = parseLine(line) { + var section = config[currentSectionName] ?? [:] + section[key] = value + config[currentSectionName] = section + } + } + return config + } + + /** + Remove all whitespace and additional characters from individual lines. + */ + private static func trim(_ s: String) -> String { + let whitespaces = CharacterSet(charactersIn: " \n\r\t") + return s.trimmingCharacters(in: whitespaces) + } + + /** + It may prove beneficial to strip all comments, which can start with # or ;. + In this case, strip both. + */ + private static func stripComment(_ line: String) -> String { + var line = line + + let characters: [String.Element] = ["#", ";"] + + for character in characters { + // Only keep checking for comments as long as the line isn't empty + if line.isEmpty { + return line + } + + // Check for the next comment character + line = strip(character: character, line) + } + + return line + } + + /** + Empties a line if it happens to be commented out, causing it to be ignored. + */ + private static func strip(character: String.Element, _ line: String) -> String { + let parts = line.split( + separator: character, + maxSplits: 1, + omittingEmptySubsequences: false + ) + if !parts.isEmpty { + return String(parts[0]) + } + return "" + } + + /** + Attempts to parse a section header. Requires the line to start with [ and end with ]. + */ + private static func parseSectionHeader(_ line: String) -> String { + let from = line.index(after: line.startIndex) + let to = line.index(before: line.endIndex) + return line[from.. (String, String)? { + let parts = stripComment(line) + .split(separator: "=", maxSplits: 1) + if parts.count == 2 { + let k = trim(String(parts[0])) + let v = trim(String(parts[1])) + return (k, v) + } + return nil + } + +} diff --git a/phpmon/Common/PHP/PhpInitializationFile.swift b/phpmon/Common/PHP/PhpInitializationFile.swift deleted file mode 100644 index c731941..0000000 --- a/phpmon/Common/PHP/PhpInitializationFile.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// PhpInitFile.swift -// PHP Monitor -// -// Created by Nico Verbruggen on 04/05/2022. -// Copyright © 2022 Nico Verbruggen. All rights reserved. -// - -import Foundation - -class PhpInitializationFile { - - /// The file where this extension was located. - let file: String - - /// The original string contained within the file when scanned. - let raw: [String] - - /// The extensions found in this .ini file. - let extensions: [PhpExtension] - - init(fileUrl: URL) { - self.file = fileUrl.path - - let rawString = (try? String(contentsOf: fileUrl, encoding: .utf8)) ?? "" - - self.raw = rawString.components(separatedBy: "\n") - - self.extensions = PhpExtension.load(from: fileUrl) - - dump(self) - - // TODO: Actually parse the .ini file - // Parsing the file could be done like this gist: - // https://gist.github.com/jetmind/f776c0d223e4ac6aec1ff9389e874553 - } - -}