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

Compare commits

...

10 Commits
v1.0 ... v1.2

Author SHA1 Message Date
1bfcdd546b 🐛 Fixes #3: Bad Gateway caused by valet use
Note: The terminal output work-in-progress is currently disabled in this
particular commit.
2019-07-08 10:43:56 +02:00
78702ae325 🚧 Adds window to view terminal output 2019-07-08 08:40:29 +02:00
22c173c2c6 📝 Adds screenshot to README 2019-07-03 19:22:57 +02:00
593ac60cae 📝 Update README 2019-07-03 19:16:18 +02:00
cea95f4b64 Code cleanup 2019-07-03 19:13:02 +02:00
9427b206b4 Project structure changes, version change 2019-07-03 19:07:04 +02:00
ff6ca2b79d 🚧 WIP: Boot check changes 2019-07-01 13:58:56 +02:00
8de76dc95a 🚨 Cleanup 2019-07-01 13:46:02 +02:00
5b319d2691 🔧 Tweaked order of versions
The latest version is now visible at the top.
2019-07-01 13:31:14 +02:00
76390a687f 📝 Updated README 2019-07-01 13:30:13 +02:00
16 changed files with 416 additions and 216 deletions

View File

@ -1,3 +1,30 @@
# phpmon
phpmon is a macOS utility that runs on your Mac and displays the active PHP version in your status bar. Handy if you're running multiple versions of PHP with Homebrew and wish to see which version is currently linked.
<img src="./docs/phpmon.png" width="306px" alt="phpmon screenshot"/>
phpmon is a macOS utility that runs on your Mac and displays the active PHP version in your status bar. Handy if you're running multiple versions of PHP with Homebrew and wish to see which version is currently linked & active with Laravel Valet.
## How it works
### Version detection
This utility runs `php -r 'print phpversion();'` in the background periodically and extracts the version number.
### Switching PHP versions
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
This means:
- You have at least the latest version of PHP installed (`php@7.3`)
- 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.3 (this is done in order to ensure that Valet works, even when attempting to use PHP 5.6)
- Tell Valet to switch to a specific PHP version
- Link the desired version of PHP
If you want to know more about how this works, you can find the file Services.swift under the Helpers directory to learn more about how switching PHP versions works. In the end, this just executes some shell commands.

BIN
assets.sketch Normal file

Binary file not shown.

BIN
docs/phpmon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

View File

