diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index afff5a5..9b0cec9 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */; }; C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Actions.swift */; }; C42295DD2358D02000E263B2 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; }; + C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; }; C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; @@ -35,6 +36,7 @@ C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersion.swift; sourceTree = ""; }; C41C1B4C22B0215A00E7CF16 /* Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = ""; }; C42295DC2358D02000E263B2 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; + C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = ""; }; C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = ""; }; @@ -139,6 +141,7 @@ isa = PBXGroup; children = ( C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */, + C46FA23E246C358E00944F05 /* StringExtension.swift */, ); path = Extensions; sourceTree = ""; @@ -224,6 +227,7 @@ C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, + C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */, C4EE188422D3386B00E126E5 /* Constants.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -365,14 +369,14 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = 8M54J5J787; INFOPLIST_FILE = phpmon/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8; + MARKETING_VERSION = 1.9; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -387,14 +391,14 @@ CODE_SIGN_IDENTITY = "-"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = 8M54J5J787; INFOPLIST_FILE = phpmon/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 1.8; + MARKETING_VERSION = 1.9; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; diff --git a/README.md b/README.md index cd8bfd9..3345c08 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ PHP Monitor performs some integrity checks to ensure a good experience when usin - Laravel Valet is missing in `/usr/local/bin/valet` - Brew has not been added to sudoers in `/private/etc/sudoers.d/brew` - Valet has not been added to sudoers in `/private/etc/sudoers.d/valet` +- Multiple PHP services are active (see more info below) Follow instructions as specified in the alert in order to resolve any issues. @@ -107,9 +108,10 @@ The easiest way to make sure that PHP Monitor works again is to run the followin sudo brew services stop php@7.1 sudo brew services stop php@7.0 sudo brew services stop php@5.6 + sudo brew services stop nginx Then, in PHP Monitor, select "Restart php-fpm service", which should start the service. Alternatively, you can run `sudo brew services start php@{x}` where `{x}` is your preferred version of PHP (for the latest version of PHP, you can omit `@{x}`). --- -If this software has been useful to you, star the repository so I know that the software is being used. I did not include any tracking or analytics software, so if you encounter issues, let me know via an issue. \ No newline at end of file +If this software has been useful to you, star the repository so I know that the software is being used. I did not include any tracking or analytics software, so if you encounter issues, let me know via an issue. diff --git a/phpmon/Classes/Commands/Actions.swift b/phpmon/Classes/Commands/Actions.swift index ea88403..c1bc361 100644 --- a/phpmon/Classes/Commands/Actions.swift +++ b/phpmon/Classes/Commands/Actions.swift @@ -103,4 +103,25 @@ class Actions { } Shell.user.run(command) } + + // unlink all the crap and link the latest version + // this also restarts all services + public static func fixMyPhp() { + let versions = self.detectPhpVersions() + versions.forEach { (version) in + Shell.user.run("brew unlink php@\(version)") + if (version == Constants.LatestPhpVersion) { + Shell.user.run("brew services stop php") + Shell.user.run("sudo brew services stop php") + } else { + Shell.user.run("brew services stop php@\(version)") + Shell.user.run("sudo brew services stop php@\(version)") + } + } + Shell.user.run("brew services stop php") + Shell.user.run("brew services stop nginx") + Shell.user.run("brew link php") + Shell.user.run("sudo brew services restart php") + Shell.user.run("sudo brew services restart nginx") + } } diff --git a/phpmon/Classes/Commands/Startup.swift b/phpmon/Classes/Commands/Startup.swift index 1a81514..11f9615 100644 --- a/phpmon/Classes/Commands/Startup.swift +++ b/phpmon/Classes/Commands/Startup.swift @@ -15,7 +15,7 @@ class Startup { self.presentAlertOnMainThreadIf( !Shell.user.pipe("which php").contains("/usr/local/bin/php"), messageText: "PHP is not correctly installed", - informativeText: "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php`. The app will not work correctly until you resolve this issue." + informativeText: "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php`. The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)" ) self.presentAlertOnMainThreadIf( @@ -41,6 +41,17 @@ class Startup { messageText: "Valet has not been added to sudoers.d", informativeText: "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue." ) + + let services = Shell.user.pipe("brew services list | grep php") + self.presentAlertOnMainThreadIf( + (services.countInstances(of: "started") > 1), + messageText: "Multiple PHP services are active", + informativeText: "This can cause php-fpm to serve a more recent version of PHP than the one you'd like to see active. Please terminate all extra PHP processes." + + "\n\nThe easiest solution is to choose the option 'Force load latest PHP version' in the menu bar." + + "\n\nAlternatively, you can fix this manually. You can do this by running `brew services list` and running `sudo brew services stop php@7.3` (and use the version that applies)." + + "\n\nPHP Monitor usually handles the starting and stopping of these services, so once the correct version is the only PHP version running you should not have any issues. It is recommended to restart PHP Monitor once you have resolved this issue." + + "\n\nFor more information about this issue, please see the README.md file in the repository on GitHub." + ) } private static func presentAlertOnMainThreadIf( diff --git a/phpmon/Classes/Helpers/PhpVersion.swift b/phpmon/Classes/Helpers/PhpVersion.swift index 690e1f2..bf6c350 100644 --- a/phpmon/Classes/Helpers/PhpVersion.swift +++ b/phpmon/Classes/Helpers/PhpVersion.swift @@ -16,9 +16,18 @@ class PhpVersion { var xdebugFound: Bool = false var xdebugEnabled : Bool = false + var error : Bool = false + init() { let version = Command.execute(path: "/usr/local/bin/php", arguments: ["-r", "print phpversion();"]) + if (version == "" || version.contains("Warning")) { + self.short = "💩 BROKEN" + self.long = ""; + self.error = true + return; + } + // That's the long version self.long = version @@ -33,5 +42,7 @@ class PhpVersion { if (self.xdebugFound) { self.xdebugEnabled = Actions.XdebugEnabled(self.short) } + + self.error = false; } } diff --git a/phpmon/Extensions/StringExtension.swift b/phpmon/Extensions/StringExtension.swift new file mode 100644 index 0000000..d2f46db --- /dev/null +++ b/phpmon/Extensions/StringExtension.swift @@ -0,0 +1,22 @@ +// +// StringExtension.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 13/05/2020. +// Copyright © 2020 Nico Verbruggen. All rights reserved. +// + +import Foundation + +extension String { + func countInstances(of stringToFind: String) -> Int { + assert(!stringToFind.isEmpty) + var count = 0 + var searchRange: Range? + while let foundRange = range(of: stringToFind, options: [], range: searchRange) { + count += 1 + searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex)) + } + return count + } +} diff --git a/phpmon/Singletons/MainMenu.swift b/phpmon/Singletons/MainMenu.swift index 4bc07d2..a3f68b9 100644 --- a/phpmon/Singletons/MainMenu.swift +++ b/phpmon/Singletons/MainMenu.swift @@ -42,9 +42,18 @@ class MainMenu: NSObject, NSWindowDelegate { let menu = NSMenu() var string = "We are not sure what version of PHP you are running." if (App.shared.currentVersion != nil) { - string = "You are running PHP \(App.shared.currentVersion!.long)" + if (!App.shared.currentVersion!.error) { + string = "You are running PHP \(App.shared.currentVersion!.long)" + menu.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: "")) + } else { + // in case of an error show the error message + menu.addItem(NSMenuItem(title: "Oof! It appears your PHP installation is broken...", action: nil, keyEquivalent: "")) + menu.addItem(NSMenuItem(title: "Try running `php -v` in your terminal.", action: nil, keyEquivalent: "")) + menu.addItem(NSMenuItem(title: "You could also try switching to another version.", action: nil, keyEquivalent: "")) + menu.addItem(NSMenuItem(title: "Running `brew reinstall php` (or for the equivalent version) might help.", action: nil, keyEquivalent: "")) + } } - menu.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: "")) + menu.addItem(NSMenuItem.separator()) if (App.shared.availablePhpVersions.count > 0 && !App.shared.busy) { var shortcutKey = 1 @@ -60,6 +69,7 @@ class MainMenu: NSObject, NSWindowDelegate { menu.addItem(NSMenuItem(title: "Active Services", action: nil, keyEquivalent: "")) menu.addItem(NSMenuItem(title: "Restart php-fpm service", action: #selector(self.restartPhpFpm), keyEquivalent: "f")) menu.addItem(NSMenuItem(title: "Restart nginx service", action: #selector(self.restartNginx), keyEquivalent: "n")) + menu.addItem(NSMenuItem(title: "Force load latest PHP version", action: #selector(self.fixMyPhp), keyEquivalent: "")) menu.addItem(NSMenuItem.separator()) } if (App.shared.busy) { @@ -219,6 +229,24 @@ class MainMenu: NSObject, NSWindowDelegate { } } + @objc public func fixMyPhp() { + DispatchQueue.global(qos: .userInitiated).async { [unowned self] in + DispatchQueue.main.async { + Alert.present(messageText: "PHP Monitor will force reload the latest version of PHP", informativeText: "This can take a while. You'll get another alert when the force reload has completed.") + App.shared.busy = true + self.updatePhpVersionInStatusBar() + self.update() + } + Actions.fixMyPhp() + DispatchQueue.main.async { + Alert.present(messageText: "PHP has been force reloaded", informativeText: "All appropriate services have been restarted, and the latest version of PHP is now active. You can now try switching to another version of PHP.") + App.shared.busy = false + self.updatePhpVersionInStatusBar() + self.update() + } + } + } + func windowWillClose(_ notification: Notification) { App.shared.windowController = nil Shell.user.delegate = nil