From 5a50e7fdab3b49190e90051c2b648101fbb878be Mon Sep 17 00:00:00 2001 From: Nico Verbruggen Date: Sun, 31 Jan 2021 22:50:39 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Allow=20toggling=20of=20PHP=20exten?= =?UTF-8?q?sions=20in=20php.ini?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PHP Monitor.xcodeproj/project.pbxproj | 4 ++ phpmon/Classes/Commands/Actions.swift | 10 ++--- phpmon/Classes/Commands/Startup.swift | 4 +- phpmon/Classes/Core/PhpExtension.swift | 58 +++++++++++++++++++++++++ phpmon/Classes/Core/PhpInstall.swift | 21 +++------ phpmon/Classes/Menu/StatusMenu.swift | 46 +++++++++++--------- phpmon/Extensions/StringExtension.swift | 6 +++ phpmon/Localizable.strings | 6 +-- phpmon/Singletons/MainMenu.swift | 8 ++++ 9 files changed, 115 insertions(+), 48 deletions(-) create mode 100644 phpmon/Classes/Core/PhpExtension.swift diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 928a801..11c1351 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; }; + C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; @@ -54,6 +55,7 @@ C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = ""; }; + C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = ""; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = ""; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = ""; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = ""; }; @@ -182,6 +184,7 @@ children = ( C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, C41C1B4A22B019FF00E7CF16 /* PhpInstall.swift */, + C4ACA38E25C754C100060C66 /* PhpExtension.swift */, ); path = Core; sourceTree = ""; @@ -268,6 +271,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */, C4D8016622B1584700C6DA1B /* Startup.swift in Sources */, C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */, C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, diff --git a/phpmon/Classes/Commands/Actions.swift b/phpmon/Classes/Commands/Actions.swift index f7ced8f..1ac67dc 100644 --- a/phpmon/Classes/Commands/Actions.swift +++ b/phpmon/Classes/Commands/Actions.swift @@ -165,7 +165,7 @@ class Actions { /** Runs a `brew` command. Can run as superuser. */ - private static func brew(_ command: String, sudo: Bool = false) + public static func brew(_ command: String, sudo: Bool = false) { Shell.run("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)") } @@ -173,17 +173,15 @@ class Actions { /** Runs `sed` in order to replace all occurrences of a string in a specific file with another. */ - private static func sed(file: String, original: String, replacement: String) + public static func sed(file: String, original: String, replacement: String) { - Shell.run(""" - sed -i '' 's/\(original)/\(replacement)/g' \(file) - """) + Shell.run("sed -i '' 's/\(original)/\(replacement)/g' \(file)") } /** Uses `grep` to determine whether a particular query string can be found in a particular file. */ - private static func grepContains(file: String, query: String) -> Bool + public static func grepContains(file: String, query: String) -> Bool { return Shell.pipe(""" grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO" diff --git a/phpmon/Classes/Commands/Startup.swift b/phpmon/Classes/Commands/Startup.swift index b63ec38..44dae07 100644 --- a/phpmon/Classes/Commands/Startup.swift +++ b/phpmon/Classes/Commands/Startup.swift @@ -113,9 +113,7 @@ class Startup { // Present the information to the user Alert.notify(message: messageText, info: informativeText) // Only breaking issues will throw the extra retry modal - if (breaking) { - self.failureCallback() - } + breaking ? self.failureCallback() : () } } } diff --git a/phpmon/Classes/Core/PhpExtension.swift b/phpmon/Classes/Core/PhpExtension.swift new file mode 100644 index 0000000..2f12a2d --- /dev/null +++ b/phpmon/Classes/Core/PhpExtension.swift @@ -0,0 +1,58 @@ +// +// PhpExtension.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 31/01/2021. +// Copyright © 2021 Nico Verbruggen. All rights reserved. +// + +import Foundation + +/** + A PHP extension that was detected in the php.ini file. + Please note that the extension may be disabled. + + - Note: You need to know more about regular expressions to be able to deal with these NSRegularExpression + instances. You can find more information here: https://nshipster.com/swift-regular-expressions/ + */ +class PhpExtension { + var file: String + var line: String + var name: String + var enabled: Bool + + init(_ line: String, file: String) { + let regex = try! NSRegularExpression(pattern: #"^(extension=|zend_extension=|; extension=|; zend_extension=)"(?[a-zA-Z]*).so"$"#, options: []) + let match = regex.matches(in: line, options: [], range: NSMakeRange(0, line.count)).first + let range = Range(match!.range(withName: "name"), in: line)! + + self.line = line + self.name = line[range] + self.enabled = !line.contains(";") + self.file = file + } + + public func toggle() { + Actions.sed( + file: self.file, + original: self.line, + replacement: self.enabled ? "; \(self.line)" : self.line.replacingOccurrences(of: "; ", with: "") + ) + self.enabled = !self.enabled + } + + static func load(from path: URL) -> [PhpExtension] { + let file = try! String(contentsOf: path, encoding: .utf8) + + return file.components(separatedBy: "\n") + .filter({ (line) -> Bool in + return line.range( + of: #"^(extension=|zend_extension=|; extension=|; zend_extension=)"[a-zA-Z]*.so"$"#, + options: .regularExpression + ) != nil + }) + .map { (line) -> PhpExtension in + return PhpExtension(line, file: path.path) + } + } +} diff --git a/phpmon/Classes/Core/PhpInstall.swift b/phpmon/Classes/Core/PhpInstall.swift index 16b4f5f..464f4b7 100644 --- a/phpmon/Classes/Core/PhpInstall.swift +++ b/phpmon/Classes/Core/PhpInstall.swift @@ -9,8 +9,8 @@ import Foundation class PhpInstall { - var version: Version = Version() - var xdebug: Xdebug = Xdebug() + var version: Version! + var extensions: [PhpExtension]! // MARK: - Computed @@ -23,6 +23,8 @@ class PhpInstall { init() { let version = Command.execute(path: Paths.php, arguments: ["-r", "print phpversion();"]) + self.version = Version() + if (version == "" || version.contains("Warning")) { self.version.short = "💩 BROKEN" self.version.long = ""; @@ -39,13 +41,9 @@ class PhpInstall { // Get the first two elements self.version.short = segments[0...1].joined(separator: ".") - // Determine the Xdebug status - self.xdebug = Xdebug( - found: Actions.didFindXdebug(self.version.short), - enabled: Actions.didEnableXdebug(self.version.short) - ) - - self.version.error = false + // Load extension information + let path = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(self.version.short)/php.ini") + self.extensions = PhpExtension.load(from: path) } // MARK: - Structs @@ -55,9 +53,4 @@ class PhpInstall { var long = "???" var error = false } - - struct Xdebug { - var found: Bool = false - var enabled: Bool = false - } } diff --git a/phpmon/Classes/Menu/StatusMenu.swift b/phpmon/Classes/Menu/StatusMenu.swift index 32d6fb6..486f49d 100644 --- a/phpmon/Classes/Menu/StatusMenu.swift +++ b/phpmon/Classes/Menu/StatusMenu.swift @@ -7,10 +7,6 @@ import Cocoa -class PhpMenuItem: NSMenuItem { - var version: String = "" -} - class StatusMenu : NSMenu { public func addPhpVersionMenuItems() { @@ -79,26 +75,34 @@ class StatusMenu : NSMenu { self.addItem(NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i")) self.addItem(NSMenuItem.separator()) - self.addItem(NSMenuItem(title: "mi_enabled_extensions".localized, action: nil, keyEquivalent: "")) - self.addXdebugMenuItem() - } - - private func addXdebugMenuItem() - { - let xdebugFound = App.phpInstall!.xdebug.found - let xdebugOn = App.phpInstall!.xdebug.enabled + self.addItem(NSMenuItem(title: "mi_detected_extensions".localized, action: nil, keyEquivalent: "")) - let menuItem = NSMenuItem( - title: xdebugFound ? "mi_xdebug".localized : "mi_xdebug_missing".localized, - action: #selector(MainMenu.toggleXdebug), keyEquivalent: "x" - ) - - if (!xdebugFound) { - menuItem.isEnabled = false - } else { - menuItem.state = xdebugOn ? .on : .off + for phpExtension in App.phpInstall!.extensions { + self.addExtensionItem(phpExtension) } + if (App.phpInstall!.extensions.count == 0) { + self.addItem(NSMenuItem(title: "mi_no_extensions_detected".localized, action: nil, keyEquivalent: "")) + } + } + + private func addExtensionItem(_ phpExtension: PhpExtension) { + let menuItem = ExtensionMenuItem( + title: "\(phpExtension.name.capitalized) (php.ini)", + action: #selector(MainMenu.toggleExtension), keyEquivalent: "" + ) + menuItem.state = phpExtension.enabled ? .on : .off + menuItem.phpExtension = phpExtension self.addItem(menuItem) } } + +// MARK: - In order to store extra data in each item, NSMenuItem is subclassed + +class PhpMenuItem: NSMenuItem { + var version: String = "" +} + +class ExtensionMenuItem: NSMenuItem { + var phpExtension: PhpExtension? = nil +} diff --git a/phpmon/Extensions/StringExtension.swift b/phpmon/Extensions/StringExtension.swift index 8ca2b54..21ea98b 100644 --- a/phpmon/Extensions/StringExtension.swift +++ b/phpmon/Extensions/StringExtension.swift @@ -27,4 +27,10 @@ extension String { return count } + + subscript (r: Range) -> String { + let start = r.lowerBound + let end = r.upperBound + return String(self[start ..< end]) + } } diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 9a71400..aac6309 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -29,10 +29,8 @@ "mi_valet_config" = "Locate Valet folder (.config/valet)"; "mi_php_config" = "Locate PHP configuration file (php.ini)"; "mi_phpinfo" = "Show current configuration (phpinfo)"; -"mi_enabled_extensions" = "Enabled Extensions"; - -"mi_xdebug" = "Xdebug"; -"mi_xdebug_missing" = "xdebug.so missing"; +"mi_detected_extensions" = "Detected Extensions"; +"mi_no_extensions_detected" = "No additional extensions detected."; "mi_quit" = "Quit PHP Monitor"; "mi_about" = "About PHP Monitor"; diff --git a/phpmon/Singletons/MainMenu.swift b/phpmon/Singletons/MainMenu.swift index d8ea0f6..93d6b89 100644 --- a/phpmon/Singletons/MainMenu.swift +++ b/phpmon/Singletons/MainMenu.swift @@ -210,6 +210,14 @@ class MainMenu: NSObject, NSWindowDelegate { }) } + @objc public func toggleExtension(sender: ExtensionMenuItem) { + self.waitAndExecute({ + // Toggle that extension + print("Toggling extension '\(sender.phpExtension!.name)'") + sender.phpExtension?.toggle() + }) + } + @objc public func openPhpInfo() { self.waitAndExecute({ try! "