diff --git a/.gitignore b/.gitignore index cc4368e..80b325c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ phpmon.xcodeproj/project.xcworkspace phpmon.xcodeproj/xcuserdata PHP Monitor.xcodeproj/project.xcworkspace PHP Monitor.xcodeproj/xcuserdata +.DS_Store \ No newline at end of file diff --git a/PHP Monitor.xcodeproj/project.pbxproj b/PHP Monitor.xcodeproj/project.pbxproj index 85324d7..971c5f8 100644 --- a/PHP Monitor.xcodeproj/project.pbxproj +++ b/PHP Monitor.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; }; C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; }; + C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; }; C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; }; C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; }; C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; }; @@ -32,6 +33,7 @@ /* Begin PBXFileReference section */ C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = ""; }; C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = ""; }; + C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = ""; }; C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -51,6 +53,8 @@ C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.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 = ""; }; C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -80,6 +84,8 @@ isa = PBXGroup; children = ( C4F8C0A522D4FA41002EFE61 /* README.md */, + C4E713562570150F00007428 /* SECURITY.md */, + C4E713572570151400007428 /* docs */, C41C1B3522B0097F00E7CF16 /* phpmon */, C41C1B3422B0097F00E7CF16 /* Products */, ); @@ -164,6 +170,7 @@ C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */, C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */, C474B00524C0E98C00066A22 /* LocalNotification.swift */, + C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */, ); path = Helpers; sourceTree = ""; @@ -258,6 +265,7 @@ C4811D2422D70A4700B5F6B3 /* App.swift in Sources */, C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */, C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */, + C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, @@ -407,7 +415,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -415,7 +423,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4; + MARKETING_VERSION = 2.5; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -431,7 +439,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 25; + CURRENT_PROJECT_VERSION = 26; DEVELOPMENT_TEAM = 8M54J5J787; ENABLE_HARDENED_RUNTIME = YES; INFOPLIST_FILE = phpmon/Info.plist; @@ -439,7 +447,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 2.4; + MARKETING_VERSION = 2.5; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/README.md b/README.md index a8f8d55..eec19cc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PHP Monitor (or phpmon) is a lightweight macOS utility app that runs on your Mac It also gives you quick access to various useful functionality (like switching PHP versions, restarting services, accessing configuration files, and more). -phpmon screenshot +phpmon screenshot For me, it comes in handy when running multiple versions of PHP with Homebrew. If you wish to be able to see at a glance which version is currently linked & active with Laravel Valet, PHP Monitor is your new best friend. @@ -14,13 +14,13 @@ It's also super convenient to switch between different versions of PHP, or to fi ## 🖥 System requirements -PHP Monitor is a universal application that runs on Apple Silicon *and* Intel-based Macs. +PHP Monitor is a universal application that runs on Apple Silicon **and** Intel-based Macs. * macOS 10.15 Catalina or higher (works on macOS 11 Big Sur) -* PHP 7.4 installed with Homebrew 2.x +* The brew formula `php` has to be installed (which version it is, is detected) * Laravel Valet 2.x -_Please note that future versions of PHP will not work automatically, minor changes are required to add support for newer versions of PHP._ +_Please note that future versions of PHP will not work automatically, minor changes are usually required to add support for newer versions of PHP. You may need to update your Valet installation to keep everything working if a major version update of PHP has been released._ ## 🚀 How to install @@ -51,14 +51,14 @@ This utility will detect which PHP versions you have installed via Homebrew, and This means: -- You have at least the latest version of PHP installed (`php@7.4`) +- You have at least the latest version of PHP installed (`php`) - You have installed Laravel Valet (`which valet` returns `/usr/local/bin/valet`) - You ran `valet trust`, which means Valet commands can be run without using sudo The utility runs the following commands: - Unlink all detected PHP versions -- Switch to PHP 7.4 (this is done to ensure that Valet works, even when attempting to use PHP 5.6) +- Switch to whatever version of PHP `php` is at (this is done to ensure that Valet works, even when attempting to use PHP 5.6) - Stop all php-fpm service instances - Link the desired version of PHP - Start the correct php-fpm service for the desired PHP version @@ -71,12 +71,12 @@ This app isn't very complicated after all. In the end, this just (conveniently) ## 🤬 Troubleshooting -**If you are having issues, the first thing you should be doing is installing the latest version of PHP Monitor. This can resolve a variety of issues.** +**If you are having issues, the first thing you should be doing is installing the latest version of PHP Monitor _and_ Laravel Valet. This can resolve a variety of issues. Don't forget to run `valet install` after upgrading.** PHP Monitor performs some integrity checks to ensure a good experience when using the app. You'll get a message telling you that PHP Monitor won't work correctly in the following scenarios: - The PHP binary is not located in `/usr/local/bin/php` -- PHP 7.4 is missing in `/usr/local/opt` +- PHP is missing in `/usr/local/opt` - 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` @@ -84,9 +84,9 @@ PHP Monitor performs some integrity checks to ensure a good experience when usin Follow instructions as specified in the alert in order to resolve any issues. -## 📝 Additional information +## 📝 Additional information & FAQ -Please consult the [additional information][2] file that contains more information. +Please consult the [additional information][2] file that contains more information. It has answers to additional questions and more information to troubleshoot your problem. ## ⭐️ Star me! diff --git a/SECURITY.md b/SECURITY.md index 14b2c59..e936605 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,8 @@ The following versions of PHP Monitor are supported: | Version | Universal | Supported | Runs on macOS | | ------- | ------------- | ------------------ | ----- | -| 2.4 | ✅ | ✅ | Catalina (10.15), Big Sur (11.0) | +| 2.5 | ✅ | ✅ | Catalina (10.15), Big Sur (11.0) | +| 2.4 | ✅ | ❌ | Catalina (10.15), Big Sur (11.0) | | < 2.4 | ❌ | ❌ | Catalina (10.15) | ## Reporting a Vulnerability diff --git a/docs/ADDITIONAL.md b/docs/ADDITIONAL.md index 30a7bc7..4e8230c 100644 --- a/docs/ADDITIONAL.md +++ b/docs/ADDITIONAL.md @@ -1,6 +1,14 @@ ### Q&A -#### Q: This app is doing network requests? +#### Q: Does this support Apple Silicon? + +Yes. This is a universal app. + +#### Q: Is PHP 8.x supported? + +Yes. + +#### Q: This app is doing network requests? Why? It's Homebrew. I can't prevent `brew` from doing things via the network when I invoke it. @@ -49,10 +57,14 @@ Super convenient! #### Q: PHP Monitor says that the latest version of PHP is not installed, but it is! -Try installing again using `brew install php@7.4`. +Try installing again using `brew install php`. This should resolve the issue. +#### Q: PHP Monitor says the correct version is loaded, but my Valet sites don't work! + +You may need to run `valet install`. (Preferably after updating `valet` by running `composer global update`). + #### Q: PHP Monitor reports another version compared to phpinfo on my local website, what is going on? _Beginning with version 2.0 you'll get alerts about this at startup._ @@ -96,4 +108,4 @@ The easiest way to make sure that PHP Monitor works again is to run the followin Then, in PHP Monitor, select "Restart php-fpm service", which should start the service. -Alternatively, you can run `sudo brew services start php@7.4` where `7.4` is your preferred version of PHP (for the latest version of PHP, you may omit `@7.4` like in the example above). \ No newline at end of file +Alternatively, you can run `sudo brew services start php@7.4` where `7.4` is your preferred version of PHP (for the latest version of PHP, you may omit `@7.4` like in the example above). diff --git a/docs/screenshot.png b/docs/screenshot.png index 24759b8..e743403 100644 Binary files a/docs/screenshot.png and b/docs/screenshot.png differ diff --git a/phpmon/Classes/Commands/Actions.swift b/phpmon/Classes/Commands/Actions.swift index 5e28446..19215f0 100644 --- a/phpmon/Classes/Commands/Actions.swift +++ b/phpmon/Classes/Commands/Actions.swift @@ -14,21 +14,32 @@ class Actions { public static func detectPhpVersions() -> [String] { let files = Shell.user.pipe("ls /usr/local/opt | grep php@") var versions = files.components(separatedBy: "\n") + // Remove all empty strings versions.removeAll { (string) -> Bool in return (string == "") } + // Get a list of versions only var versionsOnly : [String] = [] versions.forEach { (string) in versionsOnly.append(string.components(separatedBy: "php@")[1]) } + + // Make sure the aliased version is detected + // The user may have `php` installed, but not e.g. `php@8.0` + // We should also detect that as a version that is installed + let phpAlias = App.shared.brewPhpVersion + if (!versionsOnly.contains(phpAlias)) { + versionsOnly.append(phpAlias); + } + return versionsOnly } public static func restartPhpFpm() { let version = App.shared.currentVersion!.short - if (version == Constants.LatestPhpVersion) { + if (version == App.shared.brewPhpVersion) { Shell.user.run("sudo brew services restart php") } else { Shell.user.run("sudo brew services restart php@\(version)") @@ -45,16 +56,16 @@ class Actions { // Unlink the current version Shell.user.run("brew unlink php@\(version)") // Stop the services - if (version == Constants.LatestPhpVersion) { + if (version == App.shared.brewPhpVersion) { Shell.user.run("sudo brew services stop php") } else { Shell.user.run("sudo brew services stop php@\(version)") } } - if (availableVersions.contains(Constants.LatestPhpVersion)) { + if (availableVersions.contains(App.shared.brewPhpVersion)) { // Use the latest version as a default - Shell.user.run("brew link php@\(Constants.LatestPhpVersion) --overwrite --force") - if (version == Constants.LatestPhpVersion) { + Shell.user.run("brew link php@\(App.shared.brewPhpVersion) --overwrite --force") + if (version == App.shared.brewPhpVersion) { // If said version was also requested, all we need to do is start the service Shell.user.run("sudo brew services start php") } else { @@ -115,7 +126,7 @@ class Actions { let versions = self.detectPhpVersions() versions.forEach { (version) in Shell.user.run("brew unlink php@\(version)") - if (version == Constants.LatestPhpVersion) { + if (version == App.shared.brewPhpVersion) { Shell.user.run("brew services stop php") Shell.user.run("sudo brew services stop php") } else { diff --git a/phpmon/Classes/Commands/Startup.swift b/phpmon/Classes/Commands/Startup.swift index fa8e3d5..84a38be 100644 --- a/phpmon/Classes/Commands/Startup.swift +++ b/phpmon/Classes/Commands/Startup.swift @@ -32,9 +32,9 @@ class Startup { ) self.performEnvironmentCheck( - !Shell.user.pipe("ls /usr/local/opt | grep php@7.4").contains("php@7.4"), - messageText: "PHP 7.4 is not correctly installed", - informativeText: "PHP 7.4 alias was not found in `/usr/local/opt`. The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php@7.4` in order for PHP Monitor to detect this installation.", + !Shell.user.pipe("ls /usr/local/opt | grep php").contains("php"), + messageText: "PHP is not correctly installed", + informativeText: "PHP alias was not found in `/usr/local/opt`. The app will not work correctly until you resolve this issue. If you already have the `php` formula installed, you may need to run `brew install php` in order for PHP Monitor to detect this installation.", breaking: true ) @@ -72,10 +72,30 @@ class Startup { ) if (!self.failed) { + self.determineBrewAliasVersion() success() } } + /** + * In order to avoid having to hard-code which version of PHP is aliased to what specific subversion, + * PHP Monitor now determines the alias by checking the user's system. + */ + private func determineBrewAliasVersion() + { + print("PHP Monitor has determined the application has successfully passed all checks.") + print("Determining which version of PHP is aliased to `php` via Homebrew...") + + let brewPhpAlias = Shell.user.pipe("brew info php --json"); + + App.shared.brewPhpPackage = try! JSONDecoder().decode( + [HomebrewPackage].self, + from: brewPhpAlias.data(using: .utf8)! + ).first! + + print("When on your system, the `php` formula means version \(App.shared.brewPhpVersion)!") + } + /** * Perform an environment check. Will cause the application to terminate, if `breaking` is set to true. * diff --git a/phpmon/Classes/Helpers/HomebrewPackage.swift b/phpmon/Classes/Helpers/HomebrewPackage.swift new file mode 100644 index 0000000..4f662fd --- /dev/null +++ b/phpmon/Classes/Helpers/HomebrewPackage.swift @@ -0,0 +1,19 @@ +// +// HomebrewPackage.swift +// PHP Monitor +// +// Created by Nico Verbruggen on 26/11/2020. +// Copyright © 2020 Nico Verbruggen. All rights reserved. +// + +import Foundation + +struct HomebrewPackage : Decodable { + let name: String + let full_name: String + let aliases: [String] + + public func getVersion() -> String { + return aliases.first!.replacingOccurrences(of: "php@", with: "") + } +} diff --git a/phpmon/Classes/Menu/StatusMenu.swift b/phpmon/Classes/Menu/StatusMenu.swift index c160543..f8a9dfa 100644 --- a/phpmon/Classes/Menu/StatusMenu.swift +++ b/phpmon/Classes/Menu/StatusMenu.swift @@ -35,7 +35,8 @@ class StatusMenu : NSMenu { for index in (0..Relevance Essential Purpose - PHP Monitor directly invokes Homebrew which contacts GitHub. + PHP Monitor directly invokes Homebrew which contacts GitHub. This happens when PHP Monitor asks for more information about the PHP formula to determine which version of PHP you've got running. DenyConsequences If you deny these connections, PHP Monitor might not be able to complete its preset set of instructions, causing version switching to fail. diff --git a/phpmon/Localizable.strings b/phpmon/Localizable.strings index 0f79f92..9307c8d 100644 --- a/phpmon/Localizable.strings +++ b/phpmon/Localizable.strings @@ -23,8 +23,9 @@ "mi_force_load_latest" = "Force load latest PHP version"; "mi_configuration" = "Configuration"; -"mi_valet_config" = "Valet configuration (.config/valet)"; -"mi_php_config" = "PHP Configuration file (php.ini)"; +"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"; diff --git a/phpmon/Singletons/App.swift b/phpmon/Singletons/App.swift index ad179e5..cd7bfb9 100644 --- a/phpmon/Singletons/App.swift +++ b/phpmon/Singletons/App.swift @@ -32,4 +32,24 @@ class App { */ var timer: Timer? + /** + Information we were able to discern from the Homebrew info command (as JSON). + */ + var brewPhpPackage: HomebrewPackage? = nil { + didSet { + self.brewPhpVersion = self.brewPhpPackage!.getVersion() + } + } + + /** + The version that the `php` formula via Brew is aliased to on the current system. + + If you're up to date, `php` will be aliased to the latest version, + but that might not be the case. + + We'll technically default to version 8.0, but the information should always be loaded + from Homebrew itself upon starting the application. + */ + var brewPhpVersion: String = "8.0" + } diff --git a/phpmon/Singletons/MainMenu.swift b/phpmon/Singletons/MainMenu.swift index 7119e4a..5f28e09 100644 --- a/phpmon/Singletons/MainMenu.swift +++ b/phpmon/Singletons/MainMenu.swift @@ -198,6 +198,12 @@ class MainMenu: NSObject, NSWindowDelegate { }) } + @objc public func openPhpInfo() { + try! " /tmp/phpmon_phpinfo.html") + NSWorkspace.shared.open(URL(string: "file:///private/tmp/phpmon_phpinfo.html")!) + } + @objc public func forceRestartLatestPhp() { // Tell the user the switch is about to occur _ = Alert.present( diff --git a/phpmon/View Controllers/Base.lproj/Main.storyboard b/phpmon/View Controllers/Base.lproj/Main.storyboard index c2c0ba1..a2f2ee0 100644 --- a/phpmon/View Controllers/Base.lproj/Main.storyboard +++ b/phpmon/View Controllers/Base.lproj/Main.storyboard @@ -1,9 +1,8 @@ - + - - + @@ -55,68 +54,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -