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

🚀 Version 2.6

* develop:
  📝 Updated quick troubleshooting
  📝 Updated documentation
  📝 Updated SECURITY
  📝 Updated README
   New build number, updated shortcut keys
  🌐 Add info about `valet install` when running force reload
   Fix my PHP should also restart dnsmasq
   Add menu item to restart dnsmasq
  🔧 Bump build number
  ♻️ Cleanup, updated README (#20)
  ♻️ Refactoring, version bump for M1 support (#20)
   Add support for Homebrew in /opt/homebrew (#20)
  👌 Clean up switchToPhpVersion() and fixMyPhp()
  👌 Avoid using sender.tag
  👌 Improved pipe() method
  🌐 Localized startup environment checks
  🐛 Perform the phpinfo.html on another thread and show busy
  📝 Updated instructions
This commit is contained in:
2021-01-06 17:51:10 +01:00
17 changed files with 347 additions and 150 deletions

View File

@ -25,6 +25,8 @@
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; }; C4811D2422D70A4700B5F6B3 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; }; C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
C486EFFC2586931100A02B2C /* PhpMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C486EFFB2586931100A02B2C /* PhpMenuItem.swift */; };
C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; };
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; }; C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; }; C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; }; C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
@ -52,6 +54,8 @@
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; }; C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; }; C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
C486EFFB2586931100A02B2C /* PhpMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpMenuItem.swift; sourceTree = "<group>"; };
C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; }; C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; }; C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; }; C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
@ -139,6 +143,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C47331A1247093B7009A0597 /* StatusMenu.swift */, C47331A1247093B7009A0597 /* StatusMenu.swift */,
C486EFFB2586931100A02B2C /* PhpMenuItem.swift */,
); );
path = Menu; path = Menu;
sourceTree = "<group>"; sourceTree = "<group>";
@ -146,6 +151,7 @@
C4811D2622D70CEF00B5F6B3 /* Singletons */ = { C4811D2622D70CEF00B5F6B3 /* Singletons */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C49EAB45259FC305007F6C3B /* Paths.swift */,
C41C1B4622B009A400E7CF16 /* Shell.swift */, C41C1B4622B009A400E7CF16 /* Shell.swift */,
C42295DC2358D02000E263B2 /* Command.swift */, C42295DC2358D02000E263B2 /* Command.swift */,
C4811D2322D70A4700B5F6B3 /* App.swift */, C4811D2322D70A4700B5F6B3 /* App.swift */,
@ -268,6 +274,8 @@
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */, C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */, C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */,
C486EFFC2586931100A02B2C /* PhpMenuItem.swift in Sources */,
C49EAB46259FC305007F6C3B /* Paths.swift in Sources */,
C476FF9822B0DD830098105B /* Alert.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */,
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */, C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */, C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
@ -415,7 +423,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 26; CURRENT_PROJECT_VERSION = 31;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
@ -423,7 +431,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.5; MARKETING_VERSION = 2.6;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@ -439,7 +447,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 26; CURRENT_PROJECT_VERSION = 31;
DEVELOPMENT_TEAM = 8M54J5J787; DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist; INFOPLIST_FILE = phpmon/Info.plist;
@ -447,7 +455,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MARKETING_VERSION = 2.5; MARKETING_VERSION = 2.6;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -4,21 +4,22 @@
PHP Monitor (or phpmon) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. PHP Monitor (or phpmon) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar.
It also gives you quick access to various useful functionality (like switching PHP versions, restarting services, accessing configuration files, and more). <img src="./docs/screenshot.png" width="370px" alt="phpmon screenshot (menu bar app)"/>
<img src="./docs/screenshot.png" width="370px" alt="phpmon screenshot"/> It's also super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
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. <img src="./docs/notification.png" width="370px" alt="phpmon screenshot (notification)"/>
It's also super convenient to switch between different versions of PHP, or to find your currently active .ini file! It also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
## 🖥 System requirements ## 🖥 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) * macOS 10.15 Catalina or higher (works on macOS 11 Big Sur)
* The brew formula `php` has to be installed (which version it is, is detected) * Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew` (the default)
* Laravel Valet 2.x * The brew formula `php` has to be installed (which version is detected)
* Laravel Valet 2.13 or higher
_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._ _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._
@ -71,12 +72,12 @@ This app isn't very complicated after all. In the end, this just (conveniently)
## 🤬 Troubleshooting ## 🤬 Troubleshooting
**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.** **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. To upgrade Valet, run `composer global update`. 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: 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` - The PHP binary is not located in `/usr/local/bin/php` (or `/opt/homebrew/bin/php`)
- PHP is missing in `/usr/local/opt` - PHP is missing in `/usr/local/opt` (or `/opt/homebrew/opt`)
- Laravel Valet is missing in `/usr/local/bin/valet` - Laravel Valet is missing in `/usr/local/bin/valet`
- Brew has not been added to sudoers in `/private/etc/sudoers.d/brew` - 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` - Valet has not been added to sudoers in `/private/etc/sudoers.d/valet`
@ -84,9 +85,29 @@ 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. Follow instructions as specified in the alert in order to resolve any issues.
## 📝 Additional information & FAQ ## 🏎 Quick Troubleshooting
Please consult the [additional information][2] file that contains more information. It has answers to additional questions and more information to troubleshoot your problem. ### PHP Monitor tells me `php` is not installed
Try installing again using `brew install php`.
This should resolve the issue! If that does not fix the issue, run `brew link php --force`. (Afterwards, you may need to restart your terminal to make sure the new linked version is detected.)
brew install php
brew link php --force
### Valet sites won't load (502 Bad Gateway)
If you're visiting your `.test` domain, and you're getting a 502 (Bad Gateway) after switching to a different PHP version, you're dealing with a common issue.
This problem is usually resolved by upgrading Valet and running `valet install` again.
composer global update
valet install
## 📝 Additional Info
Please consult the [additional file][2] that contains more information. It may have answers to additional questions and more information to troubleshoot your problem.
## ⭐️ Star me! ## ⭐️ Star me!
@ -96,7 +117,7 @@ I did not include any tracking or analytics software, so if you encounter issues
## 💵 Support me? ## 💵 Support me?
I develop this application in my spare time, after work. If you find the application useful and you have a bit of money to spare, feel free to send me [a tip via PayPal][3]. I develop this application in my spare time, after work. If you find the application useful and you have a bit of money to spare, feel free to send me [a tip via PayPal][3].
[1]: https://github.com/nicoverbruggen/phpmon/releases [1]: https://github.com/nicoverbruggen/phpmon/releases
[2]: docs/ADDITIONAL.md [2]: docs/ADDITIONAL.md

View File

@ -1,15 +1,21 @@
# Security Policy # Security Policy
## Supported Versions ## Supported versions
The following versions of PHP Monitor are supported: Generally speaking, only the latest version of **PHP Monitor** is supported:
| Version | Universal | Supported | Runs on macOS | | Version | Apple Silicon | Supported | Supported macOS | Deployment Target |
| ------- | ------------- | ------------------ | ----- | | ------- | ------------- | ------------------ | ----- | ----- |
| 2.5 | ✅ | ✅ | Catalina (10.15), Big Sur (11.0) | | 2.6 | ✅ Universal binary, full support | ✅ | Big Sur (11.0) | macOS 10.14+ |
| 2.4 | ✅ | ❌ | Catalina (10.15), Big Sur (11.0) |
| < 2.4 | | | Catalina (10.15) |
## Reporting a Vulnerability The following versions are no longer supported:
Contact Nico Verbruggen at the email address used for the commits in the repository. Please include "PHP Monitor" in the subject. | Version | Apple Silicon | Supported | Supported macOS | Deployment Target |
| ------- | ------------- | ------------------ | ----- | ----- |
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only) | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ |
| 2.4 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only) | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ |
| < 2.4 | (Intel binary<br/>`/usr/local/homebrew` installations only) | ❌ | Catalina (10.15) | macOS 10.14+ |
## Reporting a vulnerability
Contact me (Nico Verbruggen) at the email address used for the commits in the repository. Please include "PHP Monitor" in the subject.

View File

@ -1,22 +1,6 @@
### Q&A ### Quick Setup
#### Q: Does this support Apple Silicon? If you want to set up your computer for the very first time with PHP Monitor, here's how I do it:
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.
PHP Monitor itself doesn't do any network requests. Feel free to check the source code or intercept the traffic, if you don't believe me.
#### Q: How can I set this up on a fresh Mac?
If you want to set up your computer for the very first time, here's how I do it:
Install [Homebrew](https://brew.sh) first. Install [Homebrew](https://brew.sh) first.
@ -28,8 +12,14 @@ Install PHP, composer, add to path:
Make sure the following line is not in the comments: Make sure the following line is not in the comments:
# on an Intel Mac
export PATH=$HOME/bin:/usr/local/bin:$PATH export PATH=$HOME/bin:/usr/local/bin:$PATH
If you're on an Apple Silicon-based Mac, you'll need to add:
# on an M1 Mac
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
and add the following to your .zshrc: and add the following to your .zshrc:
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
@ -38,7 +28,7 @@ Make sure PHP is linked correctly:
which php which php
should return: `/usr/local/bin/php` should return: `/usr/local/bin/php` (or `/opt/homebrew/bin/php`)
composer global require laravel/valet composer global require laravel/valet
valet install valet install
@ -49,21 +39,44 @@ This should install `dnsmasq` and set up Valet. Great, almost there!
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work. Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
### FAQ
#### Q: Does this support Apple Silicon?
Yes. This is a universal app.
The following installation paths are supported:
* `/usr/local/homebrew` (default on Intel Macs)
* `/opt/homebrew` (default on Apple Silicon Macs)
#### Q: Is PHP 8.0 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.
PHP Monitor itself doesn't do any network requests. Feel free to check the source code or intercept the traffic, if you don't believe me.
#### Q: I want PHP Monitor to start up when I boot my Mac! #### Q: I want PHP Monitor to start up when I boot my Mac!
You can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account. You can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account.
Super convenient! Super convenient!
#### Q: PHP Monitor says that the latest version of PHP is not installed, but it is! ### Q: PHP Monitor says that the latest version of PHP is not installed, but it is!
Try installing again using `brew install php`. Try installing again using `brew install php`.
This should resolve the issue. This should resolve the issue! If that does not fix the issue, run `brew link php --force`. (Afterwards, you may need to restart your terminal to make sure the new linked version is detected.)
#### Q: PHP Monitor says the correct version is loaded, but my Valet sites don't work! ### 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`). Your sites aren't showing up, or you are seeing a 502? It's a common issue.
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? #### Q: PHP Monitor reports another version compared to phpinfo on my local website, what is going on?