@ -8,31 +8,33 @@
/* Begin PBXBuildFile section */
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
C41C1B3922B0097F00E7CF16 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3822B0097F00E7CF16 /* ViewController.swift */; };
C41C1B3922B0097F00E7CF16 /* LogViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3822B0097F00E7CF16 /* LogViewController.swift */; };
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; };
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; };
C41C1B4922B00A9800E7CF16 /* ImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */; };
C41C1B4B22B019FF00E7CF16 /* PHPVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */; };
C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */; };
C41C1B4D22B0215A00E7CF16 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Services.swift */; };
C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
C4D8016622B1584700C6DA1B /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Environment.swift */; };
C4D8016622B1584700C6DA1B /* BootChecks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* BootChecks.swift */; };
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
C41C1B3322B0097F00E7CF16 /* phpmon.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = phpmon.app; sourceTree = BUILT_PRODUCTS_DIR; };
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C41C1B3822B0097F00E7CF16 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
C41C1B3822B0097F00E7CF16 /* LogViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewController.swift; sourceTree = "<group>"; };
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C41C1B3D22B0098000E7CF16 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
C41C1B3F22B0098000E7CF16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
C41C1B4622B009A400E7CF16 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGenerator.swift; sourceTree = "<group>"; };
C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPVersion.swift; sourceTree = "<group>"; };
C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersion.swift; sourceTree = "<group>"; };
C41C1B4C22B0215A00E7CF16 /* Services.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = "<group>"; };
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
C4D8016522B1584700C6DA1B /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = "<group>"; };
C4D8016522B1584700C6DA1B /* BootChecks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootChecks.swift; sourceTree = "<group>"; };
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -65,13 +67,16 @@
C41C1B3522B0097F00E7CF16 /* phpmon */ = {
isa = PBXGroup;
children = (
C4EE188322D3386B00E126E5 /* Constants.swift */,
C41E181722CB61EB0072CF09 /* Classes */,
C41E181622CB61890072CF09 /* Startup */,
C41E181522CB614C0072CF09 /* Terminal */,
C41C1B4E22B024F100E7CF16 /* Helpers */,
C41E181822CB62200072CF09 /* View Controllers */,
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */,
C41C1B3822B0097F00E7CF16 /* ViewController.swift */,
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */,
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
C41C1B3F22B0098000E7CF16 /* Info.plist */,
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */,
C41C1B4E22B024F100E7CF16 /* Helpers */,
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */,
);
path = phpmon;
sourceTree = "<group>";
@ -79,16 +84,46 @@
C41C1B4E22B024F100E7CF16 /* Helpers */ = {
isa = PBXGroup;
children = (
C41C1B4622B009A400E7CF16 /* Shell.swift */,
C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */,
C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */,
C41C1B4C22B0215A00E7CF16 /* Services.swift */,
C476FF9722B0DD830098105B /* Alert.swift */,
C4D8016522B1584700C6DA1B /* Environment.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
C41E181522CB614C0072CF09 /* Terminal */ = {
isa = PBXGroup;
children = (
C41C1B4622B009A400E7CF16 /* Shell.swift */,
C41C1B4C22B0215A00E7CF16 /* Services.swift */,
);
path = Terminal;
sourceTree = "<group>";
};
C41E181622CB61890072CF09 /* Startup */ = {
isa = PBXGroup;
children = (
C4D8016522B1584700C6DA1B /* BootChecks.swift */,
);
path = Startup;
sourceTree = "<group>";
};
C41E181722CB61EB0072CF09 /* Classes */ = {
isa = PBXGroup;
children = (
C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */,
);
path = Classes;
sourceTree = "<group>";
};
C41E181822CB62200072CF09 /* View Controllers */ = {
isa = PBXGroup;
children = (
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
C41C1B3822B0097F00E7CF16 /* LogViewController.swift */,
);
path = "View Controllers";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -159,14 +194,15 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C4D8016622B1584700C6DA1B /* Environment.swift in Sources */,
C4D8016622B1584700C6DA1B /* BootChecks.swift in Sources */,
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
C41C1B4D22B0215A00E7CF16 /* Services.swift in Sources */,
C41C1B4922B00A9800E7CF16 /* ImageGenerator.swift in Sources */,
C41C1B3922B0097F00E7CF16 /* ViewController.swift in Sources */,
C41C1B3922B0097F00E7CF16 /* LogViewController.swift in Sources */,
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
C41C1B4B22B019FF00E7CF16 /* PHPVersion.swift in Sources */,
C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */,
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -11,21 +11,24 @@ import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(
withLength: 32
)
// MARK: - Variables
let statusItem = NSStatusBar.system.statusItem(withLength: 32)
var timer: Timer?
var version: PHPVersion? = nil
var version: PhpVersion? = nil
var availablePhpVersions : [String] = []
var busy: Bool = false
var log: String = ""
var windowController: NSWindowController? = nil
// MARK: - Lifecycle
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Start with the icon
self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
// Perform environment boot checks
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
Environment.performBootChecks()
BootChecks.perform()
self.availablePhpVersions = Services.detectPhpVersions()
print("The following PHP versions were detected:")
print(self.availablePhpVersions)
@ -41,6 +44,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
// MARK: - UI related
func setStatusBarImage(version: String) {
self.setStatusBar(image: ImageGenerator.generateImageForStatusBar(width: 32.0, text: version))
}
@ -52,12 +61,63 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
func updateMenu() {
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
let menu = NSMenu()
var string = "We are not sure what version of PHP you are running."
if (self.version != nil) {
string = "You are running PHP \(self.version!.long)"
}
menu.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: ""))
if (self.version != nil) {
// Actions
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Open php.ini in Finder", action: #selector(self.openActiveConfigFolder), keyEquivalent: ""))
// OPTIONAL
// menu.addItem(NSMenuItem(title: "Restart PHP \(self.version!.short) service", action: #selector(self.restartPhp), keyEquivalent: ""))
}
menu.addItem(NSMenuItem.separator())
if (self.availablePhpVersions.count > 0 && !self.busy) {
var shortcutKey = 1
for index in (0..<self.availablePhpVersions.count).reversed() {
let version = self.availablePhpVersions[index]
let action = #selector(self.switchToPhpVersion(sender:))
let menuItem = NSMenuItem(title: "Switch to PHP \(version)", action: (version == self.version?.short) ? nil : action, keyEquivalent: "\(shortcutKey)")
menuItem.tag = index
shortcutKey = shortcutKey + 1
menu.addItem(menuItem)
}
menu.addItem(NSMenuItem.separator())
}
if (self.busy) {
menu.addItem(NSMenuItem(title: "Switching PHP versions...", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
}
// TODO: Enable when implementation is complete
// menu.addItem(NSMenuItem(title: "View terminal output", action: #selector(self.openOutput), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "About phpmon", action: #selector(self.openAbout), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Quit phpmon", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
DispatchQueue.main.async {
self.statusItem.menu = menu
}
}
}
// MARK: - Callable via Obj-C (#selector)
@objc func openOutput() {
if (self.windowController == nil) {
let vc = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "logWindow") as! LogViewController
let window = NSWindow(contentViewController: vc)
window.title = "Terminal Output"
self.windowController = NSWindowController(window: window)
}
self.windowController!.showWindow(self)
// TODO: Send window to front (if possible)
}
@objc func updatePhpVersionInStatusBar() {
self.version = PHPVersion()
self.version = PhpVersion()
if (self.busy) {
DispatchQueue.main.async {
self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
@ -70,42 +130,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.updateMenu()
}
func updateMenu() {
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
let menu = NSMenu()
var string = "We are not sure what version of PHP you are running."
if (self.version != nil) {
string = "You are running PHP \(self.version!.long)"
}
menu.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
if (self.availablePhpVersions.count > 0 && !self.busy) {
for index in (0..<self.availablePhpVersions.count) {
let version = self.availablePhpVersions[index]
let action = #selector(self.switchToPhpVersion(sender:))
let menuItem = NSMenuItem(title: "Switch to PHP \(version)", action: (version == self.version?.short) ? nil : action, keyEquivalent: "\(index + 1)")
menuItem.tag = index
menu.addItem(menuItem)
}
menu.addItem(NSMenuItem.separator())
}
if (self.busy) {
menu.addItem(NSMenuItem(title: "Switching PHP versions...", action: nil, keyEquivalent: ""))
menu.addItem(NSMenuItem.separator())
}
menu.addItem(NSMenuItem(title: "About phpmon", action: #selector(self.openAbout), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Quit phpmon", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
DispatchQueue.main.async {
self.statusItem.menu = menu
}
}
}
@objc public func openAbout() {
NSApplication.shared.activate(ignoringOtherApps: true)
NSApplication.shared.orderFrontStandardAboutPanel()
}
@objc public func openActiveConfigFolder() {
Services.openPhpConfigFolder(version: self.version!.short)
}
@objc public func restartPhp() {
Services.restartPhp(version: self.version!.short)
}
@objc public func switchToPhpVersion(sender: AnyObject) {
self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
let index = sender.tag!

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="phpmon" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="phpmon" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About phpmon" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Quit phpmon" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="phpmon Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="phpmon" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="0.0"/>
</scene>
<!--View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="phpmon" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" identifier="main" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="425" height="264"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="74.5" y="217"/>
</scene>
</scenes>
</document>

View File

@ -8,21 +8,23 @@
import Foundation
class PHPVersion {
class PhpVersion {
var short : String = "???"
var long : String = "???"
init() {
// Get the info about the PHP installation
let output = Shell.execute(command: "php -v")
// Get everything before "(cli)" (PHP X.X.X (cli) ...)
var version = output.components(separatedBy: " (cli)")[0]
// Strip away the text before the version number
version = version.components(separatedBy: "PHP ")[1]
let version = Shell
// Get the version directly from PHP
.execute(command: "php -r 'print phpversion();'")
// also remove any colors
.replacingOccurrences(of: "\u{1b}(B\u{1b}[m", with: "")
// That's the long version
self.long = version
// Next up, let's strip away the minor version number
let segments = version.components(separatedBy: ".")
let segments = long.components(separatedBy: ".")
// Get the first two elements
self.short = segments[0...1].joined(separator: ".")
}

26
phpmon/Constants.swift Normal file
View File

@ -0,0 +1,26 @@
//
// Constants.swift
// phpmon
//
// Created by Nico Verbruggen on 08/07/2019.
// Copyright © 2019 Nico Verbruggen. All rights reserved.
//
import Cocoa
class Constants {
/**
* The PHP versions supported by this application.
*/
static let SupportedPhpVersions = [
"5.6", "7.0", "7.1", "7.2", "7.3"
]
/**
Which php version is aliased as `php` to brew?
This is usually the latest PHP version.
*/
static let LatestPhpVersion = "7.3"
}

View File

@ -1,39 +0,0 @@
//
// Environment.swift
// phpmon
//
// Created by Nico Verbruggen on 12/06/2019.
// Copyright © 2019 Nico Verbruggen. All rights reserved.
//
import Foundation
class Environment {
public static func performBootChecks()
{
if (!Shell.execute(command: "which php").contains("/usr/local/bin/php")) {
DispatchQueue.main.async {
Alert.present(
messageText: "PHP is not correctly installed",
informativeText: "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php`. The app will not work correctly until you resolve this issue."
)
}
}
if (!Shell.execute(command: "ls /usr/local/opt | grep php@7.3").contains("php@7.3")) {
DispatchQueue.main.async {
Alert.present(
messageText: "PHP 7.3 is not correctly installed",
informativeText: "PHP 7.3 alias was not found in `/usr/local/opt`. The app will not work correctly until you resolve this issue."
)
}
}
if (!Shell.execute(command: "which valet").contains("/usr/local/bin/valet")) {
DispatchQueue.main.async {
Alert.present(
messageText: "Laravel Valet is not correctly installed",
informativeText: "You must install Valet via brew. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet`. The app will not work correctly until you resolve this issue."
)
}
}
}
}

View File

@ -17,9 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<string>1.2</string>
<key>CFBundleVersion</key>
<string>4</string>
<string>10</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>

View File

@ -0,0 +1,61 @@
//
// Environment.swift
// phpmon
//
// Created by Nico Verbruggen on 12/06/2019.
// Copyright © 2019 Nico Verbruggen. All rights reserved.
//
import Foundation
class BootChecks {
public static func perform()
{
self.presentAlertOnMainThreadIf(
!Shell.execute(command: "which php").contains("/usr/local/bin/php"),
messageText: "PHP is not correctly installed",
informativeText: "You must install PHP via brew. Try running `which php` in Terminal, it should return `/usr/local/bin/php`. The app will not work correctly until you resolve this issue."
)
self.presentAlertOnMainThreadIf(
!Shell.execute(command: "ls /usr/local/opt | grep php@7.3").contains("php@7.3"),
messageText: "PHP 7.3 is not correctly installed",
informativeText: "PHP 7.3 alias was not found in `/usr/local/opt`. The app will not work correctly until you resolve this issue."
)
self.presentAlertOnMainThreadIf(
!Shell.execute(command: "which valet").contains("/usr/local/bin/valet"),
messageText: "Laravel Valet is not correctly installed",
informativeText: "You must install Valet via brew. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet`. The app will not work correctly until you resolve this issue."
)
self.presentAlertOnMainThreadIf(
!Shell.execute(command: "cat /private/etc/sudoers.d/brew").contains("/usr/local/bin/brew"),
messageText: "Brew has not been added to sudoers.d",
informativeText: "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue."
)
self.presentAlertOnMainThreadIf(
!Shell.execute(command: "cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet"),
messageText: "Valet has not been added to sudoers.d",
informativeText: "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue."
)
}
private static func presentAlertOnMainThreadIf(
_ condition: Bool,
messageText: String,
informativeText: String
)
{
if (condition) {
DispatchQueue.main.async {
Alert.present(
messageText: messageText,
informativeText: informativeText
)
}
}
}
}

View File

@ -7,22 +7,17 @@
//
import Foundation
import AppKit
class Services {
public static func mysqlIsRunning() -> Bool {
let running = Shell.execute(command: "launchctl list | grep homebrew.mxcl.mysql")
if (running != "") {
return true
}
return false
return (running != "")
}
public static func nginxIsRunning() -> Bool {
let running = Shell.execute(command: "launchctl list | grep homebrew.mxcl.nginx")
if (running != "") {
return true
}
return false
return (running != "")
}
public static func detectPhpVersions() -> [String] {
@ -46,7 +41,20 @@ class Services {
}
if (availableVersions.contains("7.3")) {
_ = Shell.execute(command: "brew link php@7.3")
_ = Shell.execute(command: "valet use php@\(version)")
if (version == Constants.LatestPhpVersion) {
_ = Shell.execute(command: "valet use php")
} else {
_ = Shell.execute(command: "valet use php@\(version)")
}
}
}
public static func restartPhp(version: String) {
_ = Shell.execute(command: "brew services restart php@\(version)")
}
public static func openPhpConfigFolder(version: String) {
let files = [NSURL(fileURLWithPath: "/usr/local/etc/php/\(version)/php.ini")];
NSWorkspace.shared.activateFileViewerSelecting(files as [URL]);
}
}

View File

@ -9,8 +9,8 @@
import Cocoa
class Shell {
public static func execute(command: String) -> String
{
public static func execute(command: String) -> String {
let task = Process()
task.launchPath = "/bin/bash"
task.arguments = ["--login", "-c", command]
@ -20,7 +20,11 @@ class Shell {
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String
let output: String = NSString(
data: data,
encoding: String.Encoding.utf8.rawValue
)! as String
return output
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Application-->
<scene sceneID="JPo-4y-FX3">
<objects>
<application id="hnw-xV-0zn" sceneMemberID="viewController">
<menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="phpmon" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="phpmon" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About phpmon" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Quit phpmon" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="phpmon Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="Ady-hI-5gd" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
</connections>
</application>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="phpmon" customModuleProvider="target"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="134" y="40"/>
</scene>
<!--Log View Controller-->
<scene sceneID="hIz-AP-VOD">
<objects>
<viewController storyboardIdentifier="logWindow" id="XfG-lQ-9wD" customClass="LogViewController" customModule="phpmon" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" identifier="main" id="m2S-Jp-Qdl">
<rect key="frame" x="0.0" y="0.0" width="578" height="308"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ICa-gx-jgq">
<rect key="frame" x="489" y="13" 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="20" y="51" width="538" height="237"/>
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="s5L-AU-0fw">
<rect key="frame" x="1" y="1" width="536" height="235"/>
<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="536" height="235"/>
<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="536" height="235"/>
<size key="maxSize" width="538" height="10000000"/>
<color key="insertionPointColor" name="textColor" catalog="System" colorSpace="catalog"/>
</textView>
</subviews>
</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="521" y="1" width="16" height="235"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<constraints>
<constraint firstItem="vZy-5S-021" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" constant="20" id="K0k-oE-r37"/>
<constraint firstAttribute="trailing" secondItem="ICa-gx-jgq" secondAttribute="trailing" constant="20" id="LFS-0E-Ibw"/>
<constraint firstItem="vZy-5S-021" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="20" id="Nec-oI-CjE"/>
<constraint firstAttribute="trailing" secondItem="vZy-5S-021" secondAttribute="trailing" constant="20" id="kBJ-O5-eYI"/>
<constraint firstAttribute="bottom" secondItem="ICa-gx-jgq" secondAttribute="bottom" constant="20" 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>
</viewController>
<customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-147" y="294"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,17 @@
//
// ViewController.swift
// phpmon
//
// Created by Nico Verbruggen on 11/06/2019.
// Copyright © 2019 Nico Verbruggen. All rights reserved.
//
import Cocoa
class LogViewController: NSViewController {
@IBAction func pressed(_ sender: Any) {
self.view.window?.windowController?.close()
}
}

View File

@ -1,27 +0,0 @@
//
// ViewController.swift
// phpmon
//
// Created by Nico Verbruggen on 11/06/2019.
// Copyright © 2019 Nico Verbruggen. All rights reserved.
//
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}