mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
4147cc7b4b | |||
72b309a716 | |||
12c2716715 | |||
4d4019204b | |||
157033a3b3 | |||
717cddacdd | |||
f13ed5dd90 | |||
a8f823cd04 | |||
51fd22f595 | |||
bf6ebff3bf | |||
1887b19329 | |||
a53972404c | |||
64a605235a | |||
a194ecdebe | |||
03158a568c | |||
bdc6be7384 | |||
4908dba57e | |||
0f0aa176b6 | |||
485729f9a5 | |||
5eb36a9bdf | |||
86113d2067 | |||
70c04d4dc7 | |||
5d69b423c1 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ phpmon.xcodeproj/project.xcworkspace
|
||||
phpmon.xcodeproj/xcuserdata
|
||||
PHP Monitor.xcodeproj/project.xcworkspace
|
||||
PHP Monitor.xcodeproj/xcuserdata
|
||||
.DS_Store
|
@ -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 */; };
|
||||
@ -20,6 +21,7 @@
|
||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
|
||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.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 */; };
|
||||
@ -31,6 +33,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
||||
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@ -45,10 +48,13 @@
|
||||
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
||||
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
||||
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
|
||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotification.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>"; };
|
||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.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>"; };
|
||||
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
|
||||
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
|
||||
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
@ -78,6 +84,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
||||
C4E713562570150F00007428 /* SECURITY.md */,
|
||||
C4E713572570151400007428 /* docs */,
|
||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||
C41C1B3422B0097F00E7CF16 /* Products */,
|
||||
);
|
||||
@ -161,6 +169,8 @@
|
||||
C476FF9722B0DD830098105B /* Alert.swift */,
|
||||
C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */,
|
||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@ -201,7 +211,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1020;
|
||||
LastUpgradeCheck = 1110;
|
||||
LastUpgradeCheck = 1220;
|
||||
ORGANIZATIONNAME = "Nico Verbruggen";
|
||||
TargetAttributes = {
|
||||
C41C1B3222B0097F00E7CF16 = {
|
||||
@ -255,9 +265,11 @@
|
||||
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 */,
|
||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
|
||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
||||
@ -304,6 +316,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@ -365,6 +378,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@ -401,7 +415,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 26;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
@ -409,7 +423,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2;
|
||||
MARKETING_VERSION = 2.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -425,7 +439,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 24;
|
||||
CURRENT_PROJECT_VERSION = 26;
|
||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = phpmon/Info.plist;
|
||||
@ -433,7 +447,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 2.2;
|
||||
MARKETING_VERSION = 2.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1150"
|
||||
LastUpgradeVersion = "1220"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -39,6 +39,7 @@
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
44
README.md
44
README.md
@ -6,28 +6,32 @@ 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).
|
||||
|
||||
<img src="./docs/screenshot.png" width="278px" alt="phpmon screenshot"/>
|
||||
<img src="./docs/screenshot.png" width="370px" alt="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.
|
||||
|
||||
It's also super convenient to and switch between versions.
|
||||
It's also super convenient to switch between different versions of PHP, or to find your currently active .ini file!
|
||||
|
||||
## 🖥 System requirements
|
||||
|
||||
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
|
||||
|
||||
You can install via Homebrew, or may download the latest [release](https://github.com/nicoverbruggen/phpmon/releases).
|
||||
You can install via Homebrew, or may download the latest [release][1].
|
||||
|
||||
To install via Homebrew, run:
|
||||
|
||||
brew tap nicoverbruggen/homebrew-cask
|
||||
brew cask install phpmon
|
||||
brew tap nicoverbruggen/homebrew-cask
|
||||
brew cask install phpmon
|
||||
|
||||
_The app is signed and notarized, meaning all you have to do is approve its first launch._
|
||||
|
||||
## 👨💻 Why I built this
|
||||
|
||||
@ -47,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 in order 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
|
||||
@ -67,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`
|
||||
@ -80,10 +84,20 @@ 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](docs/ADDITIONAL.md) 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.
|
||||
|
||||
## ⭐️ Is this helpful?
|
||||
## ⭐️ Star me!
|
||||
|
||||
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.
|
||||
If this software has been useful to you, I ask that you **please 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](https://github.com/nicoverbruggen/phpmon/issues/new).
|
||||
|
||||
## 💵 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].
|
||||
|
||||
[1]: https://github.com/nicoverbruggen/phpmon/releases
|
||||
[2]: docs/ADDITIONAL.md
|
||||
[3]: https://paypal.me/nicoverbruggen
|
||||
|
15
SECURITY.md
Normal file
15
SECURITY.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
The following versions of PHP Monitor are supported:
|
||||
|
||||
| Version | Universal | Supported | Runs on macOS |
|
||||
| ------- | ------------- | ------------------ | ----- |
|
||||
| 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
|
||||
|
||||
Contact Nico Verbruggen at the email address used for the commits in the repository. Please include "PHP Monitor" in the subject.
|
@ -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).
|
||||
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).
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Release Procedure
|
||||
|
||||
1. Merge into `master`
|
||||
1. Merge into `main`
|
||||
2. Create tag
|
||||
3. Add changes to changelog
|
||||
4. Archive
|
||||
@ -10,4 +10,4 @@
|
||||
8. Calculate SHA256: `openssl dgst -sha256 phpmon-2.x.zip`
|
||||
9. Upload to GitHub
|
||||
10. Update Cask
|
||||
11. Check new version can be installed via Cask
|
||||
11. Check new version can be installed via Cask
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 615 KiB After Width: | Height: | Size: 270 KiB |
@ -7,18 +7,37 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import UserNotifications
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
|
||||
|
||||
// MARK: - Variables
|
||||
|
||||
/**
|
||||
The Shell singleton that keeps track of the history of all
|
||||
(invoked by PHP Monitor) shell commands. It is used to
|
||||
invoke all commands in this application.
|
||||
*/
|
||||
let sharedShell : Shell
|
||||
|
||||
/**
|
||||
The App singleton contains information about the state of
|
||||
the application and global variables.
|
||||
*/
|
||||
let state : App
|
||||
|
||||
/**
|
||||
The MainMenu singleton is responsible for rendering the
|
||||
menu bar item and its menu, as well as its actions.
|
||||
*/
|
||||
let menu : MainMenu
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
/**
|
||||
When the application initializes, create all singletons.
|
||||
*/
|
||||
override init() {
|
||||
self.sharedShell = Shell.user
|
||||
self.state = App.shared
|
||||
@ -27,13 +46,29 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
|
||||
/**
|
||||
When the application has finished launching, we'll want to set up
|
||||
the user notification center delegate, and kickoff the menu
|
||||
startup procedure.
|
||||
*/
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
NSUserNotificationCenter.default.delegate = self
|
||||
self.menu.startup()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
self.state.windowController = nil
|
||||
// MARK: - NSUserNotificationCenterDelegate
|
||||
|
||||
/**
|
||||
When a notification is sent, the delegate of the notification center
|
||||
is asked whether the notification should be presented or not. Since
|
||||
the user can now disable notifications per application since macOS
|
||||
Catalina, any and all notifications should be displayed.
|
||||
*/
|
||||
func userNotificationCenter(
|
||||
_ center: NSUserNotificationCenter,
|
||||
shouldPresent notification: NSUserNotification
|
||||
) -> Bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
*
|
||||
|
19
phpmon/Classes/Helpers/HomebrewPackage.swift
Normal file
19
phpmon/Classes/Helpers/HomebrewPackage.swift
Normal file
@ -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: "")
|
||||
}
|
||||
}
|
19
phpmon/Classes/Helpers/LocalNotification.swift
Normal file
19
phpmon/Classes/Helpers/LocalNotification.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// LocalNotification.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 16/07/2020.
|
||||
// Copyright © 2020 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class LocalNotification {
|
||||
public static func send(title: String, subtitle: String)
|
||||
{
|
||||
let notification = NSUserNotification()
|
||||
notification.title = title
|
||||
notification.subtitle = subtitle
|
||||
NSUserNotificationCenter.default.deliver(notification)
|
||||
}
|
||||
}
|
@ -12,17 +12,18 @@ class StatusMenu : NSMenu {
|
||||
|
||||
public func addPhpVersionMenuItems()
|
||||
{
|
||||
var string = "We are not sure what version of PHP you are running."
|
||||
var string = "mi_unsure".localized
|
||||
if (App.shared.currentVersion != nil) {
|
||||
if (!App.shared.currentVersion!.error) {
|
||||
string = "You are running PHP \(App.shared.currentVersion!.long)"
|
||||
// in case the php version loaded without issue
|
||||
string = "\("mi_php_version".localized) \(App.shared.currentVersion!.long)"
|
||||
self.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: ""))
|
||||
} else {
|
||||
// in case of an error show the error message
|
||||
self.addItem(NSMenuItem(title: "Oof! It appears your PHP installation is broken...", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "Try running `php -v` in your terminal.", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "You could also try switching to another version.", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "Running `brew reinstall php` (or for the equivalent version) might help.", action: nil, keyEquivalent: ""))
|
||||
["mi_php_broken_1", "mi_php_broken_2",
|
||||
"mi_php_broken_3", "mi_php_broken_4"].forEach { (message) in
|
||||
self.addItem(NSMenuItem(title: message.localized, action: nil, keyEquivalent: ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -34,30 +35,32 @@ class StatusMenu : NSMenu {
|
||||
for index in (0..<App.shared.availablePhpVersions.count).reversed() {
|
||||
let version = App.shared.availablePhpVersions[index]
|
||||
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
||||
let menuItem = NSMenuItem(title: "Switch to PHP \(version)", action: (version == App.shared.currentVersion?.short) ? nil : action, keyEquivalent: "\(shortcutKey)")
|
||||
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)")
|
||||
menuItem.tag = index
|
||||
shortcutKey = shortcutKey + 1
|
||||
self.addItem(menuItem)
|
||||
}
|
||||
self.addItem(NSMenuItem.separator())
|
||||
self.addItem(NSMenuItem(title: "Active Services", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "Restart php-fpm service", action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "f"))
|
||||
self.addItem(NSMenuItem(title: "Restart nginx service", action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||
self.addItem(NSMenuItem(title: "Force load latest PHP version", action: #selector(MainMenu.forceRestartLatestPhp), 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_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||
self.addItem(NSMenuItem(title: "mi_force_load_latest".localized, action: #selector(MainMenu.forceRestartLatestPhp), keyEquivalent: ""))
|
||||
}
|
||||
if (App.shared.busy) {
|
||||
self.addItem(NSMenuItem(title: "PHP Monitor is busy...", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: ""))
|
||||
}
|
||||
}
|
||||
|
||||
public func addPhpConfigurationMenuItems()
|
||||
{
|
||||
if (App.shared.currentVersion != nil) {
|
||||
self.addItem(NSMenuItem(title: "Configuration", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "Valet configuration (.config/valet)", action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
|
||||
self.addItem(NSMenuItem(title: "PHP configuration file (php.ini)", action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c"))
|
||||
self.addItem(NSMenuItem(title: "mi_configuration".localized, action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "mi_valet_config".localized, action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
|
||||
self.addItem(NSMenuItem(title: "mi_php_config".localized, action: #selector(MainMenu.openActiveConfigFolder), keyEquivalent: "c"))
|
||||
self.addItem(NSMenuItem(title: "mi_phpinfo".localized, action: #selector(MainMenu.openPhpInfo), keyEquivalent: "i"))
|
||||
self.addItem(NSMenuItem.separator())
|
||||
self.addItem(NSMenuItem(title: "Enabled Extensions", action: nil, keyEquivalent: ""))
|
||||
self.addItem(NSMenuItem(title: "mi_enabled_extensions".localized, action: nil, keyEquivalent: ""))
|
||||
self.addXdebugMenuItem()
|
||||
}
|
||||
}
|
||||
@ -68,7 +71,7 @@ class StatusMenu : NSMenu {
|
||||
if (xdebugFound) {
|
||||
let xdebugOn = App.shared.currentVersion!.xdebugEnabled
|
||||
let xdebugToggleMenuItem = NSMenuItem(
|
||||
title: "Xdebug",
|
||||
title: "mi_xdebug".localized,
|
||||
action: #selector(MainMenu.toggleXdebug), keyEquivalent: "x"
|
||||
)
|
||||
if (xdebugOn) {
|
||||
@ -77,7 +80,7 @@ class StatusMenu : NSMenu {
|
||||
self.addItem(xdebugToggleMenuItem)
|
||||
} else {
|
||||
let disabledItem = NSMenuItem(
|
||||
title: "xdebug.so missing",
|
||||
title: "mi_xdebug_missing".localized,
|
||||
action: nil, keyEquivalent: "x"
|
||||
)
|
||||
disabledItem.isEnabled = false
|
||||
|
@ -12,15 +12,17 @@ class Constants {
|
||||
|
||||
/**
|
||||
* The PHP versions supported by this application.
|
||||
* Versions that do not appear in this array are omitted from the list.
|
||||
*/
|
||||
static let SupportedPhpVersions = [
|
||||
"5.6", "7.0", "7.1", "7.2", "7.3", "7.4"
|
||||
"5.6",
|
||||
"7.0",
|
||||
"7.1",
|
||||
"7.2",
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0",
|
||||
"8.1"
|
||||
]
|
||||
|
||||
/**
|
||||
Which php version is aliased as `php` to brew?
|
||||
This is usually the latest PHP version.
|
||||
*/
|
||||
static let LatestPhpVersion = "7.4"
|
||||
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
<key>Relevance</key>
|
||||
<string>Essential</string>
|
||||
<key>Purpose</key>
|
||||
<string>PHP Monitor directly invokes Homebrew which contacts GitHub.</string>
|
||||
<string>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.</string>
|
||||
<key>DenyConsequences</key>
|
||||
<string>If you deny these connections, PHP Monitor might not be able to complete its preset set of instructions, causing version switching to fail.</string>
|
||||
</dict>
|
||||
|
@ -6,6 +6,39 @@
|
||||
Copyright © 2020 Nico Verbruggen. All rights reserved.
|
||||
*/
|
||||
|
||||
// MENU ITEMS (MI)
|
||||
|
||||
"mi_busy" = "PHP Monitor is busy...";
|
||||
"mi_unsure" = "We are not sure what version of PHP you are running.";
|
||||
"mi_php_version" = "You are running PHP";
|
||||
"mi_php_switch" = "Switch to PHP";
|
||||
"mi_php_broken_1" = "Oof! It appears your PHP installation is broken...";
|
||||
"mi_php_broken_2" = "Try running `php -v` in your terminal.";
|
||||
"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_active_services" = "Active Services";
|
||||
"mi_restart_php_fpm" = "Restart php-fpm service";
|
||||
"mi_restart_nginx" = "Restart nginx service";
|
||||
"mi_force_load_latest" = "Force load latest PHP version";
|
||||
|
||||
"mi_configuration" = "Configuration";
|
||||
"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_quit" = "Quit PHP Monitor";
|
||||
"mi_about" = "About PHP Monitor";
|
||||
|
||||
// NOTIFICATIONS
|
||||
|
||||
"notification.version_changed_title" = "PHP %@ now active";
|
||||
"notification.version_changed_desc" = "PHP Monitor has finished the switch to PHP %@.";
|
||||
|
||||
// ALERTS
|
||||
|
||||
// Force Reload Started
|
||||
@ -15,3 +48,9 @@
|
||||
// Force Reload Done
|
||||
"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.";
|
||||
|
||||
// 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.close" = "Close";
|
||||
"alert.cannot_start.retry" = "Retry";
|
||||
|
@ -33,8 +33,23 @@ class App {
|
||||
var timer: Timer?
|
||||
|
||||
/**
|
||||
The window controller that will show the log.
|
||||
Information we were able to discern from the Homebrew info command (as JSON).
|
||||
*/
|
||||
var windowController: NSWindowController? = nil
|
||||
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"
|
||||
|
||||
}
|
||||
|
@ -9,8 +9,13 @@
|
||||
import Cocoa
|
||||
|
||||
class Command {
|
||||
|
||||
/// Immediately executes a command.
|
||||
|
||||
/**
|
||||
Immediately executes a command.
|
||||
|
||||
- Parameter path: The path of the command or program to invoke.
|
||||
- Parameter arguments: A list of arguments that are passed on.
|
||||
*/
|
||||
public static func execute(path: String, arguments: [String]) -> String {
|
||||
let task = Process()
|
||||
task.launchPath = path
|
||||
|
@ -12,10 +12,18 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
|
||||
static let shared = MainMenu()
|
||||
|
||||
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
||||
/**
|
||||
The status bar item with variable length.
|
||||
*/
|
||||
let statusItem = NSStatusBar.system.statusItem(
|
||||
withLength: NSStatusItem.variableLength
|
||||
)
|
||||
|
||||
// MARK: - UI related
|
||||
|
||||
/**
|
||||
Kick off the startup of the rendering of the main menu.
|
||||
*/
|
||||
public func startup() {
|
||||
// Start with the icon
|
||||
self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||
@ -29,6 +37,9 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
When the environment is all clear and the app can run, let's go.
|
||||
*/
|
||||
private func onEnvironmentPass() {
|
||||
App.shared.availablePhpVersions = Actions.detectPhpVersions()
|
||||
self.updatePhpVersionInStatusBar()
|
||||
@ -44,13 +55,16 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
When the environment is not OK, present an alert to inform the user.
|
||||
*/
|
||||
private func onEnvironmentFail() {
|
||||
DispatchQueue.main.async {
|
||||
let close = Alert.present(
|
||||
messageText: "PHP Monitor cannot start",
|
||||
informativeText: "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.",
|
||||
buttonTitle: "Close",
|
||||
secondButtonTitle: "Retry"
|
||||
messageText: "alert.cannot_start.title".localized,
|
||||
informativeText: "alert.cannot_start.info".localized,
|
||||
buttonTitle: "alert.cannot_start.close".localized,
|
||||
secondButtonTitle: "alert.cannot_start.retry".localized
|
||||
)
|
||||
if (!close) {
|
||||
self.startup()
|
||||
@ -60,6 +74,9 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Update the menu's contents, based on what's going on.
|
||||
*/
|
||||
public func update() {
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
// Create a new menu
|
||||
@ -78,8 +95,8 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
|
||||
// Add about & quit menu items
|
||||
menu.addItem(NSMenuItem(title: "About PHP Monitor", action: #selector(self.openAbout), keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem(title: "Quit PHP Monitor", action: #selector(self.terminateApp), keyEquivalent: "q"))
|
||||
menu.addItem(NSMenuItem(title: "mi_about".localized, action: #selector(self.openAbout), keyEquivalent: ""))
|
||||
menu.addItem(NSMenuItem(title: "mi_quit".localized, action: #selector(self.terminateApp), keyEquivalent: "q"))
|
||||
|
||||
// Make sure every item can be interacted with
|
||||
menu.items.forEach({ (item) in
|
||||
@ -93,10 +110,19 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the status bar image based on a version string.
|
||||
*/
|
||||
func setStatusBarImage(version: String) {
|
||||
self.setStatusBar(image: MenuBarImageGenerator.textToImage(text: version))
|
||||
self.setStatusBar(
|
||||
image: MenuBarImageGenerator.textToImage(text: version)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the status bar image, based on the provided NSImage.
|
||||
The image will be used as a template image.
|
||||
*/
|
||||
func setStatusBar(image: NSImage) {
|
||||
if let button = statusItem.button {
|
||||
image.isTemplate = true
|
||||
@ -106,6 +132,14 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
|
||||
// MARK: - Nicer callbacks
|
||||
|
||||
/**
|
||||
Executes a specific callback and fires the completion callback,
|
||||
while updating the UI as required. As long as the completion callback
|
||||
does not fire, the app is presumed to be busy and the UI reflects this.
|
||||
|
||||
- Parameter execute: Escaping callback of the work that needs to happen.
|
||||
- Parameter completion: Callback that is fired when the work is done.
|
||||
*/
|
||||
private func waitAndExecute(_ execute: @escaping () -> Void, _ completion: @escaping () -> Void = {})
|
||||
{
|
||||
App.shared.busy = true
|
||||
@ -122,7 +156,7 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
// MARK: - User Interface
|
||||
|
||||
@objc func updatePhpVersionInStatusBar() {
|
||||
App.shared.currentVersion = PhpVersion()
|
||||
@ -144,6 +178,8 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc public func restartPhpFpm() {
|
||||
self.waitAndExecute({
|
||||
Actions.restartPhpFpm()
|
||||
@ -162,11 +198,19 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
})
|
||||
}
|
||||
|
||||
@objc public func openPhpInfo() {
|
||||
try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8)
|
||||
Shell.user.run("/usr/local/bin/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() {
|
||||
// Tell the user the switch is about to occur
|
||||
_ = Alert.present(
|
||||
messageText: "alert.force_reload.title".localized,
|
||||
informativeText: "alert.force_reload.info".localized
|
||||
)
|
||||
// Start switching
|
||||
self.waitAndExecute({ Actions.fixMyPhp() }, {
|
||||
_ = Alert.present(
|
||||
messageText: "alert.force_reload_done.title".localized,
|
||||
@ -192,6 +236,7 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
|
||||
@objc public func switchToPhpVersion(sender: AnyObject) {
|
||||
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
|
||||
@ -211,6 +256,11 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
DispatchQueue.main.async {
|
||||
self.updatePhpVersionInStatusBar()
|
||||
self.update()
|
||||
// Send a notification that the switch has been completed
|
||||
LocalNotification.send(
|
||||
title: String(format: "notification.version_changed_title".localized, version),
|
||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,11 +273,4 @@ class MainMenu: NSObject, NSWindowDelegate {
|
||||
@objc public func terminateApp() {
|
||||
NSApplication.shared.terminate(nil)
|
||||
}
|
||||
|
||||
// MARK: - Cleanup when window closes
|
||||
|
||||
func windowWillClose(_ notification: Notification) {
|
||||
App.shared.windowController = nil
|
||||
Shell.user.delegate = nil
|
||||
}
|
||||
}
|
||||
|
@ -8,38 +8,30 @@
|
||||
|
||||
import Cocoa
|
||||
|
||||
protocol ShellDelegate: class {
|
||||
func didCompleteCommand(historyItem: ShellHistoryItem)
|
||||
}
|
||||
|
||||
class ShellHistoryItem {
|
||||
var command: String
|
||||
var output: String
|
||||
var date: Date
|
||||
|
||||
init(command: String, output: String) {
|
||||
self.command = command
|
||||
self.output = output
|
||||
self.date = Date()
|
||||
}
|
||||
}
|
||||
|
||||
class Shell {
|
||||
|
||||
// Singleton to access a user shell (with --login)
|
||||
/**
|
||||
Singleton to access a user shell (with --login)
|
||||
*/
|
||||
static let user = Shell()
|
||||
|
||||
var history : [ShellHistoryItem] = []
|
||||
|
||||
var delegate : ShellDelegate?
|
||||
|
||||
/// Runs a shell command without using the description.
|
||||
/**
|
||||
Runs a shell command without using the output.
|
||||
Uses the default shell.
|
||||
|
||||
- Parameter command: The command to run
|
||||
*/
|
||||
public func run(_ command: String) {
|
||||
// Equivalent of piping to /dev/null; don't do anything with the string
|
||||
_ = self.pipe(command)
|
||||
}
|
||||
|
||||
/// Runs a shell command and returns the output.
|
||||
/**
|
||||
Runs a shell command and returns the output.
|
||||
|
||||
- Parameter command: The command to run
|
||||
- Parameter shell: Path to the shell to invoke
|
||||
*/
|
||||
public func pipe(_ command: String, shell: String = "/bin/sh") -> String {
|
||||
let task = Process()
|
||||
task.launchPath = shell
|
||||
@ -51,17 +43,10 @@ class Shell {
|
||||
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
|
||||
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
|
||||
|
||||
let historyItem = ShellHistoryItem(command: command, output: output)
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
self.history.append(historyItem)
|
||||
// Keep the last 100 items
|
||||
self.history = self.history.suffix(100)
|
||||
}
|
||||
|
||||
delegate?.didCompleteCommand(historyItem: historyItem)
|
||||
let output: String = NSString(
|
||||
data: data,
|
||||
encoding: String.Encoding.utf8.rawValue
|
||||
)! as String
|
||||
|
||||
return output
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Application-->
|
||||
@ -55,68 +54,5 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-343" y="-16"/>
|
||||
</scene>
|
||||
<!--Log View Controller-->
|
||||
<scene sceneID="hIz-AP-VOD">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="logWindow" id="XfG-lQ-9wD" customClass="LogViewController" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" identifier="main" id="m2S-Jp-Qdl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="662" height="475"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ICa-gx-jgq">
|
||||
<rect key="frame" x="578" y="8" width="75" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="3md-FI-EWa">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressed:" target="XfG-lQ-9wD" id="fIC-Bz-vTK"/>
|
||||
</connections>
|
||||
</button>
|
||||
<scrollView borderType="line" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vZy-5S-021">
|
||||
<rect key="frame" x="15" y="46" width="632" height="414"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="s5L-AU-0fw">
|
||||
<rect key="frame" x="1" y="1" width="630" height="412"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textView importsGraphics="NO" richText="NO" verticallyResizable="YES" smartInsertDelete="YES" id="tN6-Y9-1pA">
|
||||
<rect key="frame" x="0.0" y="0.0" width="630" height="412"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<size key="minSize" width="630" height="412"/>
|
||||
<size key="maxSize" width="640" height="10000000"/>
|
||||
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="Kho-JF-NZJ">
|
||||
<rect key="frame" x="-100" y="-100" width="240" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="qp7-7R-gTO">
|
||||
<rect key="frame" x="615" y="1" width="16" height="412"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vZy-5S-021" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" constant="15" id="K0k-oE-r37"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ICa-gx-jgq" secondAttribute="trailing" constant="15" id="LFS-0E-Ibw"/>
|
||||
<constraint firstItem="vZy-5S-021" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="15" id="Nec-oI-CjE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vZy-5S-021" secondAttribute="trailing" constant="15" id="kBJ-O5-eYI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ICa-gx-jgq" secondAttribute="bottom" constant="15" id="kYB-Fn-DSA"/>
|
||||
<constraint firstItem="ICa-gx-jgq" firstAttribute="top" secondItem="vZy-5S-021" secondAttribute="bottom" constant="10" id="xdn-yU-LVb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="textView" destination="tN6-Y9-1pA" id="z77-me-Od6"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-105" y="377.5"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
Reference in New Issue
Block a user