View File

@ -7,7 +7,7 @@
5. Notarize and prepare for own distribution 5. Notarize and prepare for own distribution
6. After notarization, export .app 6. After notarization, export .app
7. Create zipped version 7. Create zipped version
8. Calculate SHA256: `openssl dgst -sha256 phpmon-2.x.zip` 8. Calculate SHA256: `openssl dgst -sha256 phpmon.zip`
9. Upload to GitHub 9. Upload to GitHub and add to tagged release
10. Update Cask 10. Update Cask with new version + hash
11. Check new version can be installed via Cask 11. Check new version can be installed via Cask

BIN
docs/notification.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -33,6 +33,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
*/ */
let menu : MainMenu let menu : MainMenu
/**
The paths singleton that determines where Homebrew is installed,
and where to look for binaries.
*/
let paths : Paths
// MARK: - Initializer // MARK: - Initializer
/** /**
@ -42,6 +48,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
self.sharedShell = Shell.user self.sharedShell = Shell.user
self.state = App.shared self.state = App.shared
self.menu = MainMenu.shared self.menu = MainMenu.shared
self.paths = Paths.shared
super.init() super.init()
} }

View File

@ -12,7 +12,7 @@ import AppKit
class Actions { class Actions {
public static func detectPhpVersions() -> [String] { public static func detectPhpVersions() -> [String] {
let files = Shell.user.pipe("ls /usr/local/opt | grep php@") let files = Shell.user.pipe("ls \(Paths.optPath()) | grep php@")
var versions = files.components(separatedBy: "\n") var versions = files.components(separatedBy: "\n")
// Remove all empty strings // Remove all empty strings
@ -40,49 +40,50 @@ class Actions {
public static func restartPhpFpm() { public static func restartPhpFpm() {
let version = App.shared.currentVersion!.short let version = App.shared.currentVersion!.short
if (version == App.shared.brewPhpVersion) { if (version == App.shared.brewPhpVersion) {
Shell.user.run("sudo brew services restart php") Shell.user.run("sudo \(Paths.brew()) services restart php")
} else { } else {
Shell.user.run("sudo brew services restart php@\(version)") Shell.user.run("sudo \(Paths.brew()) services restart php@\(version)")
} }
} }
public static func restartNginx() public static func restartNginx()
{ {
Shell.user.run("sudo brew services restart nginx") Shell.user.run("sudo \(Paths.brew()) services restart nginx")
} }
public static func restartDnsMasq()
{
Shell.user.run("sudo \(Paths.brew()) services restart dnsmasq")
}
/**
Switching to a new PHP version involves:
- unlinking the current version
- stopping the active services
- linking the new desired version
Please note that depending on which version is installed,
the version that is switched to may or may not be identical to `php` (without @version).
*/
public static func switchToPhpVersion(version: String, availableVersions: [String]) { public static func switchToPhpVersion(version: String, availableVersions: [String]) {
availableVersions.forEach { (version) in availableVersions.forEach { (available) in
// Unlink the current version let formula = (available == App.shared.brewPhpVersion) ? "php" : "php@\(available)"
Shell.user.run("brew unlink php@\(version)") Shell.user.run("\(Paths.brew()) unlink \(formula)")
// Stop the services Shell.user.run("sudo \(Paths.brew()) services stop \(formula)")
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(App.shared.brewPhpVersion)) {
// Use the latest version as a default
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 {
// Otherwise, link the correct php version + start the correct service
Shell.user.run("brew link php@\(version) --overwrite --force")
Shell.user.run("sudo brew services start php@\(version)")
}
} }
let formula = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)"
Shell.user.run("\(Paths.brew()) link \(formula) --overwrite --force")
Shell.user.run("sudo \(Paths.brew()) services start \(formula)")
} }
public static func openGenericPhpConfigFolder() { public static func openGenericPhpConfigFolder() {
let files = [NSURL(fileURLWithPath: "/usr/local/etc/php")]; let files = [NSURL(fileURLWithPath: "\(Paths.etcPath())/php")];
NSWorkspace.shared.activateFileViewerSelecting(files as [URL]) NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
} }
public static func openPhpConfigFolder(version: String) { public static func openPhpConfigFolder(version: String) {
let files = [NSURL(fileURLWithPath: "/usr/local/etc/php/\(version)/php.ini")]; let files = [NSURL(fileURLWithPath: "\(Paths.etcPath())/php/\(version)/php.ini")];
NSWorkspace.shared.activateFileViewerSelecting(files as [URL]) NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
} }
@ -93,7 +94,7 @@ class Actions {
public static func didFindXdebug(_ version: String) -> Bool { public static func didFindXdebug(_ version: String) -> Bool {
let command = """ let command = """
grep -q 'zend_extension="xdebug.so"' /usr/local/etc/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" grep -q 'zend_extension="xdebug.so"' \(Paths.etcPath())/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO"
""" """
let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines) let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines)
return (output == "YES") return (output == "YES")
@ -101,7 +102,7 @@ class Actions {
public static func didEnableXdebug(_ version: String) -> Bool { public static func didEnableXdebug(_ version: String) -> Bool {
let command = """ let command = """
grep -q '; zend_extension="xdebug.so"' /usr/local/etc/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO" grep -q '; zend_extension="xdebug.so"' \(Paths.etcPath())/php/\(version)/php.ini; [ $? -eq 0 ] && echo "YES" || echo "NO"
""" """
let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines) let output = Shell.user.pipe(command).trimmingCharacters(in: .whitespacesAndNewlines)
return (output == "NO") return (output == "NO")
@ -110,34 +111,40 @@ class Actions {
public static func toggleXdebug() { public static func toggleXdebug() {
let version = App.shared.currentVersion?.short let version = App.shared.currentVersion?.short
var command = """ var command = """
sed -i '' 's/; zend_extension="xdebug.so"/zend_extension="xdebug.so"/g' /usr/local/etc/php/\(version!)/php.ini sed -i '' 's/; zend_extension="xdebug.so"/zend_extension="xdebug.so"/g' \(Paths.etcPath())/php/\(version!)/php.ini
""" """
if (self.didEnableXdebug(version!)) { if (self.didEnableXdebug(version!)) {
command = """ command = """
sed -i '' 's/zend_extension="xdebug.so"/; zend_extension="xdebug.so"/g' /usr/local/etc/php/\(version!)/php.ini sed -i '' 's/zend_extension="xdebug.so"/; zend_extension="xdebug.so"/g' \(Paths.etcPath())/php/\(version!)/php.ini
""" """
} }
Shell.user.run(command) Shell.user.run(command)
} }
// unlink all the crap and link the latest version /**
// this also restarts all services Detects all currently available PHP versions, and unlinks each and every one of them.
After this, the brew services are also stopped, the latest PHP version is linked, and php + nginx are restarted.
If this does not solve the issue, the user may need to install additional extensions and/or run `composer global update`.
*/
public static func fixMyPhp() { public static func fixMyPhp() {
Shell.user.run("sudo \(Paths.brew()) services stop dnsmasq")
Shell.user.run("sudo \(Paths.brew()) services start dnsmasq")
let versions = self.detectPhpVersions() let versions = self.detectPhpVersions()
versions.forEach { (version) in versions.forEach { (version) in
Shell.user.run("brew unlink php@\(version)") Shell.user.run("\(Paths.brew()) unlink php@\(version)")
if (version == App.shared.brewPhpVersion) { if (version == App.shared.brewPhpVersion) {
Shell.user.run("brew services stop php") Shell.user.run("\(Paths.brew()) services stop php")
Shell.user.run("sudo brew services stop php") Shell.user.run("sudo \(Paths.brew()) services stop php")
} else { } else {
Shell.user.run("brew services stop php@\(version)") Shell.user.run("\(Paths.brew()) services stop php@\(version)")
Shell.user.run("sudo brew services stop php@\(version)") Shell.user.run("sudo \(Paths.brew()) services stop php@\(version)")
} }
} }
Shell.user.run("brew services stop php") Shell.user.run("\(Paths.brew()) services stop php")
Shell.user.run("brew services stop nginx") Shell.user.run("\(Paths.brew()) services stop nginx")
Shell.user.run("brew link php") Shell.user.run("\(Paths.brew()) link php")
Shell.user.run("sudo brew services restart php") Shell.user.run("sudo \(Paths.brew()) services restart dnsmasq")
Shell.user.run("sudo brew services restart nginx") Shell.user.run("sudo \(Paths.brew()) services restart php")
Shell.user.run("sudo \(Paths.brew()) services restart nginx")
} }
} }

View File

@ -25,50 +25,46 @@ class Startup {
self.failureCallback = failure self.failureCallback = failure
self.performEnvironmentCheck( self.performEnvironmentCheck(
!Shell.user.pipe("which php").contains("/usr/local/bin/php"), !Shell.fileExists("\(Paths.binPath())/php"),
messageText: "PHP is not correctly installed", messageText: "startup.errors.php_binary.title".localized,
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.)", informativeText: "startup.errors.php_binary_desc".localized,
breaking: true breaking: true
) )
self.performEnvironmentCheck( self.performEnvironmentCheck(
!Shell.user.pipe("ls /usr/local/opt | grep php").contains("php"), !Shell.user.pipe("ls \(Paths.optPath()) | grep php").contains("php"),
messageText: "PHP is not correctly installed", messageText: "startup.errors.php_opt.title".localized,
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.", informativeText: "startup.errors.php_opt.desc".localized,
breaking: true breaking: true
) )
self.performEnvironmentCheck( self.performEnvironmentCheck(
!Shell.user.pipe("which valet").contains("/usr/local/bin/valet"), !Shell.user.pipe("which valet").contains("/usr/local/bin/valet"),
messageText: "Laravel Valet is not correctly installed", messageText: "startup.errors.valet_executable.title".localized,
informativeText: "You must install Valet with composer. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet`. The app will not work correctly until you resolve this issue.", informativeText: "startup.errors.valet_executable.desc".localized,
breaking: true breaking: true
) )
self.performEnvironmentCheck( self.performEnvironmentCheck(
!Shell.user.pipe("cat /private/etc/sudoers.d/brew").contains("/usr/local/bin/brew"), !Shell.user.pipe("cat /private/etc/sudoers.d/brew").contains("\(Paths.binPath())/brew"),
messageText: "Brew has not been added to sudoers.d", messageText: "startup.errors.sudoers_brew.title".localized,
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.", informativeText: "startup.errors.sudoers_brew.desc".localized,
breaking: true breaking: true
) )
self.performEnvironmentCheck( self.performEnvironmentCheck(
!Shell.user.pipe("cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet"), !Shell.user.pipe("cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet"),
messageText: "Valet has not been added to sudoers.d", messageText: "startup.errors.sudoers_valet.title".localized,
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.", informativeText: "startup.errors.sudoers_valet.desc".localized,
breaking: true breaking: true
) )
let services = Shell.user.pipe("brew services list | grep php") let services = Shell.user.pipe("\(Paths.brew()) services list | grep php")
self.performEnvironmentCheck( self.performEnvironmentCheck(
(services.countInstances(of: "started") > 1), (services.countInstances(of: "started") > 1),
messageText: "Multiple PHP services are active", messageText: "startup.errors.services.title".localized,
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." + informativeText: "startup.errors.services.desc".localized,
"\n\nThe easiest solution is to choose the option 'Force load latest PHP version' in the menu bar." + breaking: false
"\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.",
breaking: false
) )
if (!self.failed) { if (!self.failed) {
@ -86,7 +82,7 @@ class Startup {
print("PHP Monitor has determined the application has successfully passed all checks.") print("PHP Monitor has determined the application has successfully passed all checks.")
print("Determining which version of PHP is aliased to `php` via Homebrew...") print("Determining which version of PHP is aliased to `php` via Homebrew...")
let brewPhpAlias = Shell.user.pipe("brew info php --json"); let brewPhpAlias = Shell.user.pipe("\(Paths.brew()) info php --json");
App.shared.brewPhpPackage = try! JSONDecoder().decode( App.shared.brewPhpPackage = try! JSONDecoder().decode(
[HomebrewPackage].self, [HomebrewPackage].self,
@ -99,7 +95,7 @@ class Startup {
/** /**
* Perform an environment check. Will cause the application to terminate, if `breaking` is set to true. * Perform an environment check. Will cause the application to terminate, if `breaking` is set to true.
* *
* - Parameter condition: Condition to check for * - Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown
* - Parameter messageText: Short description of what is wrong * - Parameter messageText: Short description of what is wrong
* - Parameter informativeText: Expanded description of the environment check that failed * - Parameter informativeText: Expanded description of the environment check that failed
* - Parameter breaking: If the application should terminate afterwards * - Parameter breaking: If the application should terminate afterwards

View File

@ -19,7 +19,10 @@ class PhpVersion {
var error : Bool = false var error : Bool = false
init() { init() {
let version = Command.execute(path: "/usr/local/bin/php", arguments: ["-r", "print phpversion();"]) let version = Command.execute(
path: Paths.php(),
arguments: ["-r", "print phpversion();"]
)
if (version == "" || version.contains("Warning")) { if (version == "" || version.contains("Warning")) {
self.short = "💩 BROKEN" self.short = "💩 BROKEN"

View File

@ -0,0 +1,15 @@
//
// PhpMenuItem.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 13/12/2020.
// Copyright © 2020 Nico Verbruggen. All rights reserved.
//
import Cocoa
class PhpMenuItem: NSMenuItem {
var version: String = ""
}

View File

@ -36,16 +36,22 @@ class StatusMenu : NSMenu {
let version = App.shared.availablePhpVersions[index] let version = App.shared.availablePhpVersions[index]
let action = #selector(MainMenu.switchToPhpVersion(sender:)) let action = #selector(MainMenu.switchToPhpVersion(sender:))
let brew = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)" let brew = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)"
let menuItem = NSMenuItem(title: "\("mi_php_switch".localized) \(version) (\(brew))", action: (version == App.shared.currentVersion?.short) ? nil : action, keyEquivalent: "\(shortcutKey)") let menuItem = PhpMenuItem(title: "\("mi_php_switch".localized) \(version) (\(brew))", action: (version == App.shared.currentVersion?.short) ? nil : action, keyEquivalent: "\(shortcutKey)")
menuItem.tag = index menuItem.version = version
shortcutKey = shortcutKey + 1 shortcutKey = shortcutKey + 1
self.addItem(menuItem) self.addItem(menuItem)
} }
self.addItem(NSMenuItem.separator()) self.addItem(NSMenuItem.separator())
self.addItem(NSMenuItem(title: "mi_active_services".localized, action: nil, keyEquivalent: "")) self.addItem(NSMenuItem(title: "mi_active_services".localized, action: nil, keyEquivalent: ""))
self.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "f")) self.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
self.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
self.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n")) self.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
self.addItem(NSMenuItem(title: "mi_force_load_latest".localized, action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "")) self.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s"))
self.addItem(NSMenuItem.separator())
self.addItem(NSMenuItem(title: "mi_diagnostics".localized, action: nil, keyEquivalent: ""))
self.addItem(NSMenuItem(title: "mi_force_load_latest".localized, action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: "f"))
} }
if (App.shared.busy) { if (App.shared.busy) {
self.addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: "")) self.addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: ""))

View File

@ -17,9 +17,12 @@
"mi_php_broken_3" = "You could also try switching to another version."; "mi_php_broken_3" = "You could also try switching to another version.";
"mi_php_broken_4" = "Running `brew reinstall php` (or for the equivalent version) might help."; "mi_php_broken_4" = "Running `brew reinstall php` (or for the equivalent version) might help.";
"mi_diagnostics" = "Diagnostics";
"mi_active_services" = "Active Services"; "mi_active_services" = "Active Services";
"mi_restart_php_fpm" = "Restart php-fpm service"; "mi_restart_php_fpm" = "Restart service: php";
"mi_restart_nginx" = "Restart nginx service"; "mi_restart_nginx" = "Restart service: nginx";
"mi_restart_dnsmasq" = "Restart service: dnsmasq";
"mi_restart_all_services" = "Restart all services";
"mi_force_load_latest" = "Force load latest PHP version"; "mi_force_load_latest" = "Force load latest PHP version";
"mi_configuration" = "Configuration"; "mi_configuration" = "Configuration";
@ -47,10 +50,36 @@
// Force Reload Done // Force Reload Done
"alert.force_reload_done.title" = "PHP has been force reloaded"; "alert.force_reload_done.title" = "PHP has been force reloaded";
"alert.force_reload_done.info" = "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."; "alert.force_reload_done.info" = "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. If visiting sites still does not work, you may try running `valet install` again, this can fix a 502 issue (Bad Gateway).";
// PHP Monitor Cannot Start // PHP Monitor Cannot Start
"alert.cannot_start.title" = "PHP Monitor cannot start"; "alert.cannot_start.title" = "PHP Monitor cannot start";
"alert.cannot_start.info" = "The issue you were just notified about is keeping PHP Monitor from functioning correctly. Please fix the issue and restart PHP Monitor. After clicking on OK, PHP Monitor will close.\n\nIf you have fixed the issue (or don't remember what the exact issue is) you can click on Retry, which will have PHP Monitor retry the startup checks."; "alert.cannot_start.info" = "The issue you were just notified about is keeping PHP Monitor from functioning correctly. Please fix the issue and restart PHP Monitor. After clicking on OK, PHP Monitor will close.\n\nIf you have fixed the issue (or don't remember what the exact issue is) you can click on Retry, which will have PHP Monitor retry the startup checks.";
"alert.cannot_start.close" = "Close"; "alert.cannot_start.close" = "Close";
"alert.cannot_start.retry" = "Retry"; "alert.cannot_start.retry" = "Retry";
// STARTUP
/// 1. PHP binary not found
"startup.errors.php_binary.title" = "PHP is not correctly installed";
"startup.errors.php_binary_desc" = "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php` (or `/opt/homebrew/bin/php`). The app will not work correctly until you resolve this issue. (Usually `brew link php` resolves this issue.)";
/// 2. PHP not found in /usr/local/opt or /opt/homebrew/opt
"startup.errors.php_opt.title" = "PHP is not correctly installed";
"startup.errors.php_opt.desc" = "PHP alias was not found in `/usr/local/opt` (or `/opt/homebrew/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.";
/// 3. Valet not installed
"startup.errors.valet_executable.title" = "Laravel Valet is not correctly installed";
"startup.errors.valet_executable.desc" = "You must install Valet with composer. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet`. The app will not work correctly until you resolve this issue.";
/// 4. Brew & sudoers
"startup.errors.sudoers_brew.title" = "Brew has not been added to sudoers.d";
"startup.errors.sudoers_brew.desc" = "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.";
/// 5. Valet & sudoers
"startup.errors.sudoers_valet.title" = "Valet has not been added to sudoers.d";
"startup.errors.sudoers_valet.desc" = "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.";
/// 6. Multiple services active
"startup.errors.services.title" = "Multiple PHP services are active";
"startup.errors.services.desc" = "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.";

View File

@ -186,12 +186,26 @@ class MainMenu: NSObject, NSWindowDelegate {
}) })
} }
@objc public func restartAllServices() {
self.waitAndExecute({
Actions.restartDnsMasq()
Actions.restartPhpFpm()
Actions.restartNginx()
})
}
@objc public func restartNginx() { @objc public func restartNginx() {
self.waitAndExecute({ self.waitAndExecute({
Actions.restartNginx() Actions.restartNginx()
}) })
} }
@objc public func restartDnsMasq() {
self.waitAndExecute({
Actions.restartDnsMasq()
})
}
@objc public func toggleXdebug() { @objc public func toggleXdebug() {
self.waitAndExecute({ self.waitAndExecute({
Actions.toggleXdebug() Actions.toggleXdebug()
@ -199,9 +213,12 @@ class MainMenu: NSObject, NSWindowDelegate {
} }
@objc public func openPhpInfo() { @objc public func openPhpInfo() {
try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8) self.waitAndExecute({
Shell.user.run("/usr/local/bin/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html") try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8)
NSWorkspace.shared.open(URL(string: "file:///private/tmp/phpmon_phpinfo.html")!) Shell.user.run("\(Paths.binPath())/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
}, {
NSWorkspace.shared.open(URL(string: "file:///private/tmp/phpmon_phpinfo.html")!)
})
} }
@objc public func forceRestartLatestPhp() { @objc public func forceRestartLatestPhp() {
@ -234,11 +251,9 @@ class MainMenu: NSObject, NSWindowDelegate {
Actions.openValetConfigFolder() Actions.openValetConfigFolder()
} }
@objc public func switchToPhpVersion(sender: AnyObject) { @objc public func switchToPhpVersion(sender: PhpMenuItem) {
print("Switching to: PHP \(sender.version)")
self.setBusyImage() self.setBusyImage()
// TODO: A wise man once said: using tags is not good. Fix this.
let index = sender.tag!
let version = App.shared.availablePhpVersions[index]
App.shared.busy = true App.shared.busy = true
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
// Update the PHP version in the status bar // Update the PHP version in the status bar
@ -247,7 +262,7 @@ class MainMenu: NSObject, NSWindowDelegate {
self.update() self.update()
// Switch the PHP version // Switch the PHP version
Actions.switchToPhpVersion( Actions.switchToPhpVersion(
version: version, version: sender.version,
availableVersions: App.shared.availablePhpVersions availableVersions: App.shared.availablePhpVersions
) )
// Mark as no longer busy // Mark as no longer busy
@ -258,8 +273,8 @@ class MainMenu: NSObject, NSWindowDelegate {
self.update() self.update()
// Send a notification that the switch has been completed // Send a notification that the switch has been completed
LocalNotification.send( LocalNotification.send(
title: String(format: "notification.version_changed_title".localized, version), title: String(format: "notification.version_changed_title".localized, sender.version),
subtitle: String(format: "notification.version_changed_desc".localized, version) subtitle: String(format: "notification.version_changed_desc".localized, sender.version)
) )
} }
} }

View File

@ -0,0 +1,66 @@
//
// Paths.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 01/01/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Foundation
enum HomebrewDir: String {
case opt = "/opt/homebrew"
case usr = "/usr/local"
}
class Paths {
static let shared = Paths()
var baseDir : HomebrewDir
init() {
let optBrewFound = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew")
let usrBrewFound = Shell.fileExists("\(HomebrewDir.usr.rawValue)/bin/brew")
if (optBrewFound) {
// This is usually the case with Homebrew installed on Apple Silicon
self.baseDir = .opt
} else if (usrBrewFound) {
// This is usually the case with Homebrew installed on Intel (or Rosetta 2)
self.baseDir = .usr
} else {
// Falling back to default "legacy" Homebrew location (for Intel)
print("Seems like we couldn't determine the Homebrew directory.")
print("This usually means we're in trouble... (no Homebrew?)")
self.baseDir = .usr
}
print("Homebrew directory: \(self.baseDir)")
}
// - MARK: Binaries
public static func brew() -> String {
return "\(self.binPath())/brew"
}
public static func php() -> String {
return "\(self.binPath())/php"
}
// - MARK: Paths
public static func binPath() -> String {
return "\(self.shared.baseDir.rawValue)/bin"
}
public static func optPath() -> String {
return "\(self.shared.baseDir.rawValue)/opt"
}
public static func etcPath() -> String {
return "\(self.shared.baseDir.rawValue)/etc"
}
}

View File

@ -34,20 +34,25 @@ class Shell {
*/ */
public func pipe(_ command: String, shell: String = "/bin/sh") -> String { public func pipe(_ command: String, shell: String = "/bin/sh") -> String {
let task = Process() let task = Process()
let pipe = Pipe()
task.launchPath = shell task.launchPath = shell
task.arguments = ["--login", "-c", command] task.arguments = ["--login", "-c", command]
let pipe = Pipe()
task.standardOutput = pipe task.standardOutput = pipe
task.launch() task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(
data: data,
encoding: String.Encoding.utf8.rawValue
)! as String
return output return String(
data: pipe.fileHandleForReading.readDataToEndOfFile(),
encoding: .utf8
)!
}
/**
Checks if a file exists at the provided path.
*/
public static func fileExists(_ path: String) -> Bool {
return Shell.user.pipe(
"if [ -f \(path) ]; then echo \"PHP_Y_FE\"; fi"
).contains("PHP_Y_FE")
} }
} }