mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 20:33:01 +02:00
Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
015f406ddf | |||
e1a97672b5 | |||
493b5945f9 | |||
52606aae8b | |||
2d6ca0f841 | |||
34900f929f | |||
5dbd05fdfb | |||
fe3cf9adb1 | |||
9bc8460cce | |||
4cbd2fd6eb | |||
6fef3fe37a | |||
72a20d1ed9 | |||
73ed80434a | |||
a78672927b | |||
4256eae442 | |||
76412b68f3 | |||
9153bb140a | |||
c9c15d10f9 | |||
e8c2277ef5 | |||
23720c5dc9 | |||
f881f07cba | |||
b072ee8dec | |||
acfbc0b66f |
@@ -18,7 +18,7 @@
|
|||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||||
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; };
|
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; };
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
||||||
C41C1B4B22B019FF00E7CF16 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PhpInstallation.swift */; };
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||||
C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Actions.swift */; };
|
C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Actions.swift */; };
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
C42295DD2358D02000E263B2 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; };
|
C42295DD2358D02000E263B2 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; };
|
||||||
@@ -49,6 +49,10 @@
|
|||||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.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 */; };
|
||||||
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; };
|
||||||
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */; };
|
||||||
|
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||||
|
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||||
C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; };
|
C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; };
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
||||||
C4F7809F25D8037C000DBC97 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; };
|
C4F7809F25D8037C000DBC97 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42295DC2358D02000E263B2 /* Command.swift */; };
|
||||||
@@ -70,7 +74,7 @@
|
|||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
||||||
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
||||||
C4F780CC25D80B75000DBC97 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PhpInstallation.swift */; };
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||||
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; };
|
||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
||||||
@@ -101,7 +105,7 @@
|
|||||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; 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>"; };
|
C41C1B4622B009A400E7CF16 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = "<group>"; };
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
||||||
C41C1B4A22B019FF00E7CF16 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
C41C1B4C22B0215A00E7CF16 /* Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
|
C41C1B4C22B0215A00E7CF16 /* Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
|
||||||
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
||||||
C42295DC2358D02000E263B2 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
C42295DC2358D02000E263B2 /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
||||||
@@ -129,6 +133,8 @@
|
|||||||
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>"; };
|
||||||
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
|
C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewDiagnostics.swift; sourceTree = "<group>"; };
|
||||||
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
C4F7807425D7F7E5000DBC97 /* RELEASE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RELEASE.md; sourceTree = "<group>"; };
|
C4F7807425D7F7E5000DBC97 /* RELEASE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RELEASE.md; sourceTree = "<group>"; };
|
||||||
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
@@ -170,6 +176,16 @@
|
|||||||
path = Preferences;
|
path = Preferences;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
54B20EDF263AA22C00D3250E /* PHP */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
||||||
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
||||||
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
||||||
|
);
|
||||||
|
path = PHP;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C405A4CD24B9B9070062FAFA /* IAP */ = {
|
C405A4CD24B9B9070062FAFA /* IAP */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -228,10 +244,11 @@
|
|||||||
C41E181722CB61EB0072CF09 /* Domain */ = {
|
C41E181722CB61EB0072CF09 /* Domain */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5420395726135DB800FB00FA /* Preferences */,
|
|
||||||
C4F7808A25D7F918000DBC97 /* Terminal */,
|
|
||||||
C4B13B1D25C4915000548C3A /* Core */,
|
C4B13B1D25C4915000548C3A /* Core */,
|
||||||
|
54B20EDF263AA22C00D3250E /* PHP */,
|
||||||
|
C4F7808A25D7F918000DBC97 /* Terminal */,
|
||||||
C47331A0247093AC009A0597 /* Menu */,
|
C47331A0247093AC009A0597 /* Menu */,
|
||||||
|
5420395726135DB800FB00FA /* Preferences */,
|
||||||
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
||||||
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
||||||
);
|
);
|
||||||
@@ -257,6 +274,8 @@
|
|||||||
C476FF9722B0DD830098105B /* Alert.swift */,
|
C476FF9722B0DD830098105B /* Alert.swift */,
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
||||||
|
C4F2E4362752F0870020E974 /* HomebrewDiagnostics.swift */,
|
||||||
|
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */,
|
||||||
);
|
);
|
||||||
path = Helpers;
|
path = Helpers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -266,9 +285,8 @@
|
|||||||
children = (
|
children = (
|
||||||
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
|
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
|
||||||
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
||||||
C41C1B4A22B019FF00E7CF16 /* PhpInstallation.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
C41C1B4C22B0215A00E7CF16 /* Actions.swift */,
|
||||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */,
|
|
||||||
);
|
);
|
||||||
path = Core;
|
path = Core;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -294,8 +312,6 @@
|
|||||||
C49EAB45259FC305007F6C3B /* Paths.swift */,
|
C49EAB45259FC305007F6C3B /* Paths.swift */,
|
||||||
C42295DC2358D02000E263B2 /* Command.swift */,
|
C42295DC2358D02000E263B2 /* Command.swift */,
|
||||||
C41C1B4622B009A400E7CF16 /* Shell.swift */,
|
C41C1B4622B009A400E7CF16 /* Shell.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
|
||||||
C41C1B4C22B0215A00E7CF16 /* Actions.swift */,
|
|
||||||
);
|
);
|
||||||
path = Terminal;
|
path = Terminal;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -431,8 +447,10 @@
|
|||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
||||||
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
|
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
|
||||||
|
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
||||||
C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */,
|
C41C1B4D22B0215A00E7CF16 /* Actions.swift in Sources */,
|
||||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */,
|
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */,
|
||||||
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C42295DD2358D02000E263B2 /* Command.swift in Sources */,
|
C42295DD2358D02000E263B2 /* Command.swift in Sources */,
|
||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||||
@@ -443,7 +461,7 @@
|
|||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C41C1B4B22B019FF00E7CF16 /* PhpInstallation.swift in Sources */,
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
||||||
C49EAB46259FC305007F6C3B /* Paths.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 */,
|
||||||
@@ -461,13 +479,14 @@
|
|||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
||||||
C4F780CC25D80B75000DBC97 /* PhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
||||||
C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */,
|
C4F780C725D80B75000DBC97 /* StatusMenu.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
@@ -477,6 +496,7 @@
|
|||||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||||
C4F780A225D804AA000DBC97 /* Paths.swift in Sources */,
|
C4F780A225D804AA000DBC97 /* Paths.swift in Sources */,
|
||||||
|
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */,
|
||||||
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
|
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
|
||||||
C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */,
|
C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */,
|
||||||
C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */,
|
C4F7809625D7FBF8000DBC97 /* Shell.swift in Sources */,
|
||||||
@@ -639,7 +659,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 = 56;
|
CURRENT_PROJECT_VERSION = 80;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@@ -647,7 +667,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 3.4;
|
MARKETING_VERSION = 4.0;
|
||||||
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 = "";
|
||||||
@@ -663,7 +683,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 = 56;
|
CURRENT_PROJECT_VERSION = 80;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@@ -671,7 +691,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 3.4;
|
MARKETING_VERSION = 4.0;
|
||||||
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 = "";
|
||||||
@@ -692,7 +712,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
"@loader_path/../Frameworks",
|
"@loader_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.1;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -713,7 +733,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
"@loader_path/../Frameworks",
|
"@loader_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.1;
|
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
|
@@ -21,7 +21,7 @@ PHP Monitor also gives you quick access to various useful functionality (like ac
|
|||||||
|
|
||||||
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.14 Mojave or higher (works on macOS 11 Big Sur)
|
* macOS 10.14 Mojave or higher (works on macOS 11 Big Sur and macOS 12 Monterey)
|
||||||
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
||||||
* The brew formula `php` has to be installed (which version is detected)
|
* The brew formula `php` has to be installed (which version is detected)
|
||||||
* Laravel Valet 2.13 or higher
|
* Laravel Valet 2.13 or higher
|
||||||
@@ -30,7 +30,7 @@ _You may need to update your Valet installation to keep everything working if a
|
|||||||
|
|
||||||
## 🚀 How to install
|
## 🚀 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 (recommended), or may download the latest release on GitHub.
|
||||||
|
|
||||||
To install via Homebrew, run:
|
To install via Homebrew, run:
|
||||||
|
|
||||||
@@ -73,6 +73,7 @@ If you're still having issues, here's a few common questions & answers, as well
|
|||||||
<li>PHP 7.4</li>
|
<li>PHP 7.4</li>
|
||||||
<li>PHP 8.0</li>
|
<li>PHP 8.0</li>
|
||||||
<li>PHP 8.1</li>
|
<li>PHP 8.1</li>
|
||||||
|
<li>PHP 8.2 (experimental)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Constants.swift#L16) file to see which versions are supported.
|
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Constants.swift#L16) file to see which versions are supported.
|
||||||
@@ -274,7 +275,7 @@ Thank you very much for your contributions, kind words and support.
|
|||||||
|
|
||||||
### Loading info about PHP in the background
|
### Loading info about PHP in the background
|
||||||
|
|
||||||
This utility runs `php-config --version'` in the background periodically. It also checks your `.ini` files for extensions and loads more information about your limits (memory limit, POST limit, upload limit).
|
This utility runs `php-config --version` in the background periodically. It also checks your `.ini` files for extensions and loads more information about your limits (memory limit, POST limit, upload limit).
|
||||||
|
|
||||||
In order to save power, this only happens once every 60 seconds.
|
In order to save power, this only happens once every 60 seconds.
|
||||||
|
|
||||||
@@ -301,7 +302,7 @@ This app isn't very complicated after all. In the end, this just (conveniently)
|
|||||||
|
|
||||||
## 🔧 Build instructions
|
## 🔧 Build instructions
|
||||||
|
|
||||||
<img src="./docs/build.png" width="320px" alt="build button in Xcode"/>
|
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||||
|
|
||||||
If you'd like to build PHP Monitor yourself, you need:
|
If you'd like to build PHP Monitor yourself, you need:
|
||||||
|
|
||||||
|
17
SECURITY.md
17
SECURITY.md
@@ -4,13 +4,16 @@
|
|||||||
|
|
||||||
Generally speaking, only the latest version of **PHP Monitor** is supported:
|
Generally speaking, only the latest version of **PHP Monitor** is supported:
|
||||||
|
|
||||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target |
|
| Version | Apple silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions |
|
||||||
| ------- | ------------- | ------------------ | ----- | ----- |
|
| ------- | ------------- | ------------------ | ----- | ----- | ----- |
|
||||||
| 3.x | ✅ Universal binary | ✅ | Big Sur (11.0) | macOS 10.14+ |
|
| 4.0 | ✅ Universal binary | ✅ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 |
|
||||||
| 2.6 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ |
|
| 3.5 | ✅ Universal binary | ✅ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 |
|
||||||
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ |
|
| 3.5 | ✅ Universal binary | ✅ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 |
|
||||||
| 2.4 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ |
|
| 3.0—3.4 | ✅ Universal binary | ✅ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.1 |
|
||||||
| < 2.4 | ❌ Intel binary<br/>`/usr/local/homebrew` installations only | ❌ | Catalina (10.15) | macOS 10.14+ |
|
| 2.6 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.0 |
|
||||||
|
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ | not applicable |
|
||||||
|
| 2.4 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ | not applicable |
|
||||||
|
| < 2.4 | ❌ Intel binary<br/>`/usr/local/homebrew` installations only | ❌ | Catalina (10.15) | macOS 10.14+ | not applicable |
|
||||||
|
|
||||||
## Reporting a vulnerability
|
## Reporting a vulnerability
|
||||||
|
|
||||||
|
BIN
docs/build.png
BIN
docs/build.png
Binary file not shown.
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 18 KiB |
@@ -23,6 +23,9 @@ class BrewJsonParserTest: XCTestCase {
|
|||||||
XCTAssertEqual(package.name, "php")
|
XCTAssertEqual(package.name, "php")
|
||||||
XCTAssertEqual(package.full_name, "php")
|
XCTAssertEqual(package.full_name, "php")
|
||||||
XCTAssertEqual(package.aliases.first!, "php@8.0")
|
XCTAssertEqual(package.aliases.first!, "php@8.0")
|
||||||
|
XCTAssertEqual(package.installed.contains(where: { installed in
|
||||||
|
installed.version.starts(with: "8.0")
|
||||||
|
}), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,11 +27,16 @@ class ExtensionParserTest: XCTestCase {
|
|||||||
return ext.name
|
return ext.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These 6 should be found
|
||||||
XCTAssertTrue(extensionNames.contains("xdebug"))
|
XCTAssertTrue(extensionNames.contains("xdebug"))
|
||||||
XCTAssertTrue(extensionNames.contains("imagick"))
|
XCTAssertTrue(extensionNames.contains("imagick"))
|
||||||
|
XCTAssertTrue(extensionNames.contains("sodium-next"))
|
||||||
XCTAssertTrue(extensionNames.contains("opcache"))
|
XCTAssertTrue(extensionNames.contains("opcache"))
|
||||||
XCTAssertTrue(extensionNames.contains("yaml"))
|
XCTAssertTrue(extensionNames.contains("yaml"))
|
||||||
|
XCTAssertTrue(extensionNames.contains("custom"))
|
||||||
|
|
||||||
XCTAssertFalse(extensionNames.contains("fake"))
|
XCTAssertFalse(extensionNames.contains("fake"))
|
||||||
|
XCTAssertFalse(extensionNames.contains("nice"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExtensionStatusIsCorrect() throws {
|
func testExtensionStatusIsCorrect() throws {
|
||||||
@@ -47,7 +52,7 @@ class ExtensionParserTest: XCTestCase {
|
|||||||
func testToggleWorksAsExpected() throws {
|
func testToggleWorksAsExpected() throws {
|
||||||
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||||
let extensions = PhpExtension.load(from: destination)
|
let extensions = PhpExtension.load(from: destination)
|
||||||
XCTAssertEqual(extensions.count, 4)
|
XCTAssertEqual(extensions.count, 6)
|
||||||
|
|
||||||
// Try to disable xdebug (should be detected first)!
|
// Try to disable xdebug (should be detected first)!
|
||||||
let xdebug = extensions.first!
|
let xdebug = extensions.first!
|
||||||
|
@@ -1,8 +1,16 @@
|
|||||||
|
# These should be detected
|
||||||
|
|
||||||
zend_extension="xdebug.so"
|
zend_extension="xdebug.so"
|
||||||
; zend_extension="imagick.so"
|
; zend_extension="imagick.so"
|
||||||
zend_extension=/opt/homebrew/opt/php/lib/php/20200930/opcache.so
|
zend_extension=/opt/homebrew/opt/php/lib/php/20200930/opcache.so
|
||||||
zend_extension="/opt/homebrew/opt/php/lib/php/20200930/yaml.so"
|
zend_extension="/opt/homebrew/opt/php/lib/php/20200930/yaml.so"
|
||||||
#zend_extension="/opt/homebrew/opt/php/lib/php/20200930/fake.so"
|
;zend_extension="sodium-next.so"
|
||||||
|
extension = custom.so
|
||||||
|
|
||||||
|
# These should not be detected
|
||||||
|
|
||||||
|
#zend_extension="/opt/homebrew/opt/php/lib/php/20200930/commented.so"
|
||||||
|
hextension = nice.so
|
||||||
|
|
||||||
[PHP]
|
[PHP]
|
||||||
|
|
||||||
|
@@ -18,25 +18,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
(invoked by PHP Monitor) shell commands. It is used to
|
(invoked by PHP Monitor) shell commands. It is used to
|
||||||
invoke all commands in this application.
|
invoke all commands in this application.
|
||||||
*/
|
*/
|
||||||
let sharedShell : Shell
|
let sharedShell: Shell
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The App singleton contains information about the state of
|
The App singleton contains information about the state of
|
||||||
the application and global variables.
|
the application and global variables.
|
||||||
*/
|
*/
|
||||||
let state : App
|
let state: App
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The MainMenu singleton is responsible for rendering the
|
The MainMenu singleton is responsible for rendering the
|
||||||
menu bar item and its menu, as well as its actions.
|
menu bar item and its menu, as well as its actions.
|
||||||
*/
|
*/
|
||||||
let menu : MainMenu
|
let menu: MainMenu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The paths singleton that determines where Homebrew is installed,
|
The paths singleton that determines where Homebrew is installed,
|
||||||
and where to look for binaries.
|
and where to look for binaries.
|
||||||
*/
|
*/
|
||||||
let paths : Paths
|
let paths: Paths
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
|
|
||||||
@@ -77,4 +77,5 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
|||||||
) -> Bool {
|
) -> Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,11 +9,21 @@ import Cocoa
|
|||||||
|
|
||||||
class Constants {
|
class Constants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The latest PHP version that is considered to be stable at the time of release.
|
||||||
|
* This version number is currently not used (only as a default fallback).
|
||||||
|
*/
|
||||||
|
static let LatestStablePhpVersion = "8.1"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PHP versions supported by this application.
|
* The PHP versions supported by this application.
|
||||||
* Versions that do not appear in this array are omitted from the list.
|
* Versions that do not appear in this array are omitted from the list.
|
||||||
*/
|
*/
|
||||||
static let SupportedPhpVersions = [
|
static let SupportedPhpVersions = [
|
||||||
|
// ====================
|
||||||
|
// STABLE RELEASES
|
||||||
|
// ====================
|
||||||
|
// Versions of PHP that are stable and are supported.
|
||||||
"5.6",
|
"5.6",
|
||||||
"7.0",
|
"7.0",
|
||||||
"7.1",
|
"7.1",
|
||||||
@@ -21,7 +31,16 @@ class Constants {
|
|||||||
"7.3",
|
"7.3",
|
||||||
"7.4",
|
"7.4",
|
||||||
"8.0",
|
"8.0",
|
||||||
"8.1"
|
"8.1",
|
||||||
|
|
||||||
|
// ====================
|
||||||
|
// EXPERIMENTAL SUPPORT
|
||||||
|
// ====================
|
||||||
|
// Every release that supports the next release will always support the next
|
||||||
|
// dev release. In this case, that means that the version below is detected.
|
||||||
|
"8.2"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,9 +29,26 @@ class Actions {
|
|||||||
|
|
||||||
print("The PHP versions that were detected are: \(versionsOnly)")
|
print("The PHP versions that were detected are: \(versionsOnly)")
|
||||||
|
|
||||||
|
App.shared.availablePhpVersions = versionsOnly
|
||||||
|
Actions.extractPhpLongVersions()
|
||||||
|
|
||||||
return versionsOnly
|
return versionsOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method extracts the PHP full version number after finding the php installation folders.
|
||||||
|
To be refactored at some later point, I'd like to cache the `PhpInstallation` objects instead of just the version number at some point.
|
||||||
|
*/
|
||||||
|
public static func extractPhpLongVersions()
|
||||||
|
{
|
||||||
|
var mappedVersions: [String: PhpInstallation] = [:]
|
||||||
|
App.shared.availablePhpVersions.forEach { version in
|
||||||
|
mappedVersions[version] = PhpInstallation(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
App.shared.cachedPhpInstallations = mappedVersions
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Extracts valid PHP versions from an array of strings.
|
Extracts valid PHP versions from an array of strings.
|
||||||
This array of strings is usually retrieved from `grep`.
|
This array of strings is usually retrieved from `grep`.
|
||||||
@@ -201,9 +218,14 @@ class Actions {
|
|||||||
{
|
{
|
||||||
// Escape slashes (or `sed` won't work)
|
// Escape slashes (or `sed` won't work)
|
||||||
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
||||||
let e_replacment = replacement.replacingOccurrences(of: "/", with: "\\/")
|
let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/")
|
||||||
|
|
||||||
Shell.run("sed -i '' 's/\(e_original)/\(e_replacment)/g' \(file)")
|
// Check if gsed exists; it is able to follow symlinks, which we want to do to toggle the extension
|
||||||
|
if Shell.fileExists("\(Paths.binPath)/gsed") {
|
||||||
|
Shell.run("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||||
|
} else {
|
||||||
|
Shell.run("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,4 +239,5 @@ class Actions {
|
|||||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
.contains("YES")
|
.contains("YES")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -16,10 +16,12 @@ class App {
|
|||||||
loadGlobalHotkey()
|
loadGlobalHotkey()
|
||||||
}
|
}
|
||||||
|
|
||||||
static var phpInstall: PhpInstallation? {
|
/** Information about the currently linked PHP installation. */
|
||||||
|
static var phpInstall: ActivePhpInstallation? {
|
||||||
return App.shared.currentInstall
|
return App.shared.currentInstall
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Whether the app is busy doing something. Used to determine what UI to display. */
|
||||||
static var busy: Bool {
|
static var busy: Bool {
|
||||||
return App.shared.busy
|
return App.shared.busy
|
||||||
}
|
}
|
||||||
@@ -40,13 +42,18 @@ class App {
|
|||||||
/**
|
/**
|
||||||
The currently active installation of PHP.
|
The currently active installation of PHP.
|
||||||
*/
|
*/
|
||||||
var currentInstall: PhpInstallation? = nil
|
var currentInstall: ActivePhpInstallation? = nil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
All available versions of PHP.
|
All available versions of PHP.
|
||||||
*/
|
*/
|
||||||
var availablePhpVersions : [String] = []
|
var availablePhpVersions : [String] = []
|
||||||
|
|
||||||
|
/**
|
||||||
|
Cached information about the PHP installations; contains only the full version number at this point.
|
||||||
|
*/
|
||||||
|
var cachedPhpInstallations : [String: PhpInstallation] = [:]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The timer that will periodically fetch the PHP version that is currently active.
|
The timer that will periodically fetch the PHP version that is currently active.
|
||||||
*/
|
*/
|
||||||
@@ -55,7 +62,7 @@ class App {
|
|||||||
/**
|
/**
|
||||||
Information we were able to discern from the Homebrew info command (as JSON).
|
Information we were able to discern from the Homebrew info command (as JSON).
|
||||||
*/
|
*/
|
||||||
var brewPhpPackage: HomebrewPackage? = nil {
|
var brewPhpPackage: HomebrewPackage! = nil {
|
||||||
didSet {
|
didSet {
|
||||||
brewPhpVersion = brewPhpPackage!.version
|
brewPhpVersion = brewPhpPackage!.version
|
||||||
}
|
}
|
||||||
@@ -67,10 +74,10 @@ class App {
|
|||||||
If you're up to date, `php` will be aliased to the latest version,
|
If you're up to date, `php` will be aliased to the latest version,
|
||||||
but that might not be the case.
|
but that might not be the case.
|
||||||
|
|
||||||
We'll technically default to version 8.0, but the information should always be loaded
|
We'll technically default to the version in Constants.swift, but the information
|
||||||
from Homebrew itself upon starting the application.
|
should always be loaded from Homebrew itself upon startup.
|
||||||
*/
|
*/
|
||||||
var brewPhpVersion: String = "8.0"
|
var brewPhpVersion: String = Constants.LatestStablePhpVersion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The shortcut the user has requested.
|
The shortcut the user has requested.
|
||||||
@@ -83,29 +90,38 @@ class App {
|
|||||||
|
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
On startup, the preferences should be loaded from the .plist, and we'll enable the shortcut if it is set.
|
||||||
|
*/
|
||||||
private func loadGlobalHotkey() {
|
private func loadGlobalHotkey() {
|
||||||
let hotkey = Preferences.preferences[.globalHotkey] as! String?
|
// Make sure we can retrieve the hotkey from preferences; if we cannot, no hotkey is set
|
||||||
if hotkey == nil {
|
guard let hotkey = Preferences.preferences[.globalHotkey] as? String else {
|
||||||
|
print("No global hotkey loaded")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let keybindPref = GlobalKeybindPreference.fromJson(hotkey!)
|
// Make sure we can parse the JSON into the desired format; if we cannot, no hotkey is set
|
||||||
|
guard let keybindPref = GlobalKeybindPreference.fromJson(hotkey) else {
|
||||||
if (keybindPref != nil) {
|
print("No global hotkey loaded, could not be parsed!")
|
||||||
self.shortcutHotkey = HotKey(keyCombo: KeyCombo(
|
|
||||||
carbonKeyCode: keybindPref!.keyCode,
|
|
||||||
carbonModifiers: keybindPref!.carbonFlags
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
self.shortcutHotkey = nil
|
self.shortcutHotkey = nil
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.shortcutHotkey = HotKey(keyCombo: KeyCombo(
|
||||||
|
carbonKeyCode: keybindPref.keyCode,
|
||||||
|
carbonModifiers: keybindPref.carbonFlags
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets up the action that needs to occur when the shortcut key is pressed (open the menu).
|
||||||
|
*/
|
||||||
private func setupGlobalHotkeyListener() {
|
private func setupGlobalHotkeyListener() {
|
||||||
guard let hotKey = self.shortcutHotkey else {
|
guard let hotkey = self.shortcutHotkey else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hotKey.keyDownHandler = {
|
|
||||||
|
hotkey.keyDownHandler = {
|
||||||
MainMenu.shared.statusItem.button?.performClick(nil)
|
MainMenu.shared.statusItem.button?.performClick(nil)
|
||||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19455" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19455"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@@ -84,8 +84,8 @@
|
|||||||
<scene sceneID="iyi-IS-7Ps">
|
<scene sceneID="iyi-IS-7Ps">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController title="Preferences" storyboardIdentifier="preferences" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="PrefsVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="Preferences" storyboardIdentifier="preferences" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="PrefsVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" wantsLayer="YES" id="Pf1-A5-3Xz">
|
<view key="view" wantsLayer="YES" misplaced="YES" id="Pf1-A5-3Xz">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="574" height="189"/>
|
<rect key="frame" x="0.0" y="0.0" width="574" height="311"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="GSr-K5-3yw">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="GSr-K5-3yw">
|
||||||
@@ -102,7 +102,7 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MEf-MN-oXt">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MEf-MN-oXt">
|
||||||
<rect key="frame" x="148" y="152" width="406" height="18"/>
|
<rect key="frame" x="148" y="274" width="406" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="DYN_ICON" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="m5s-qp-Iaj">
|
<buttonCell key="cell" type="check" title="DYN_ICON" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="m5s-qp-Iaj">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
@@ -112,7 +112,7 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JrH-aa-AzL">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JrH-aa-AzL">
|
||||||
<rect key="frame" x="148" y="131" width="408" height="14"/>
|
<rect key="frame" x="148" y="253" width="408" height="14"/>
|
||||||
<textFieldCell key="cell" title="DYN_ICON_DESC" id="MHA-Xt-xgF">
|
<textFieldCell key="cell" title="DYN_ICON_DESC" id="MHA-Xt-xgF">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -150,7 +150,7 @@ Gw
|
|||||||
<rect key="frame" x="325" y="75" width="138" height="32"/>
|
<rect key="frame" x="325" y="75" width="138" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="CLEAR_SHORTCUT" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nvE-5d-VOS">
|
<buttonCell key="cell" type="push" title="CLEAR_SHORTCUT" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nvE-5d-VOS">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system" size="11"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="unregister:" target="AW2-rV-rbS" id="2RI-4w-6Td"/>
|
<action selector="unregister:" target="AW2-rV-rbS" id="2RI-4w-6Td"/>
|
||||||
@@ -165,7 +165,7 @@ Gw
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="31d-gd-auR">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="31d-gd-auR">
|
||||||
<rect key="frame" x="18" y="153" width="124" height="16"/>
|
<rect key="frame" x="18" y="275" width="124" height="16"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="120" id="8dt-Pg-wFI"/>
|
<constraint firstAttribute="width" constant="120" id="8dt-Pg-wFI"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
@@ -183,44 +183,106 @@ Gw
|
|||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vSc-oQ-NC5">
|
||||||
|
<rect key="frame" x="148" y="220" width="121" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="FULL_PHP_VER" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="eCd-ja-EwE">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggledFullPhpVersion:" target="AW2-rV-rbS" id="RCY-Ah-sLM"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t24-LR-wKz">
|
||||||
|
<rect key="frame" x="148" y="199" width="123" height="14"/>
|
||||||
|
<textFieldCell key="cell" title="FULL_PHP_VER_DESC" id="8gG-qs-mHR">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ogC-wz-ZfO">
|
||||||
|
<rect key="frame" x="18" y="153" width="124" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="120" id="i9O-6m-Gr9"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="PREF_SERVICES:" id="bm4-rf-kCF">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="47u-9B-eDu">
|
||||||
|
<rect key="frame" x="148" y="152" width="126" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="AUTO_RESTART" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="n1d-l4-inL">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="toggledAutoRestartServices:" target="AW2-rV-rbS" id="THn-nu-IiJ"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ObP-GE-ejZ">
|
||||||
|
<rect key="frame" x="148" y="131" width="126" height="14"/>
|
||||||
|
<textFieldCell key="cell" title="AUTO_RESTART_DESC" id="F9P-iQ-gBk">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
|
<constraint firstItem="ogC-wz-ZfO" firstAttribute="trailing" secondItem="31d-gd-auR" secondAttribute="trailing" id="2Lr-Ht-qKI"/>
|
||||||
|
<constraint firstItem="t24-LR-wKz" firstAttribute="leading" secondItem="vSc-oQ-NC5" secondAttribute="leading" id="3tK-kp-q5R"/>
|
||||||
|
<constraint firstItem="t24-LR-wKz" firstAttribute="top" secondItem="vSc-oQ-NC5" secondAttribute="bottom" constant="8" symbolic="YES" id="4Ft-lN-vwA"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="JrH-aa-AzL" secondAttribute="trailing" constant="20" symbolic="YES" id="8iM-Xf-ShU"/>
|
<constraint firstAttribute="trailing" secondItem="JrH-aa-AzL" secondAttribute="trailing" constant="20" symbolic="YES" id="8iM-Xf-ShU"/>
|
||||||
|
<constraint firstItem="ObP-GE-ejZ" firstAttribute="leading" secondItem="47u-9B-eDu" secondAttribute="leading" id="ASF-WR-A3X"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="GSr-K5-3yw" secondAttribute="trailing" constant="20" symbolic="YES" id="AT9-5F-6g1"/>
|
<constraint firstAttribute="trailing" secondItem="GSr-K5-3yw" secondAttribute="trailing" constant="20" symbolic="YES" id="AT9-5F-6g1"/>
|
||||||
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="trailing" constant="12" symbolic="YES" id="Bk6-4V-GLk"/>
|
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="trailing" constant="12" symbolic="YES" id="Bk6-4V-GLk"/>
|
||||||
<constraint firstItem="31d-gd-auR" firstAttribute="top" secondItem="Pf1-A5-3Xz" secondAttribute="top" constant="20" symbolic="YES" id="C3K-NX-BBl"/>
|
<constraint firstItem="31d-gd-auR" firstAttribute="top" secondItem="Pf1-A5-3Xz" secondAttribute="top" constant="20" symbolic="YES" id="C3K-NX-BBl"/>
|
||||||
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="top" secondItem="V7b-jv-oCB" secondAttribute="top" id="DY5-za-saX"/>
|
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="top" secondItem="V7b-jv-oCB" secondAttribute="top" id="DY5-za-saX"/>
|
||||||
|
<constraint firstItem="vSc-oQ-NC5" firstAttribute="leading" secondItem="JrH-aa-AzL" secondAttribute="leading" id="FVa-vu-VGJ"/>
|
||||||
<constraint firstItem="MEf-MN-oXt" firstAttribute="leading" secondItem="31d-gd-auR" secondAttribute="trailing" constant="10" id="G5S-JV-re3"/>
|
<constraint firstItem="MEf-MN-oXt" firstAttribute="leading" secondItem="31d-gd-auR" secondAttribute="trailing" constant="10" id="G5S-JV-re3"/>
|
||||||
<constraint firstItem="V7b-jv-oCB" firstAttribute="firstBaseline" secondItem="5ZK-BG-o1t" secondAttribute="firstBaseline" id="H5D-2D-DLH"/>
|
<constraint firstItem="V7b-jv-oCB" firstAttribute="firstBaseline" secondItem="5ZK-BG-o1t" secondAttribute="firstBaseline" id="H5D-2D-DLH"/>
|
||||||
<constraint firstItem="1TO-9H-z2d" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="leading" id="Imk-o0-2fS"/>
|
<constraint firstItem="1TO-9H-z2d" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="leading" id="Imk-o0-2fS"/>
|
||||||
|
<constraint firstItem="ObP-GE-ejZ" firstAttribute="top" secondItem="47u-9B-eDu" secondAttribute="bottom" constant="8" symbolic="YES" id="JqR-Jd-SoR"/>
|
||||||
<constraint firstItem="JrH-aa-AzL" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="K2H-Af-2qK"/>
|
<constraint firstItem="JrH-aa-AzL" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="K2H-Af-2qK"/>
|
||||||
<constraint firstItem="5ZK-BG-o1t" firstAttribute="top" secondItem="JrH-aa-AzL" secondAttribute="bottom" constant="30" id="NMk-yt-fha"/>
|
<constraint firstItem="5ZK-BG-o1t" firstAttribute="top" secondItem="ObP-GE-ejZ" secondAttribute="bottom" constant="30" id="LO4-8j-ihp"/>
|
||||||
|
<constraint firstItem="47u-9B-eDu" firstAttribute="top" secondItem="ogC-wz-ZfO" secondAttribute="top" id="T9j-v2-fSW"/>
|
||||||
<constraint firstItem="JrH-aa-AzL" firstAttribute="top" secondItem="MEf-MN-oXt" secondAttribute="bottom" constant="8" symbolic="YES" id="Vf8-fx-H50"/>
|
<constraint firstItem="JrH-aa-AzL" firstAttribute="top" secondItem="MEf-MN-oXt" secondAttribute="bottom" constant="8" symbolic="YES" id="Vf8-fx-H50"/>
|
||||||
<constraint firstItem="MEf-MN-oXt" firstAttribute="firstBaseline" secondItem="31d-gd-auR" secondAttribute="firstBaseline" id="W36-bE-iAT"/>
|
<constraint firstItem="MEf-MN-oXt" firstAttribute="firstBaseline" secondItem="31d-gd-auR" secondAttribute="firstBaseline" id="W36-bE-iAT"/>
|
||||||
<constraint firstItem="1TO-9H-z2d" firstAttribute="firstBaseline" secondItem="V7b-jv-oCB" secondAttribute="baseline" constant="25" id="bJG-ed-pch"/>
|
<constraint firstItem="1TO-9H-z2d" firstAttribute="firstBaseline" secondItem="V7b-jv-oCB" secondAttribute="baseline" constant="25" id="bJG-ed-pch"/>
|
||||||
<constraint firstItem="V7b-jv-oCB" firstAttribute="leading" secondItem="JrH-aa-AzL" secondAttribute="leading" id="bUY-uH-N7A"/>
|
<constraint firstItem="V7b-jv-oCB" firstAttribute="leading" secondItem="JrH-aa-AzL" secondAttribute="leading" id="bUY-uH-N7A"/>
|
||||||
<constraint firstItem="5ZK-BG-o1t" firstAttribute="trailing" secondItem="31d-gd-auR" secondAttribute="trailing" id="c4g-jO-JUm"/>
|
<constraint firstItem="5ZK-BG-o1t" firstAttribute="trailing" secondItem="31d-gd-auR" secondAttribute="trailing" id="c4g-jO-JUm"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="GSr-K5-3yw" secondAttribute="bottom" constant="20" symbolic="YES" id="dAS-yW-vua"/>
|
<constraint firstAttribute="bottom" secondItem="GSr-K5-3yw" secondAttribute="bottom" constant="20" symbolic="YES" id="dAS-yW-vua"/>
|
||||||
|
<constraint firstItem="vSc-oQ-NC5" firstAttribute="top" secondItem="JrH-aa-AzL" secondAttribute="bottom" constant="16" id="hQf-4s-iHn"/>
|
||||||
<constraint firstItem="GSr-K5-3yw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="mTE-WD-54L"/>
|
<constraint firstItem="GSr-K5-3yw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="mTE-WD-54L"/>
|
||||||
|
<constraint firstItem="47u-9B-eDu" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="n8B-C8-dXs"/>
|
||||||
<constraint firstItem="31d-gd-auR" firstAttribute="leading" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="o0J-yT-TDX"/>
|
<constraint firstItem="31d-gd-auR" firstAttribute="leading" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="o0J-yT-TDX"/>
|
||||||
|
<constraint firstItem="ogC-wz-ZfO" firstAttribute="top" secondItem="t24-LR-wKz" secondAttribute="bottom" constant="30" id="oXh-LE-sRS"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="MEf-MN-oXt" secondAttribute="trailing" constant="20" symbolic="YES" id="pJg-zj-cBs"/>
|
<constraint firstAttribute="trailing" secondItem="MEf-MN-oXt" secondAttribute="trailing" constant="20" symbolic="YES" id="pJg-zj-cBs"/>
|
||||||
<constraint firstItem="GSr-K5-3yw" firstAttribute="top" secondItem="1TO-9H-z2d" secondAttribute="bottom" constant="20" id="pMZ-Gx-Jmm"/>
|
<constraint firstItem="GSr-K5-3yw" firstAttribute="top" secondItem="1TO-9H-z2d" secondAttribute="bottom" constant="20" id="pMZ-Gx-Jmm"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="buttonAutoRestartServices" destination="47u-9B-eDu" id="kyg-BX-PQK"/>
|
||||||
<outlet property="buttonClearShortcut" destination="YsQ-AZ-Aei" id="1xo-hk-HgM"/>
|
<outlet property="buttonClearShortcut" destination="YsQ-AZ-Aei" id="1xo-hk-HgM"/>
|
||||||
<outlet property="buttonClose" destination="GSr-K5-3yw" id="d4I-Cf-gXD"/>
|
<outlet property="buttonClose" destination="GSr-K5-3yw" id="d4I-Cf-gXD"/>
|
||||||
|
<outlet property="buttonDisplayFullPhpVersion" destination="vSc-oQ-NC5" id="ZLa-Vf-4Dq"/>
|
||||||
<outlet property="buttonDynamicIcon" destination="MEf-MN-oXt" id="qEN-Vg-EZS"/>
|
<outlet property="buttonDynamicIcon" destination="MEf-MN-oXt" id="qEN-Vg-EZS"/>
|
||||||
<outlet property="buttonSetShortcut" destination="V7b-jv-oCB" id="2aS-S4-cKR"/>
|
<outlet property="buttonSetShortcut" destination="V7b-jv-oCB" id="2aS-S4-cKR"/>
|
||||||
|
<outlet property="labelAutoRestartServices" destination="ObP-GE-ejZ" id="uwY-D7-Uve"/>
|
||||||
|
<outlet property="labelDisplayFullPhpVersion" destination="t24-LR-wKz" id="wYj-Z0-a3h"/>
|
||||||
<outlet property="labelDynamicIcon" destination="JrH-aa-AzL" id="CFc-fF-oPq"/>
|
<outlet property="labelDynamicIcon" destination="JrH-aa-AzL" id="CFc-fF-oPq"/>
|
||||||
<outlet property="labelShortcut" destination="1TO-9H-z2d" id="paF-hK-78x"/>
|
<outlet property="labelShortcut" destination="1TO-9H-z2d" id="paF-hK-78x"/>
|
||||||
<outlet property="leftLabelDynamicIcon" destination="31d-gd-auR" id="ANZ-Zs-4d7"/>
|
<outlet property="leftLabelDynamicIcon" destination="31d-gd-auR" id="ANZ-Zs-4d7"/>
|
||||||
<outlet property="leftLabelGlobalShortcut" destination="5ZK-BG-o1t" id="73E-9i-cg8"/>
|
<outlet property="leftLabelGlobalShortcut" destination="5ZK-BG-o1t" id="73E-9i-cg8"/>
|
||||||
|
<outlet property="leftLabelServices" destination="ogC-wz-ZfO" id="BYx-Gv-N1p"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="264" y="399.5"/>
|
<point key="canvasLocation" x="264" y="457"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
</document>
|
</document>
|
||||||
|
@@ -119,9 +119,14 @@ class Startup {
|
|||||||
|
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
// Present the information to the user
|
// Present the information to the user
|
||||||
Alert.notify(message: messageText, info: informativeText)
|
Alert.notify(
|
||||||
|
message: messageText,
|
||||||
|
info: informativeText,
|
||||||
|
style: breaking ? .critical : .warning
|
||||||
|
)
|
||||||
// Only breaking issues will throw the extra retry modal
|
// Only breaking issues will throw the extra retry modal
|
||||||
breaking ? failureCallback() : ()
|
breaking ? failureCallback() : ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -7,11 +7,12 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension Date
|
extension Date {
|
||||||
{
|
|
||||||
func toString() -> String {
|
func toString() -> String {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||||
return dateFormatter.string(from: self)
|
return dateFormatter.string(from: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -33,4 +33,5 @@ extension String {
|
|||||||
let end = r.upperBound
|
let end = r.upperBound
|
||||||
return String(self[start ..< end])
|
return String(self[start ..< end])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -7,17 +7,19 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
// Adapted from: https://stackoverflow.com/a/46268778
|
// Adapted from: https://stackoverflow.com/a/46268778
|
||||||
|
|
||||||
protocol XibLoadable {
|
protocol XibLoadable {
|
||||||
|
|
||||||
static var xibName: String? { get }
|
static var xibName: String? { get }
|
||||||
static func createFromXib(in bundle: Bundle) -> Self?
|
static func createFromXib(in bundle: Bundle) -> Self?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension XibLoadable where Self: NSView {
|
extension XibLoadable where Self: NSView {
|
||||||
|
|
||||||
static var xibName: String? {
|
static var xibName: String? {
|
||||||
return String(describing: Self.self)
|
return String(describing: Self.self)
|
||||||
}
|
}
|
||||||
@@ -30,4 +32,5 @@ extension XibLoadable where Self: NSView {
|
|||||||
let views = Array<Any>(results).filter { $0 is Self }
|
let views = Array<Any>(results).filter { $0 is Self }
|
||||||
return views.last as? Self
|
return views.last as? Self
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,11 @@ class Alert {
|
|||||||
messageText: String,
|
messageText: String,
|
||||||
informativeText: String,
|
informativeText: String,
|
||||||
buttonTitle: String = "OK",
|
buttonTitle: String = "OK",
|
||||||
secondButtonTitle: String = ""
|
secondButtonTitle: String = "",
|
||||||
|
style: NSAlert.Style = .informational
|
||||||
) -> Bool {
|
) -> Bool {
|
||||||
let alert = NSAlert.init()
|
let alert = NSAlert.init()
|
||||||
|
alert.alertStyle = style
|
||||||
alert.messageText = messageText
|
alert.messageText = messageText
|
||||||
alert.informativeText = informativeText
|
alert.informativeText = informativeText
|
||||||
alert.addButton(withTitle: buttonTitle)
|
alert.addButton(withTitle: buttonTitle)
|
||||||
@@ -25,8 +27,8 @@ class Alert {
|
|||||||
return alert.runModal() == .alertFirstButtonReturn
|
return alert.runModal() == .alertFirstButtonReturn
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func notify(message: String, info: String) {
|
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
||||||
_ = self.present(messageText: message, informativeText: info, buttonTitle: "OK", secondButtonTitle: "")
|
_ = self.present(messageText: message, informativeText: info, buttonTitle: "OK", secondButtonTitle: "", style: style)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
70
phpmon/Domain/Helpers/HomebrewDiagnostics.swift
Normal file
70
phpmon/Domain/Helpers/HomebrewDiagnostics.swift
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
//
|
||||||
|
// AliasConflict.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 28/11/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class HomebrewDiagnostics {
|
||||||
|
|
||||||
|
enum Errors: String {
|
||||||
|
case aliasConflict = "alias_conflict"
|
||||||
|
}
|
||||||
|
|
||||||
|
static let shared = HomebrewDiagnostics()
|
||||||
|
var errors: [HomebrewDiagnostics.Errors] = []
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if self.determineAliasConflicts() {
|
||||||
|
self.errors.append(.aliasConflict)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
It is possible to have the `shivammathur/php` tap installed, and for the core homebrew information to be outdated.
|
||||||
|
This will then result in two different aliases claiming to point to the same formula (`php`).
|
||||||
|
This will break all linking functionality in PHP Monitor, and the user needs to be informed of this.
|
||||||
|
|
||||||
|
This check only needs to be performed if the `shivammathur/php` tap is active.
|
||||||
|
*/
|
||||||
|
public func determineAliasConflicts() -> Bool
|
||||||
|
{
|
||||||
|
let tapAlias = Shell.pipe("\(Paths.brew) info shivammathur/php/php --json")
|
||||||
|
|
||||||
|
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") {
|
||||||
|
print("The user does not appear to have tapped: shivammathur/php")
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
print("The user DOES have the following tapped: shivammathur/php")
|
||||||
|
print("Checking for `php` formula conflicts...")
|
||||||
|
|
||||||
|
let tapPhp = try! JSONDecoder().decode(
|
||||||
|
[HomebrewPackage].self,
|
||||||
|
from: tapAlias.data(using: .utf8)!
|
||||||
|
).first!
|
||||||
|
|
||||||
|
if tapPhp.version != App.shared.brewPhpVersion {
|
||||||
|
print("The `php` formula alias seems to be the different between the tap and core. This could be a problem!")
|
||||||
|
print("Determining whether both of these versions are installed...")
|
||||||
|
|
||||||
|
let bothInstalled = App.shared.availablePhpVersions.contains(tapPhp.version)
|
||||||
|
&& App.shared.availablePhpVersions.contains(App.shared.brewPhpVersion)
|
||||||
|
|
||||||
|
if bothInstalled {
|
||||||
|
print("Both conflicting aliases seem to be installed, warning the user!")
|
||||||
|
} else {
|
||||||
|
print("Conflicting aliases are not both installed, seems fine!")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bothInstalled
|
||||||
|
}
|
||||||
|
|
||||||
|
print("All seems to be OK. No conflicts, both are PHP \(tapPhp.version).")
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -7,12 +7,23 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct HomebrewPackage : Decodable {
|
struct HomebrewPackage: Decodable {
|
||||||
|
|
||||||
let name: String
|
let name: String
|
||||||
let full_name: String
|
let full_name: String
|
||||||
let aliases: [String]
|
let aliases: [String]
|
||||||
|
let installed: [HomebrewInstalled]
|
||||||
|
let linked_keg: String?
|
||||||
|
|
||||||
public var version: String {
|
public var version: String {
|
||||||
return aliases.first!.replacingOccurrences(of: "php@", with: "")
|
return aliases.first!.replacingOccurrences(of: "php@", with: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HomebrewInstalled: Decodable {
|
||||||
|
let version: String
|
||||||
|
let built_as_bottle: Bool
|
||||||
|
let installed_as_dependency: Bool
|
||||||
|
let installed_on_request: Bool
|
||||||
}
|
}
|
@@ -10,6 +10,7 @@ import Foundation
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class HeaderView: NSView, XibLoadable {
|
class HeaderView: NSView, XibLoadable {
|
||||||
|
|
||||||
@IBOutlet weak var textField: NSTextField!
|
@IBOutlet weak var textField: NSTextField!
|
||||||
|
|
||||||
static func asMenuItem(text: String) -> NSMenuItem {
|
static func asMenuItem(text: String) -> NSMenuItem {
|
||||||
@@ -20,4 +21,5 @@ class HeaderView: NSView, XibLoadable {
|
|||||||
item.target = self
|
item.target = self
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,18 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
When the environment is all clear and the app can run, let's go.
|
When the environment is all clear and the app can run, let's go.
|
||||||
*/
|
*/
|
||||||
private func onEnvironmentPass() {
|
private func onEnvironmentPass() {
|
||||||
App.shared.availablePhpVersions = Actions.detectPhpVersions()
|
_ = Actions.detectPhpVersions()
|
||||||
|
|
||||||
|
if HomebrewDiagnostics.shared.errors.contains(.aliasConflict) {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
Alert.notify(
|
||||||
|
message: "alert.php_alias_conflict.title".localized,
|
||||||
|
info: "alert.php_alias_conflict.info".localized,
|
||||||
|
style: .critical
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
|
|
||||||
let installation = App.phpInstall!
|
let installation = App.phpInstall!
|
||||||
@@ -165,7 +176,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
// MARK: - User Interface
|
// MARK: - User Interface
|
||||||
|
|
||||||
@objc func updatePhpVersionInStatusBar() {
|
@objc func updatePhpVersionInStatusBar() {
|
||||||
App.shared.currentInstall = PhpInstallation()
|
App.shared.currentInstall = ActivePhpInstallation()
|
||||||
refreshIcon()
|
refreshIcon()
|
||||||
update()
|
update()
|
||||||
}
|
}
|
||||||
@@ -180,7 +191,8 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIconStatic"))!)
|
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIconStatic"))!)
|
||||||
} else {
|
} else {
|
||||||
// The dynamic icon has been requested
|
// The dynamic icon has been requested
|
||||||
setStatusBarImage(version: App.phpInstall!.version.short)
|
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
||||||
|
setStatusBarImage(version: long ? App.phpInstall!.version.long : App.phpInstall!.version.short)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,6 +268,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
sender.phpExtension?.toggle()
|
sender.phpExtension?.toggle()
|
||||||
|
|
||||||
|
if Preferences.preferences[.autoServiceRestartAfterExtensionToggle] as! Bool == true {
|
||||||
|
Actions.restartPhpFpm()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import Foundation
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class StatsView: NSView, XibLoadable {
|
class StatsView: NSView, XibLoadable {
|
||||||
|
|
||||||
@IBOutlet weak var titleMemLimit: NSTextField!
|
@IBOutlet weak var titleMemLimit: NSTextField!
|
||||||
@IBOutlet weak var titleMaxPost: NSTextField!
|
@IBOutlet weak var titleMaxPost: NSTextField!
|
||||||
@IBOutlet weak var titleMaxUpload: NSTextField!
|
@IBOutlet weak var titleMaxUpload: NSTextField!
|
||||||
@@ -31,4 +32,5 @@ class StatsView: NSView, XibLoadable {
|
|||||||
item.target = self
|
item.target = self
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -42,15 +42,24 @@ class StatusMenu : NSMenu {
|
|||||||
private func addSwitchToPhpMenuItems() {
|
private func addSwitchToPhpMenuItems() {
|
||||||
var shortcutKey = 1
|
var shortcutKey = 1
|
||||||
for index in (0..<App.shared.availablePhpVersions.count).reversed() {
|
for index in (0..<App.shared.availablePhpVersions.count).reversed() {
|
||||||
let version = App.shared.availablePhpVersions[index]
|
|
||||||
|
// Get the short and long version
|
||||||
|
let shortVersion = App.shared.availablePhpVersions[index]
|
||||||
|
let longVersion = App.shared.cachedPhpInstallations[shortVersion]!.longVersion
|
||||||
|
|
||||||
|
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
||||||
|
let versionString = long ? longVersion : shortVersion
|
||||||
|
|
||||||
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
||||||
let brew = (version == App.shared.brewPhpVersion) ? "php" : "php@\(version)"
|
let brew = (shortVersion == App.shared.brewPhpVersion) ? "php" : "php@\(shortVersion)"
|
||||||
let menuItem = PhpMenuItem(
|
let menuItem = PhpMenuItem(
|
||||||
title: "\("mi_php_switch".localized) \(version) (\(brew))",
|
title: "\("mi_php_switch".localized) \(versionString) (\(brew))",
|
||||||
action: (version == App.phpInstall?.version.short) ? nil : action, keyEquivalent: "\(shortcutKey)"
|
action: (shortVersion == App.phpInstall?.version.short) ? nil : action, keyEquivalent: "\(shortcutKey)"
|
||||||
)
|
)
|
||||||
menuItem.version = version
|
|
||||||
|
menuItem.version = shortVersion
|
||||||
shortcutKey = shortcutKey + 1
|
shortcutKey = shortcutKey + 1
|
||||||
|
|
||||||
self.addItem(menuItem)
|
self.addItem(menuItem)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// PhpInstallation.swift
|
// ActivePhpInstallation.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
@@ -7,7 +7,15 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PhpInstallation {
|
/**
|
||||||
|
An installed version of PHP, that was detected by scanning the `/opt/php@version/bin` directory.
|
||||||
|
|
||||||
|
When initialized, that version's .ini files are also scanned (for active or inactive extensions).
|
||||||
|
Integrity checks can be performed to determine whether PHP-FPM is configured correctly.
|
||||||
|
|
||||||
|
- Note: Each installation has a separate version number. Using `version.short` is advisable if you want to interact with Homebrew.
|
||||||
|
*/
|
||||||
|
class ActivePhpInstallation {
|
||||||
|
|
||||||
var version: Version!
|
var version: Version!
|
||||||
var configuration: Configuration!
|
var configuration: Configuration!
|
||||||
@@ -111,17 +119,33 @@ class PhpInstallation {
|
|||||||
return (match == nil) ? "⚠️" : "\(value)B"
|
return (match == nil) ? "⚠️" : "\(value)B"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
It is always possible that the system configuration for PHP-FPM has not been set up for Valet.
|
||||||
|
This can occur when a user manually installs a new PHP version, but does not run `valet install`.
|
||||||
|
In that case, we should alert the user!
|
||||||
|
|
||||||
|
- Important: The underlying check is `checkPhpFpmStatus`, which can be run multiple times.
|
||||||
|
This method actively presents a modal if said checks fails, so don't call this method too many times.
|
||||||
|
*/
|
||||||
public func notifyAboutBrokenPhpFpm() {
|
public func notifyAboutBrokenPhpFpm() {
|
||||||
if !self.checkPhpFpmStatus() {
|
if !self.checkPhpFpmStatus() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Alert.notify(
|
Alert.notify(
|
||||||
message: "alert.php_fpm_broken.title".localized,
|
message: "alert.php_fpm_broken.title".localized,
|
||||||
info: "alert.php_fpm_broken.info".localized
|
info: "alert.php_fpm_broken.info".localized,
|
||||||
|
style: .critical
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determine if PHP-FPM is configured correctly.
|
||||||
|
|
||||||
|
For PHP 5.6, we'll check if `valet.sock` is included in the main `php-fpm.conf` file, but for more recent
|
||||||
|
versions of PHP, we can just check for the existence of the `valet-fpm.conf` file. If the check here fails,
|
||||||
|
that means that Valet won't work properly.
|
||||||
|
*/
|
||||||
private func checkPhpFpmStatus() -> Bool {
|
private func checkPhpFpmStatus() -> Bool {
|
||||||
if self.version.short == "5.6" {
|
if self.version.short == "5.6" {
|
||||||
// The main PHP config file should contain `valet.sock` and then we're probably fine?
|
// The main PHP config file should contain `valet.sock` and then we're probably fine?
|
||||||
@@ -146,4 +170,5 @@ class PhpInstallation {
|
|||||||
var upload_max_filesize = "???"
|
var upload_max_filesize = "???"
|
||||||
var post_max_size = "???"
|
var post_max_size = "???"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -46,7 +46,7 @@ class PhpExtension {
|
|||||||
|
|
||||||
- Note: Extensions that are disabled in a different way will not be detected. This is intentional.
|
- Note: Extensions that are disabled in a different way will not be detected. This is intentional.
|
||||||
*/
|
*/
|
||||||
static let extensionRegex = #"^(extension=|zend_extension=|; extension=|; zend_extension=)(?<name>["]?(?:\/?.\/?)+(?:\.so)"?)$"#
|
static let extensionRegex = #"^(extension|zend_extension|;(\s?)extension|;(\s?)zend_extension)(\s?)(=)(\s?)(?<name>["]?(?:\/?.\/?)+(?:\.so)"?)$"#
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When registering an extension, we do that based on the line found inside the .ini file.
|
When registering an extension, we do that based on the line found inside the .ini file.
|
||||||
@@ -61,6 +61,7 @@ class PhpExtension {
|
|||||||
let fullPath = String(line[range])
|
let fullPath = String(line[range])
|
||||||
.replacingOccurrences(of: "\"", with: "") // replace excess "
|
.replacingOccurrences(of: "\"", with: "") // replace excess "
|
||||||
.replacingOccurrences(of: ".so", with: "") // replace excess .so
|
.replacingOccurrences(of: ".so", with: "") // replace excess .so
|
||||||
|
|
||||||
self.name = String(fullPath.split(separator: "/").last!) // take last segment
|
self.name = String(fullPath.split(separator: "/").last!) // take last segment
|
||||||
|
|
||||||
self.enabled = !line.contains(";")
|
self.enabled = !line.contains(";")
|
||||||
@@ -71,12 +72,15 @@ class PhpExtension {
|
|||||||
This simply toggles the extension in the .ini file. You may need to restart the other services in order for this change to apply.
|
This simply toggles the extension in the .ini file. You may need to restart the other services in order for this change to apply.
|
||||||
*/
|
*/
|
||||||
func toggle() {
|
func toggle() {
|
||||||
Actions.sed(
|
let newLine = enabled
|
||||||
file: file,
|
// DISABLED: Commented out line
|
||||||
original: line,
|
? "; \(line)"
|
||||||
replacement: enabled ? "; \(line)" : line.replacingOccurrences(of: "; ", with: "")
|
// ENABLED: Line where the comment delimiter (;) is removed
|
||||||
)
|
: line.replacingOccurrences(of: "; ", with: "")
|
||||||
enabled = !enabled
|
|
||||||
|
Actions.sed(file: file, original: line, replacement: newLine)
|
||||||
|
|
||||||
|
enabled.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Static Methods
|
// MARK: - Static Methods
|
||||||
@@ -93,11 +97,12 @@ class PhpExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return file!.components(separatedBy: "\n")
|
return file!.components(separatedBy: "\n")
|
||||||
.filter({ (line) -> Bool in
|
.filter {
|
||||||
return line.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
||||||
})
|
}
|
||||||
.map { (line) -> PhpExtension in
|
.map {
|
||||||
return PhpExtension(line, file: path.path)
|
return PhpExtension($0, file: path.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
33
phpmon/Domain/PHP/PhpInstallation.swift
Normal file
33
phpmon/Domain/PHP/PhpInstallation.swift
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// BrewPhpInstallation.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 28/11/2021.
|
||||||
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpInstallation {
|
||||||
|
|
||||||
|
var longVersion: String
|
||||||
|
var homebrewInfo: HomebrewPackage
|
||||||
|
|
||||||
|
init(_ version: String) {
|
||||||
|
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||||
|
self.longVersion = version
|
||||||
|
if Shell.fileExists(phpConfigExecutablePath) {
|
||||||
|
self.longVersion = Command.execute(
|
||||||
|
path: phpConfigExecutablePath,
|
||||||
|
arguments: ["--version"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let info = Shell.pipe("\(Paths.brew) info php@\(version) --json")
|
||||||
|
self.homebrewInfo = try! JSONDecoder().decode(
|
||||||
|
[HomebrewPackage].self,
|
||||||
|
from: info.data(using: .utf8)!
|
||||||
|
).first!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -9,39 +9,71 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum PreferenceName: String {
|
enum PreferenceName: String {
|
||||||
|
case wasLaunchedBefore = "launched_before"
|
||||||
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
||||||
|
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
||||||
|
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
||||||
case globalHotkey = "global_hotkey"
|
case globalHotkey = "global_hotkey"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Preferences {
|
class Preferences {
|
||||||
|
|
||||||
static func handleFirstTimeLaunch() {
|
// MARK: - Singleton
|
||||||
let launchedBefore = UserDefaults.standard.bool(forKey: "launched_before")
|
|
||||||
|
|
||||||
if launchedBefore {
|
static var shared = Preferences()
|
||||||
|
|
||||||
|
var cachedPreferences: [PreferenceName: Any?]
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
Preferences.handleFirstTimeLaunch()
|
||||||
|
self.cachedPreferences = Self.cache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - First Time Run
|
||||||
|
|
||||||
|
/**
|
||||||
|
Note: macOS seems to cache plist values in memory as well as in files.
|
||||||
|
You can find the persisted configuration file in: ~/Library/Preferences/com.nicoverbruggen.phpmon.plist
|
||||||
|
|
||||||
|
To clear the cache, and get a first-run experience you may need to run:
|
||||||
|
```
|
||||||
|
defaults delete com.nicoverbruggen.phpmon
|
||||||
|
killall cfprefsd
|
||||||
|
```
|
||||||
|
*/
|
||||||
|
static func handleFirstTimeLaunch() {
|
||||||
|
UserDefaults.standard.register(defaults: [
|
||||||
|
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
||||||
|
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
||||||
|
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true
|
||||||
|
])
|
||||||
|
|
||||||
|
if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
UserDefaults.standard.setValue(true, forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue)
|
|
||||||
UserDefaults.standard.setValue(true, forKey: "launched_before")
|
|
||||||
UserDefaults.standard.synchronize()
|
|
||||||
|
|
||||||
print("Saving first-time preferences!")
|
print("Saving first-time preferences!")
|
||||||
|
UserDefaults.standard.setValue(true, forKey: PreferenceName.wasLaunchedBefore.rawValue)
|
||||||
|
UserDefaults.standard.synchronize()
|
||||||
}
|
}
|
||||||
|
|
||||||
static func retrieve() -> [PreferenceName: Any] {
|
// MARK: - API
|
||||||
Preferences.handleFirstTimeLaunch()
|
|
||||||
|
|
||||||
return [
|
|
||||||
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(
|
|
||||||
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
|
||||||
.globalHotkey: UserDefaults.standard.string(
|
|
||||||
forKey: PreferenceName.globalHotkey.rawValue) as Any
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
static var preferences: [PreferenceName: Any?] {
|
static var preferences: [PreferenceName: Any?] {
|
||||||
return Preferences.retrieve()
|
return Self.shared.cachedPreferences
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Internal Functionality
|
||||||
|
|
||||||
|
static func cache() -> [PreferenceName: Any] {
|
||||||
|
return [
|
||||||
|
// Part 1: Always Booleans
|
||||||
|
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
||||||
|
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
||||||
|
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any,
|
||||||
|
|
||||||
|
// Part 2: Always Strings
|
||||||
|
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
static func update(_ preference: PreferenceName, value: Any?) {
|
static func update(_ preference: PreferenceName, value: Any?) {
|
||||||
@@ -51,6 +83,9 @@ class Preferences {
|
|||||||
UserDefaults.standard.setValue(value, forKey: preference.rawValue)
|
UserDefaults.standard.setValue(value, forKey: preference.rawValue)
|
||||||
}
|
}
|
||||||
UserDefaults.standard.synchronize()
|
UserDefaults.standard.synchronize()
|
||||||
|
|
||||||
|
// Update the preferences cache in memory!
|
||||||
|
Preferences.shared.cachedPreferences = Preferences.cache()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -12,17 +12,31 @@ import Carbon
|
|||||||
|
|
||||||
class PrefsVC: NSViewController {
|
class PrefsVC: NSViewController {
|
||||||
|
|
||||||
|
// Labels on the left
|
||||||
@IBOutlet weak var leftLabelDynamicIcon: NSTextField!
|
@IBOutlet weak var leftLabelDynamicIcon: NSTextField!
|
||||||
|
@IBOutlet weak var leftLabelServices: NSTextField!
|
||||||
@IBOutlet weak var leftLabelGlobalShortcut: NSTextField!
|
@IBOutlet weak var leftLabelGlobalShortcut: NSTextField!
|
||||||
|
|
||||||
|
// Dynamic icon
|
||||||
@IBOutlet weak var buttonDynamicIcon: NSButton!
|
@IBOutlet weak var buttonDynamicIcon: NSButton!
|
||||||
@IBOutlet weak var labelDynamicIcon: NSTextField!
|
@IBOutlet weak var labelDynamicIcon: NSTextField!
|
||||||
@IBOutlet weak var buttonClose: NSButton!
|
|
||||||
|
|
||||||
|
// Full PHP version
|
||||||
|
@IBOutlet weak var buttonDisplayFullPhpVersion: NSButton!
|
||||||
|
@IBOutlet weak var labelDisplayFullPhpVersion: NSTextField!
|
||||||
|
|
||||||
|
// Auto-restart services
|
||||||
|
@IBOutlet weak var buttonAutoRestartServices: NSButton!
|
||||||
|
@IBOutlet weak var labelAutoRestartServices: NSTextField!
|
||||||
|
|
||||||
|
// Shortcut
|
||||||
@IBOutlet weak var buttonSetShortcut: NSButton!
|
@IBOutlet weak var buttonSetShortcut: NSButton!
|
||||||
@IBOutlet weak var buttonClearShortcut: NSButton!
|
@IBOutlet weak var buttonClearShortcut: NSButton!
|
||||||
@IBOutlet weak var labelShortcut: NSTextField!
|
@IBOutlet weak var labelShortcut: NSTextField!
|
||||||
|
|
||||||
|
// Close button (bottom right)
|
||||||
|
@IBOutlet weak var buttonClose: NSButton!
|
||||||
|
|
||||||
// MARK: - Display
|
// MARK: - Display
|
||||||
|
|
||||||
public static func show(delegate: NSWindowDelegate? = nil) {
|
public static func show(delegate: NSWindowDelegate? = nil) {
|
||||||
@@ -47,6 +61,7 @@ class PrefsVC: NSViewController {
|
|||||||
override func viewWillAppear() {
|
override func viewWillAppear() {
|
||||||
loadLocalization()
|
loadLocalization()
|
||||||
loadDynamicIconFromPreferences()
|
loadDynamicIconFromPreferences()
|
||||||
|
loadFullPhpVersionFromPreferences()
|
||||||
loadGlobalKeybindFromPreferences()
|
loadGlobalKeybindFromPreferences()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +77,15 @@ class PrefsVC: NSViewController {
|
|||||||
labelDynamicIcon.stringValue = "prefs.dynamic_icon_desc".localized
|
labelDynamicIcon.stringValue = "prefs.dynamic_icon_desc".localized
|
||||||
buttonDynamicIcon.title = "prefs.dynamic_icon_title".localized
|
buttonDynamicIcon.title = "prefs.dynamic_icon_title".localized
|
||||||
|
|
||||||
|
// Full PHP version
|
||||||
|
buttonDisplayFullPhpVersion.title = "prefs.display_full_php_version".localized
|
||||||
|
labelDisplayFullPhpVersion.stringValue = "prefs.display_full_php_version_desc".localized
|
||||||
|
|
||||||
|
// Services
|
||||||
|
leftLabelServices.stringValue = "prefs.services".localized
|
||||||
|
buttonAutoRestartServices.title = "prefs.auto_restart_services_title".localized
|
||||||
|
labelAutoRestartServices.stringValue = "prefs_auto_restart_services_desc".localized
|
||||||
|
|
||||||
// Global Shortcut
|
// Global Shortcut
|
||||||
leftLabelGlobalShortcut.stringValue = "prefs.global_shortcut".localized
|
leftLabelGlobalShortcut.stringValue = "prefs.global_shortcut".localized
|
||||||
labelShortcut.stringValue = "prefs.shortcut_desc".localized
|
labelShortcut.stringValue = "prefs.shortcut_desc".localized
|
||||||
@@ -72,13 +96,40 @@ class PrefsVC: NSViewController {
|
|||||||
buttonClose.title = "prefs.close".localized
|
buttonClose.title = "prefs.close".localized
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Dynamic Icon Preference
|
// MARK: - Loading Preferences
|
||||||
|
|
||||||
func loadDynamicIconFromPreferences() {
|
func loadDynamicIconFromPreferences() {
|
||||||
let shouldDisplay = Preferences.preferences[.shouldDisplayDynamicIcon] as! Bool == true
|
let shouldDisplay = Preferences.preferences[.shouldDisplayDynamicIcon] as! Bool == true
|
||||||
self.buttonDynamicIcon.state = shouldDisplay ? .on : .off
|
self.buttonDynamicIcon.state = shouldDisplay ? .on : .off
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadFullPhpVersionFromPreferences() {
|
||||||
|
let shouldDisplay = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool == true
|
||||||
|
self.buttonDisplayFullPhpVersion.state = shouldDisplay ? .on : .off
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadAutoRestartServicesFromPreferences() {
|
||||||
|
let shouldDisplay = Preferences.preferences[.autoServiceRestartAfterExtensionToggle] as! Bool == true
|
||||||
|
self.buttonAutoRestartServices.state = shouldDisplay ? .on : .off
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Actions
|
||||||
|
|
||||||
|
@IBAction func toggledDynamicIcon(_ sender: Any) {
|
||||||
|
Preferences.update(.shouldDisplayDynamicIcon, value: buttonDynamicIcon.state == .on)
|
||||||
|
MainMenu.shared.refreshIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func toggledFullPhpVersion(_ sender: Any) {
|
||||||
|
Preferences.update(.fullPhpVersionDynamicIcon, value: buttonDisplayFullPhpVersion.state == .on)
|
||||||
|
MainMenu.shared.refreshIcon()
|
||||||
|
MainMenu.shared.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func toggledAutoRestartServices(_ sender: Any) {
|
||||||
|
Preferences.update(.autoServiceRestartAfterExtensionToggle, value: buttonAutoRestartServices.state == .on)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Shortcut Preference
|
// MARK: - Shortcut Preference
|
||||||
// Adapted from: https://dev.to/mitchartemis/creating-a-global-configurable-shortcut-for-macos-apps-in-swift-25e9
|
// Adapted from: https://dev.to/mitchartemis/creating-a-global-configurable-shortcut-for-macos-apps-in-swift-25e9
|
||||||
|
|
||||||
@@ -166,13 +217,6 @@ class PrefsVC: NSViewController {
|
|||||||
buttonSetShortcut.title = globalKeybindPreference.description
|
buttonSetShortcut.title = globalKeybindPreference.description
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Actions
|
|
||||||
|
|
||||||
@IBAction func toggledDynamicIcon(_ sender: Any) {
|
|
||||||
Preferences.update(.shouldDisplayDynamicIcon, value: buttonDynamicIcon.state == .on)
|
|
||||||
MainMenu.shared.refreshIcon()
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func pressed(_ sender: Any) {
|
@IBAction func pressed(_ sender: Any) {
|
||||||
self.view.window?.windowController?.close()
|
self.view.window?.windowController?.close()
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ struct Keys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PrefsWC: NSWindowController {
|
class PrefsWC: NSWindowController {
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
}
|
}
|
||||||
|
@@ -34,7 +34,7 @@ class Command {
|
|||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -64,4 +64,5 @@ class Paths {
|
|||||||
public static var etcPath: String {
|
public static var etcPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/etc"
|
return "\(shared.baseDir.rawValue)/etc"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -21,7 +21,7 @@ class Shell {
|
|||||||
|
|
||||||
// MARK: - Singleton
|
// MARK: - Singleton
|
||||||
|
|
||||||
var shell = "/bin/sh"
|
var shell: String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Determine if we're using macOS Catalina or newer (that support /bin/zsh as default shell)
|
// Determine if we're using macOS Catalina or newer (that support /bin/zsh as default shell)
|
||||||
@@ -29,9 +29,14 @@ class Shell {
|
|||||||
.init(majorVersion: 10, minorVersion: 15, patchVersion: 0))
|
.init(majorVersion: 10, minorVersion: 15, patchVersion: 0))
|
||||||
|
|
||||||
// If macOS Mojave is being used, we'll default to /bin/bash
|
// If macOS Mojave is being used, we'll default to /bin/bash
|
||||||
shell = at_least_10_15 ? "/bin/sh" : "/bin/bash"
|
shell = at_least_10_15
|
||||||
print(at_least_10_15 ? "Detected recent macOS (> 10.15): defaulting to /bin/sh"
|
? "/bin/sh"
|
||||||
: "Detected older macOS (< 10.15): so defaulting to /bin/bash")
|
: "/bin/bash"
|
||||||
|
|
||||||
|
print(at_least_10_15
|
||||||
|
? "Detected recent macOS (> 10.15): defaulting to /bin/sh"
|
||||||
|
: "Detected older macOS (< 10.15): defaulting to /bin/bash"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,25 +63,31 @@ class Shell {
|
|||||||
*/
|
*/
|
||||||
func pipe(_ command: String) -> String {
|
func pipe(_ command: String) -> String {
|
||||||
let task = Process()
|
let task = Process()
|
||||||
let pipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
task.launchPath = self.shell
|
task.launchPath = self.shell
|
||||||
task.arguments = ["--login", "-c", command]
|
task.arguments = ["--login", "-c", command]
|
||||||
task.standardOutput = pipe
|
task.standardOutput = outputPipe
|
||||||
|
task.standardError = errorPipe
|
||||||
task.launch()
|
task.launch()
|
||||||
|
|
||||||
return String(
|
let error = String(data: errorPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
|
||||||
data: pipe.fileHandleForReading.readDataToEndOfFile(),
|
let output = String(data: outputPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)!
|
||||||
encoding: .utf8
|
|
||||||
)!
|
if (output == "" && error.lengthOfBytes(using: .utf8) > 0) {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a file exists at the provided path.
|
Checks if a file exists at the provided path.
|
||||||
|
Uses `/bin/echo` instead of the `builtin` (which does not support `-n`).
|
||||||
*/
|
*/
|
||||||
public static func fileExists(_ path: String) -> Bool {
|
public static func fileExists(_ path: String) -> Bool {
|
||||||
return Shell.pipe(
|
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||||
"if [ -f \(path) ]; then echo \"PHP_Y_FE\"; fi"
|
|
||||||
).contains("PHP_Y_FE")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -52,10 +52,17 @@
|
|||||||
|
|
||||||
"prefs.global_shortcut" = "Global shortcut:";
|
"prefs.global_shortcut" = "Global shortcut:";
|
||||||
"prefs.dynamic_icon" = "Dynamic icon:";
|
"prefs.dynamic_icon" = "Dynamic icon:";
|
||||||
|
"prefs.services" = "Services:";
|
||||||
|
|
||||||
|
"prefs.auto_restart_services_title" = "Auto-restart PHP-FPM";
|
||||||
|
"prefs_auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when\nyou check or uncheck an extension. Slightly slower when enabled, \nbut this applies the extension change immediately for all sites \nyou're serving, no need to restart PHP-FPM manually.";
|
||||||
|
|
||||||
"prefs.dynamic_icon_title" = "Display dynamic icon in menu bar";
|
"prefs.dynamic_icon_title" = "Display dynamic icon in menu bar";
|
||||||
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible.\nIf checked, it will display the major version number of the\ncurrently linked PHP version.";
|
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible.\nIf checked, it will display the major version number of the\ncurrently linked PHP version.";
|
||||||
|
|
||||||
|
"prefs.display_full_php_version" = "Display full PHP version in menu bar";
|
||||||
|
"prefs.display_full_php_version_desc" = "Display the full version instead of the major version only.\n(This may be undesirable on smaller displays,\nso this is disabled by default.)";
|
||||||
|
|
||||||
"prefs.shortcut_set" = "Set global shortcut";
|
"prefs.shortcut_set" = "Set global shortcut";
|
||||||
"prefs.shortcut_listening" = "<listening for keypress>";
|
"prefs.shortcut_listening" = "<listening for keypress>";
|
||||||
"prefs.shortcut_clear" = "Clear";
|
"prefs.shortcut_clear" = "Clear";
|
||||||
@@ -64,7 +71,10 @@
|
|||||||
// NOTIFICATIONS
|
// NOTIFICATIONS
|
||||||
|
|
||||||
"notification.version_changed_title" = "PHP %@ now active";
|
"notification.version_changed_title" = "PHP %@ now active";
|
||||||
"notification.version_changed_desc" = "PHP Monitor has finished the switch to PHP %@.";
|
"notification.version_changed_desc" = "PHP Monitor has switched to PHP %@.";
|
||||||
|
|
||||||
|
"notification.php_fpm_restarted" = "PHP-FPM automatically restarted";
|
||||||
|
"notification.php_fpm_restarted_desc" = "You toggled an extension, so PHP-FPM was automatically restarted.";
|
||||||
|
|
||||||
"notification.services_stopped" = "Valet services stopped";
|
"notification.services_stopped" = "Valet services stopped";
|
||||||
"notification.services_stopped_desc" = "All services have been successfully stopped.";
|
"notification.services_stopped_desc" = "All services have been successfully stopped.";
|
||||||
@@ -92,6 +102,10 @@
|
|||||||
"alert.cannot_start.close" = "Close";
|
"alert.cannot_start.close" = "Close";
|
||||||
"alert.cannot_start.retry" = "Retry";
|
"alert.cannot_start.retry" = "Retry";
|
||||||
|
|
||||||
|
// PHP alias issue
|
||||||
|
"alert.php_alias_conflict.title" = "Homebrew `php` formula alias conflict detected";
|
||||||
|
"alert.php_alias_conflict.info" = "PHP Monitor has detected conflicting `php` aliases in your Homebrew setup, both of which have been detected as installed.\n\nThis will likely result in failed linking when switching PHP versions, and will break PHP Monitor functionality.\n\nFor more information, please visit: https://github.com/nicoverbruggen/phpmon/issues/54";
|
||||||
|
|
||||||
// STARTUP
|
// STARTUP
|
||||||
|
|
||||||
/// 1. PHP binary not found
|
/// 1. PHP binary not found
|
||||||
|
Reference in New Issue
Block a user