Compare commits
957 Commits
Author | SHA1 | Date | |
---|---|---|---|
ff61d8c52e | |||
da41673855 | |||
5bda727981 | |||
d3053b8fe3 | |||
7159ca8612 | |||
141c06d14b | |||
d714d7ad4c | |||
07b17f3f84 | |||
7f0f7ff3e9 | |||
c7c143c760 | |||
8790b30706 | |||
c42188b717 | |||
cc251686f9 | |||
8a46b9d374 | |||
a62ebcff92 | |||
541378f3f9 | |||
e6f1d7e834 | |||
20d19f2f92 | |||
91bc347e57 | |||
e05300b25b | |||
1ae7a20870 | |||
5594130ccd | |||
b9c7cdb3cc | |||
00b4760b85 | |||
9a35014d2a | |||
7cba25b52e | |||
c6c3996c7b | |||
03c96a1d16 | |||
a6fa4b240f | |||
7e78026d06 | |||
d5888c1c7a | |||
e40b9fe45a | |||
f5d0ad20cd | |||
0615927f2f | |||
3d1806c094 | |||
8a57557074 | |||
19f4819450 | |||
aa8309dd9a | |||
7977a4e177 | |||
51c100f6fe | |||
aebfc9dd09 | |||
f9acbd34d0 | |||
eb566bb523 | |||
528f213f17 | |||
f8e6aa988e | |||
93e841735e | |||
cb28243181 | |||
fc68e37458 | |||
ae6736102a | |||
3ef1a6e60d | |||
5e7c7bc903 | |||
94f3c1c7c5 | |||
20aad90ba9 | |||
8bd85d8354 | |||
90b068d200 | |||
943b5aa6af | |||
4bf475bae2 | |||
125b9bb198 | |||
72cbf6996d | |||
e7cc940f65 | |||
c8323a8c27 | |||
6805855f03 | |||
db101f5a66 | |||
2302d5a5ee | |||
5cfb0f452c | |||
7da20b4f20 | |||
f1b037ce26 | |||
e59347ed7f | |||
206dff289f | |||
02f579fe81 | |||
2a74b11462 | |||
371f98b875 | |||
7955c777e7 | |||
5c9b06d83b | |||
3c7bed0a9b | |||
54f83a0aed | |||
b041ca37be | |||
2b2b027317 | |||
cdbd959159 | |||
6fc613ac4c | |||
8240b676c1 | |||
cbebf75b48 | |||
40c24793f5 | |||
6a921d8e3e | |||
a3368effec | |||
7f4c6878e4 | |||
82626b7174 | |||
326e5c58e2 | |||
2848b4dcd2 | |||
a8cf6daa94 | |||
c4749673c9 | |||
20a0059f73 | |||
402e65f82d | |||
29d17b3880 | |||
7f04dd5fcb | |||
0fe9281e3c | |||
f88035b425 | |||
2b7bb3f352 | |||
c7ee4b8838 | |||
ab993efbde | |||
c6f49de70c | |||
8b79dc44d0 | |||
458b952787 | |||
67ec63212c | |||
104f3a7d8c | |||
d2304323fe | |||
76d078735c | |||
1eeba747cf | |||
64c259d804 | |||
b307251f81 | |||
bf610b6c4e | |||
518fb16f23 | |||
24ef7eacfe | |||
050c154894 | |||
18496de104 | |||
a21418a608 | |||
f9ee63ddf6 | |||
b7de54dfa7 | |||
2d0deed4fd | |||
4b1fc1a5ce | |||
227131bd7e | |||
f48a265bbf | |||
0c3b68734c | |||
7e5cbadc09 | |||
9f608439fc | |||
8b0aeef2e6 | |||
aa406434d0 | |||
4bca47a6d9 | |||
c5e8c4c4a6 | |||
85d7c8f9a3 | |||
4c7361e635 | |||
6413287606 | |||
ff8eb4fa04 | |||
6b72d4da65 | |||
b456fdc65c | |||
0e3c9a5a68 | |||
7cf8d4697f | |||
34e5a97155 | |||
ff2c2c9b69 | |||
d320c49092 | |||
966033e052 | |||
4e095a5ae5 | |||
6aff283d08 | |||
9c8da2aa1c | |||
5755d608f8 | |||
29fcc66cba | |||
3a826b7e51 | |||
2d8ad9e9bc | |||
64b3c4e9bb | |||
f3b3dcf449 | |||
0bdbc0a056 | |||
4f11f3d8d3 | |||
e1eb61859e | |||
2939a2ab28 | |||
08dcfb36f4 | |||
f8b605f749 | |||
8f1304308d | |||
7e04f8b881 | |||
9dae03a04e | |||
78c24555f7 | |||
41af058661 | |||
7c192730e1 | |||
bb56e33ee8 | |||
84902f3a42 | |||
8775e70178 | |||
de4cefd1b9 | |||
ebb04001d0 | |||
bd23f65668 | |||
22295ed55a | |||
2bd5b8f79e | |||
34e9e3f829 | |||
016f36a8fd | |||
862add8512 | |||
6dabcd7668 | |||
7b7a5e5236 | |||
a6aecff557 | |||
81ed154db1 | |||
e0b574b33d | |||
f414c723e4 | |||
f3ef1da2bf | |||
f7c716096c | |||
54630c222b | |||
1fc63e0471 | |||
127d5f4494 | |||
13ee618d5c | |||
ed1d7f8aed | |||
063a729d67 | |||
d1498eb070 | |||
7a02fb8a1a | |||
b925262620 | |||
aaa814ac9c | |||
870868bacf | |||
e149a2500e | |||
aef7d4021c | |||
a25f8c9748 | |||
050c489158 | |||
61af6e2dc0 | |||
130bf38dd9 | |||
a6c3f0ceba | |||
defb6e357a | |||
f274e9e004 | |||
a07881a987 | |||
ae9863b962 | |||
d679c7e75c | |||
47d921547f | |||
bc22129399 | |||
98fcb686bf | |||
715b674929 | |||
089ebe7b4e | |||
608525209b | |||
c7eb1d5ce5 | |||
81eb2fee90 | |||
2eea15aac2 | |||
eb664477f9 | |||
e1adcbcde6 | |||
cb504cc3f0 | |||
7c08a2fdfe | |||
3f14754177 | |||
2f47610c85 | |||
b5758dfca7 | |||
2d4112708e | |||
cc6324b692 | |||
15d75a7f98 | |||
32a44524ef | |||
c7c5311ff9 | |||
26a097ed07 | |||
c93f047909 | |||
f82a2120f7 | |||
857cba9f45 | |||
b0de0c04c6 | |||
ec49257bcc | |||
5fc159ae5a | |||
9c6a21008a | |||
f27e07fc78 | |||
9fceab3e2e | |||
67a91e1211 | |||
babb734c25 | |||
5dffbf57d1 | |||
21a1d6576e | |||
6456741880 | |||
b08912ce11 | |||
f6d2f09b8d | |||
7285d24ef3 | |||
ac60c66bb9 | |||
03fdf23f0a | |||
412b7bad5c | |||
9a7575790a | |||
a36487f6e0 | |||
cd5cbccb04 | |||
e7e8658ea6 | |||
20291bf034 | |||
762527ece9 | |||
2c40f433d3 | |||
5ac4817048 | |||
300880f3e5 | |||
78e682688b | |||
208a430066 | |||
5c92d47ff0 | |||
121a227510 | |||
f8642b21e6 | |||
b182218cad | |||
0bb3e5c173 | |||
e541dced4a | |||
04c6cef277 | |||
6d1ea4e93b | |||
4fd48baf63 | |||
1260022d51 | |||
ca72b79924 | |||
1a17a275d4 | |||
202f9ed9d1 | |||
881d863bb8 | |||
ef37876508 | |||
0c52720e55 | |||
744ec95630 | |||
69ff907f4a | |||
57c90c216f | |||
8df126a7b0 | |||
173206bed9 | |||
8fa270fd54 | |||
92509b5a84 | |||
c609022c9b | |||
bbbbe7555b | |||
1216fe4974 | |||
147407666d | |||
f9363dd35b | |||
7be6a335df | |||
b39b4dc58b | |||
3ceded3456 | |||
4568f03a65 | |||
2eda8d6382 | |||
6d89f94c92 | |||
ff2eff2a75 | |||
339eafd34d | |||
6115ef02de | |||
dd330fecce | |||
aaa7c636db | |||
ff75fb7be3 | |||
4d7b01831b | |||
8fcaa34cbb | |||
0fceb852bb | |||
9fb5f33770 | |||
2f658ee569 | |||
ad179a325a | |||
5fa1836693 | |||
4baeaea85f | |||
9e25254ec8 | |||
24aecb3148 | |||
e623207844 | |||
f60ecb877c | |||
25f824defd | |||
7936d14440 | |||
c3261b8873 | |||
66393094b0 | |||
b5d2fef184 | |||
44bc07c9dc | |||
5923be099f | |||
f92a3f545a | |||
93790f3951 | |||
d3b1afe9fd | |||
2fa50a7dc4 | |||
b8e7397233 | |||
18b62ecc3f | |||
5c62f744ad | |||
70ebb2ef59 | |||
d9f4a19b92 | |||
90c8bcc0df | |||
1ef4f0bb81 | |||
0f62b1f1d0 | |||
2d59b8c6e8 | |||
450d7ec001 | |||
b6b1174ca3 | |||
e509f6b59d | |||
6014320441 | |||
a0d80423e9 | |||
89642de12e | |||
c6f2167c92 | |||
e8d705e228 | |||
35b19efc3e | |||
a46602d4b4 | |||
9a175e7291 | |||
d53e92ce94 | |||
3344fdb1dc | |||
7c3f416789 | |||
8d42e27ef6 | |||
020a0260f1 | |||
3ce7e8f48b | |||
894365488a | |||
3c6d2d74ff | |||
ae7e13de9b | |||
c82ea1fac1 | |||
17efb50872 | |||
d6258f54a9 | |||
44800a03a1 | |||
e6d2c873a5 | |||
300b10c5d8 | |||
6eea08cd4f | |||
3c946a53e8 | |||
d8738b685f | |||
55fc90bcf5 | |||
6a2f4d248c | |||
a50eb04f3c | |||
4cbfbeb4e5 | |||
18dd597d38 | |||
e5c80ab52f | |||
0b3a83c1e4 | |||
6e7c0d827c | |||
d05f39efe7 | |||
27894e4884 | |||
f153fee05c | |||
71e1ed1b93 | |||
61ecefb6e7 | |||
422a7738bd | |||
0beda388eb | |||
684a53fc4a | |||
456948ffd9 | |||
a090cbc20b | |||
7b07520440 | |||
6d52992c9d | |||
291d20b2b3 | |||
3d505aebde | |||
d854dee2a1 | |||
67e8589834 | |||
a36c9b1563 | |||
df1b1c5856 | |||
5af7214320 | |||
e20d3ffd22 | |||
be70559d4c | |||
61988a141b | |||
3bcf52bf0a | |||
296bc486c4 | |||
59f60b5013 | |||
de2c1aca5d | |||
44c1ea7de4 | |||
923f0237e8 | |||
ff7c68ddfb | |||
bf2c0c259f | |||
0fdd1264ed | |||
c85a8e3818 | |||
15fe5e4716 | |||
de6dea066e | |||
ee230f3086 | |||
bc96b50630 | |||
d6554ceea9 | |||
4b04f70638 | |||
d49e74fab1 | |||
e34dadcb9b | |||
a696a9a386 | |||
837392d606 | |||
912d9e7423 | |||
cb98d40bef | |||
1f165058b2 | |||
04c78eba35 | |||
4bfde7b062 | |||
00cc6711a1 | |||
209244e162 | |||
e48d8ed98b | |||
834f76e5a1 | |||
5f39cd757a | |||
7e48893247 | |||
073b7cf943 | |||
31a0bb986f | |||
81183acea8 | |||
e29ca9e1ad | |||
40e05d9445 | |||
3ebf51b319 | |||
3f8739dc30 | |||
c3e55df9fb | |||
4b8ad911f1 | |||
c6aa06842c | |||
efd902b4f3 | |||
918e272da7 | |||
272a9182d3 | |||
63f85aff91 | |||
a801174f0a | |||
fa5c843619 | |||
581c2d1974 | |||
bc739e1982 | |||
a21d928a6c | |||
4deef64537 | |||
bedabaa3bb | |||
76d96b3507 | |||
4de7179d1c | |||
f2d5b94831 | |||
ff5fdd82b1 | |||
4f6bae87d4 | |||
ce44166b48 | |||
5caca85d7a | |||
fa2d2105c5 | |||
8417d637fe | |||
e8c85f93f9 | |||
24659d4385 | |||
786b59aa92 | |||
9da3772212 | |||
e62b03d070 | |||
9a11d2efed | |||
b134e62328 | |||
771f8dc757 | |||
e18db4eadd | |||
1ece5c34bf | |||
04ed29bc9f | |||
ac2184ba97 | |||
5c69133c42 | |||
8310cf2729 | |||
696f9bf351 | |||
17b1329d71 | |||
4bfa98fc20 | |||
658cec27c1 | |||
1c0d9f6826 | |||
507d7d5b23 | |||
4b8b46a822 | |||
ea5dd3bc46 | |||
83657fee6f | |||
4c752b6a15 | |||
5e3e0c087b | |||
273070ef27 | |||
cb3208c008 | |||
d401fe997d | |||
eaf6ef658f | |||
d91e16d674 | |||
008603b8c3 | |||
728274aaca | |||
daaece3cfa | |||
eaa74b7141 | |||
8a6656d3e2 | |||
ad46f51d73 | |||
12a4efc775 | |||
ec4c4df5fd | |||
f4448e0640 | |||
7fd30d7c54 | |||
2c57dea97f | |||
a77fa5557a | |||
b0f27dcfa5 | |||
f5d2ec2b7e | |||
6db5cdec25 | |||
03cf4ef3e4 | |||
6feb8118d9 | |||
45a82b2c9e | |||
ed3622cc4e | |||
e2ab7f08ed | |||
ad41e3891e | |||
108ae05c1d | |||
fb1ca60240 | |||
3267dc8add | |||
1fd7db15a7 | |||
0b33116eb0 | |||
2c25bcbdb5 | |||
8a6139d5e7 | |||
953ccb3792 | |||
c26c491340 | |||
c9a5cd3a9f | |||
45704fc736 | |||
f28354e634 | |||
86eb295489 | |||
572330eaa1 | |||
5ebafdb4e3 | |||
ffffcad84b | |||
8055a32bde | |||
4c11fae541 | |||
99da328921 | |||
bbac2632a2 | |||
513a86ec39 | |||
5b3054326e | |||
e7f3c7e59c | |||
a59efb7fce | |||
3483569410 | |||
5399bddfeb | |||
a407515534 | |||
a682d0cfb0 | |||
b827ffb869 | |||
bdb718598e | |||
ddfc73e033 | |||
cfae520984 | |||
e871a00490 | |||
1d396202db | |||
39769d815f | |||
90a69338f7 | |||
3f25759d4f | |||
4494a0555f | |||
0d86f3ded6 | |||
0c176493e5 | |||
71da62f954 | |||
d6781568a3 | |||
fc27131cca | |||
c9c7e14416 | |||
bb124bd0ee | |||
c35e7781f4 | |||
0947dc5ecc | |||
286cdd00e9 | |||
61528cea46 | |||
883f5a1a5d | |||
42b79d3cb3 | |||
36aa41568c | |||
95729c5315 | |||
f02e45486e | |||
6ddddc744a | |||
273c51f702 | |||
186f80c90e | |||
d93af814c9 | |||
f4885f7dbc | |||
805c9f5e6a | |||
183d0bbc30 | |||
4855c14d28 | |||
588398ea76 | |||
d2502cfba2 | |||
8d46fa8a4e | |||
e59b89ea49 | |||
18b103bf9c | |||
9b8de47f5d | |||
da2934c2e5 | |||
9de4fc6712 | |||
4f4c950349 | |||
331ca8a9a4 | |||
87e08e4607 | |||
845235a276 | |||
7587126a42 | |||
f4b1e0745a | |||
c7bb4c1d37 | |||
a17512bfad | |||
a85e016b4a | |||
1292e91b33 | |||
8c55fee18d | |||
663082d725 | |||
fcdb4a8993 | |||
9134f08ec9 | |||
b281df3bbd | |||
a9f9c38e0d | |||
bbbdce6b44 | |||
237185995d | |||
48f950fc3a | |||
2ee0934080 | |||
cfbb83976d | |||
4e5b178e36 | |||
0c59d14da5 | |||
2a9c8e6830 | |||
78e750d764 | |||
c1c7561361 | |||
f90925ee17 | |||
ccfb15c83c | |||
bcc80b5210 | |||
023043a81d | |||
a2c93833df | |||
8b6267f411 | |||
1bff75311b | |||
7a580eef0c | |||
2306529936 | |||
08fdcdbc6c | |||
8cb8e5e409 | |||
6094f83e98 | |||
cdb4b60487 | |||
ae5bed2532 | |||
f098ffbf3d | |||
0a55b45c60 | |||
418d1e2479 | |||
fa3ec2aaa3 | |||
a1df2deec5 | |||
8fb43f7a16 | |||
8a8d32cb5d | |||
6e3bb1d322 | |||
cac7048d92 | |||
1aa051e12c | |||
58fd045bdf | |||
2fc71303df | |||
2c61d991d6 | |||
beca09b76f | |||
5c3e856a7b | |||
6fbbd499f8 | |||
1066bdc653 | |||
4ef5918b7a | |||
e45db50f48 | |||
8a7f045bb2 | |||
10272f3d7e | |||
96ce462021 | |||
8b635f2a14 | |||
f8f3fd5c9b | |||
6801283597 | |||
cf03727dc0 | |||
25a7dded73 | |||
c5fd43312f | |||
ea7572ffbb | |||
4d5b5de84b | |||
721bb32087 | |||
1fe086d53e | |||
347a9b7345 | |||
e0c087dbcb | |||
fd34deb7bd | |||
ccb0d96453 | |||
d821968a49 | |||
7d7a38546a | |||
30059353fe | |||
090440abc8 | |||
ceeba611d0 | |||
f1feb11baa | |||
90a02f364a | |||
6b8c68b058 | |||
fb34ea7c89 | |||
507d4785aa | |||
0c0045aead | |||
1fdf687c15 | |||
1040bcd037 | |||
655cd52ac4 | |||
2229f01eb0 | |||
f95eb7023f | |||
320e1ad3c5 | |||
e1880d9dfc | |||
998bbd231a | |||
fbf2158488 | |||
94df551b4b | |||
01288154a7 | |||
29b4fe2962 | |||
5907d9f689 | |||
86d74619b1 | |||
0c09e808bd | |||
da8659adba | |||
bbebe78997 | |||
19aa804cbb | |||
64491c6fe1 | |||
382cb177be | |||
e9f0d19d9a | |||
7709cd9f6c | |||
83eac7bf04 | |||
db8197df3d | |||
40e404fe24 | |||
e7f80ebce8 | |||
990152d77d | |||
2e61479c75 | |||
0579ebb1c1 | |||
f9df86851c | |||
b0c62e226a | |||
1392b6e4a0 | |||
12163bc87b | |||
c645bb7610 | |||
e6574966da | |||
1e9cfff05e | |||
bd34c2b255 | |||
c040ac3200 | |||
6c6888c9cb | |||
78cb6922b3 | |||
c16377c688 | |||
540ea5c310 | |||
4ba2b25f18 | |||
81b75dcaa8 | |||
884784d024 | |||
e81ff2870d | |||
7c631099b2 | |||
f7a98b88a7 | |||
3fc21fff2a | |||
0306c2b726 | |||
9d822df54e | |||
f413b84a45 | |||
b82811e6bf | |||
af922664ab | |||
8b73e69495 | |||
29a9e14741 | |||
997fb27596 | |||
c171df0a93 | |||
1c15a4e07f | |||
5067c7b87f | |||
f679231ade | |||
f725e09f55 | |||
99881bf4cd | |||
2987464da8 | |||
4d04275c57 | |||
790f63e8c9 | |||
86b49812c3 | |||
ef9e0fd916 | |||
af8807f799 | |||
4eea13f059 | |||
ba93ed93e4 | |||
932a0fe176 | |||
3cff2d6469 | |||
df506e4128 | |||
eb80214785 | |||
80a4e361a4 | |||
2af88b2bee | |||
5048ccab8c | |||
66d13c92d5 | |||
836b076da9 | |||
1a75838a3b | |||
a18b7962a7 | |||
84548634ec | |||
419ebe61f7 | |||
c45817b127 | |||
2c0c0c5a11 | |||
1b8d6311ba | |||
f0f7a3f7d6 | |||
8304d774c3 | |||
faeea4e866 | |||
6470daf7d3 | |||
94139a3669 | |||
f3b1172d0e | |||
8057019898 | |||
9b59fc5dae | |||
75f4377de8 | |||
d3657716c4 | |||
a13990b96f | |||
4c7aa7fead | |||
32278533bf | |||
39908f7fc5 | |||
347d79c88d | |||
9bc117e9f5 | |||
ba5fbed9be | |||
211556d5ce | |||
066d7bc217 | |||
f072ceae37 | |||
273dac1ca7 | |||
f3b170ba14 | |||
78d8030ed6 | |||
a300d2f4cf | |||
96a658073e | |||
8395ba407d | |||
f80e3fed2b | |||
b48edf7409 | |||
e0a0eb089d | |||
60b126333d | |||
649a3f4fb5 | |||
9a326928f3 | |||
52f87ca18a | |||
ad2d2cb57f | |||
22c0021ada | |||
2cfc5731fb | |||
1d74e1536c | |||
e6b2ddf2ad | |||
62fda6224e | |||
083f8ebec8 | |||
aec8fb1168 | |||
d5d9b38ed6 | |||
9a6975e3d9 | |||
d1c40f2eb5 | |||
ad58661449 | |||
1d6cfd419e | |||
15182ea15a | |||
c43e00c88d | |||
25c7004368 | |||
02ba57cd64 | |||
c2dc6302c0 | |||
af9f30a123 | |||
28c5754800 | |||
48c1d48573 | |||
582bf0e12c | |||
46b30bbff4 | |||
372011ca08 | |||
7255792910 | |||
0c96b11b05 | |||
ea4da12d3b | |||
8419ebad10 | |||
09a5cf836a | |||
1a1a53b472 | |||
a8bad8447a | |||
ca8f5a8fbe | |||
a0e7aec228 | |||
26badc759e | |||
e21c2168ea | |||
589ab3664e | |||
48b4f9b160 | |||
139e416c3b | |||
ba4ed3b365 | |||
06a8022265 | |||
3b297e07dc | |||
68fa8e523e | |||
768bf06a9d | |||
6a8d66758a | |||
078e6e6f23 | |||
3f80bfb641 | |||
a34389c3a9 | |||
692d3c143f | |||
bc86a45925 | |||
2a412b794a | |||
bf673263d8 | |||
ce498d3019 | |||
e398f089af | |||
e8ba24e48b | |||
e0f40be188 | |||
42848764cf | |||
46ac0c339c | |||
a0fe68f3ab | |||
c952c3d031 | |||
c05cdeda72 | |||
ae7b285eb0 | |||
6b3c562af2 | |||
e3ae878cae | |||
dd43c94e6e | |||
0e8fe1fcfb | |||
293b7f564e | |||
634ffb4c57 | |||
9468a2e9f8 | |||
921ecd99d6 | |||
d06f92051d | |||
97d68f89f1 | |||
115863f1ee | |||
bc27a4d25a | |||
5a0b2f319b | |||
50f003a2bd | |||
bc0b50f5bf | |||
dd20b25900 | |||
d0469467ac | |||
61427ec505 | |||
6cd1d78572 | |||
0ad5049785 | |||
b5c1960260 | |||
e6fbe7c4a4 | |||
537536d443 | |||
f702d14749 | |||
74bb544f3c | |||
dae47e3779 | |||
dc91d0e00c | |||
6187eb3e4e | |||
0fa6d337f2 | |||
a10e1cad11 | |||
7c252deede | |||
224c5c4fe2 | |||
e945b5fe94 | |||
23e534c5c9 | |||
dcddf74830 | |||
9baf69394e | |||
2e58bbb5e8 | |||
bb6abc5e9a | |||
e4f9d491c3 | |||
1ba7ee37db | |||
34cfbae3a9 | |||
9fabda545f | |||
7cd50aed7b | |||
cdc082071d | |||
d1479672c8 | |||
ffb112cfb2 | |||
f5af33c098 | |||
0f464f5814 | |||
776c2095e6 | |||
82a2833161 | |||
b14d7bdf07 | |||
ffa22eea25 | |||
8220e6409d | |||
927e1c02fa | |||
a13a17b106 | |||
18d6d73f94 | |||
54d101acbe | |||
76f720cb71 | |||
033e485ce1 | |||
82ee830de1 | |||
55cd1cccee | |||
5f13ba3d1b | |||
298aa0aa8d | |||
5d49be6f7e | |||
ab81bf5875 | |||
5d8765683a | |||
6c22bc0145 | |||
5c1908668f | |||
94da7fb255 | |||
46d2d35c1a | |||
cba41b29bc | |||
7a8f47b995 | |||
40062c5091 | |||
6081ef6b02 | |||
4cffe5a662 | |||
813aec2b42 | |||
f2452bbc70 | |||
28fb685bfc | |||
0661ca00ff | |||
0b04619003 | |||
85d7b6aa57 | |||
bba961269c | |||
d0f7d2c5e9 | |||
eeeb3eb184 | |||
f00f8d26f6 | |||
74817beec6 | |||
7b6809245c | |||
5b40a8fd41 | |||
193f459be1 | |||
c4c19a5b47 | |||
7d103c70e7 | |||
2ffe90948e | |||
8e61aaacde | |||
29c8fcbde2 | |||
8dd21f46aa | |||
e688dde2aa | |||
987e1e1bdb | |||
510257c436 | |||
bb1572f32a | |||
45276034b1 | |||
0d4a144524 | |||
a0e5102ca7 | |||
69c0f5ace9 | |||
d0962c2387 | |||
4670894cfd | |||
a2f6c70a03 | |||
ef469868d8 | |||
c9ba872529 | |||
1e15042be2 | |||
7647978da5 | |||
f82f3052f2 | |||
10b299ff65 | |||
e4ff0418fd | |||
a2b25e31ca | |||
c4772db808 | |||
38c2d9131b | |||
1566323fca | |||
bf0a923eb2 | |||
e372480249 |
3
.github/FUNDING.yml
vendored
@ -1 +1,2 @@
|
||||
custom: ['https://nicoverbruggen.be/sponsor', 'https://paypal.me/nicoverbruggen']
|
||||
github: nicoverbruggen
|
||||
custom: ['https://nicoverbruggen.be/sponsor']
|
62
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
name: 🐞 Bug report
|
||||
description: Something going wrong? File a bug report!
|
||||
title: "[Bug] <title>"
|
||||
labels: [bug]
|
||||
assignees: nicoverbruggen
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A concise description of what you're experiencing.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1. Open this menu...
|
||||
2. Click here...
|
||||
3. Scroll to...
|
||||
4. See error...
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment
|
||||
description: |
|
||||
examples:
|
||||
- **macOS**: (e.g. Ventura 13.3)
|
||||
- **Valet**: (e.g. 4.0)
|
||||
- **PHP Monitor**: (e.g. 5.8)
|
||||
value: |
|
||||
- macOS:
|
||||
- Valet:
|
||||
- PHP Monitor:
|
||||
render: markdown
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Do you have a log file (or a screenshot) or any additional information?
|
||||
description: |
|
||||
You can start extra verbose logging by running: `touch ~/.config/phpmon/verbose` and restarting PHP Monitor.
|
||||
|
||||
You can find the latest log in: `~/.config/phpmon/last_session.log`. Please attach it here!
|
||||
|
||||
(You can attach images or log files by clicking this area to highlight it and then dragging files in.)
|
||||
validations:
|
||||
required: false
|
31
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: 😎 Feature request
|
||||
description: Do you have a great idea for an enhancement that could improve PHP Monitor?
|
||||
title: "[Feature] <title>"
|
||||
labels: [enhancement]
|
||||
assignees: nicoverbruggen
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered. Please make sure you've checked the discussions tab as well. Enhancement requests that are not immediately approved will be moved to a discussion instead, so you will find some there.
|
||||
options:
|
||||
- label: I have searched the existing issues and discussions
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is this feature request related to a problem?
|
||||
description: "A clear and concise description of what the problem is. For example: 'I am always frustrated when...'"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like to see
|
||||
description: What would be a user-friendly way of resolving this particular issue?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional information or context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
validations:
|
||||
required: false
|
23
.github/contributing.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# Contribution Guidelines
|
||||
|
||||
Thank you for your interest in contributing to PHP Monitor.
|
||||
|
||||
I consider this project a bit of a nice side-project to my daily gig, so it is very much a personal affair where I love to tinker around.
|
||||
|
||||
**While the code of the latest PHP Monitor release is public, many things are constantly in flux that may not be pushed to this repository yet.**
|
||||
|
||||
I don't mean to be rude, but I don't want other people involved with the project beyond simply contributing a few small things here and there, as has been the case in the past.
|
||||
|
||||
The extra mental overhead of having additional contributors to report to, whose code will need to be reviewed... it's a lot and it makes working on PHP Monitor less enjoyable for me.
|
||||
|
||||
Plus, at this point, the majority of PHP Monitor's main functionality is also done.
|
||||
|
||||
As a result, I may refer you to this file at some point. Again, I don't wish to be rude, but this general rule stands:
|
||||
|
||||
**Making any changes in a fork and opening a pull request without opening an issue first will most likely result in your PR being closed without mercy.**
|
||||
|
||||
To repeat, I am **not opposed** to small contributions and fixes, if they are **meaningful or insightful**.
|
||||
|
||||
To learn more, please check out the [pull request template](/.github/pull_request_template.md) which contains more information about my contribution requirements. (This will also show up when you open a new PR.)
|
||||
|
||||
Thank you for respecting this!
|
2
.github/pull_request_template.md
vendored
@ -16,7 +16,7 @@ In short: It is usually best to *get in touch first* if you are making substanti
|
||||
|
||||
## About destination branches
|
||||
|
||||
Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Merge requests that target `main` will be closed without mercy.**
|
||||
Please keep in mind that `main` is reserved for the current code state of the latest release and should *never* be the destination branch unless a new release is happening. **Pull requests that target `main` will be closed without mercy.**
|
||||
|
||||
Usually, the best target is the stable `dev/x.x` branch that corresponds with the latest major version that is released.
|
||||
|
||||
|
1
.gitignore
vendored
@ -2,4 +2,5 @@ phpmon.xcodeproj/project.xcworkspace
|
||||
phpmon.xcodeproj/xcuserdata
|
||||
PHP Monitor.xcodeproj/project.xcworkspace
|
||||
PHP Monitor.xcodeproj/xcuserdata
|
||||
phpmon-updater/PHP Monitor Self-Updater.app/
|
||||
.DS_Store
|
15
.swiftlint.yml
Normal file
@ -0,0 +1,15 @@
|
||||
disabled_rules:
|
||||
- todo
|
||||
- identifier_name
|
||||
- force_try
|
||||
- force_cast
|
||||
|
||||
opt_in_rules:
|
||||
- empty_count
|
||||
|
||||
included:
|
||||
- phpmon
|
||||
- phpmon-tests
|
||||
|
||||
excluded:
|
||||
- phpmon/Vendor
|
97
DEVELOPER.md
Normal file
@ -0,0 +1,97 @@
|
||||
# DEVELOPER README
|
||||
|
||||
## ✅ Linting
|
||||
|
||||
This project uses the [SwiftLint](https://github.com/realm/SwiftLint) linter. You must install it and can run it like so:
|
||||
|
||||
```
|
||||
swiftlint
|
||||
```
|
||||
|
||||
It also automatically runs when you try to build the project. You'll get a warning if `swiftlint` is not installed, though. You can attempt to automatically fix issues:
|
||||
|
||||
```
|
||||
swiftlint --fix
|
||||
```
|
||||
|
||||
## ⚙️ Preferences
|
||||
|
||||
You can find the persisted configuration file in `~/Library/Preferences/com.nicoverbruggen.phpmon.plist`
|
||||
|
||||
These values are cached by the OS. You can clear this cache by running:
|
||||
|
||||
```
|
||||
defaults delete com.nicoverbruggen.phpmon && killall cfprefsd
|
||||
```
|
||||
|
||||
## 🔧 Build instructions
|
||||
|
||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||
|
||||
### PHP Monitor
|
||||
|
||||
If you'd like to build PHP Monitor yourself, you need:
|
||||
|
||||
* Xcode (usually the latest version)
|
||||
* *PHP Monitor Self-Updater.app* in the `phpmon-updater` directory (You can build it yourself, it is included as a target OR copy the signed app so it is included w/ PHP Monitor)
|
||||
* The contents of this repository
|
||||
|
||||
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
||||
|
||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||
|
||||
### PHP Monitor Updater
|
||||
|
||||
Select the separate target and build. You can then copy the product to the `phpmon-updater` directory. The binary will be re-signed when distributing the main build.
|
||||
|
||||
## 🚀 Release procedure
|
||||
|
||||
1. Merge into `main`
|
||||
2. Create tag
|
||||
3. Add changes to changelog + update security document
|
||||
4. Archive
|
||||
5. Notarize and prepare for own distribution
|
||||
6. After notarization, export .app
|
||||
7. Create zipped version
|
||||
8. Calculate SHA256: `openssl dgst -sha256 phpmon.zip`
|
||||
9. Upload to GitHub and add to tagged release
|
||||
10. Update Cask with new version + hash
|
||||
11. Check new version can be installed via Cask
|
||||
|
||||
## 🍱 Marketing Mode
|
||||
|
||||
You can enable marketing mode by setting the `PHPMON_MARKETING_MODE` environment variable. It preloads a list of (fake) domains in the domain window list for screenshot & marketing purposes.
|
||||
|
||||
launchctl setenv PHPMON_MARKETING_MODE true
|
||||
|
||||
## 🐛 Symbolication of crashes
|
||||
|
||||
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
||||
|
||||
For example, given the following crash (from an .ips file):
|
||||
|
||||
```
|
||||
Thread 2 Crashed:: Dispatch queue: com.apple.root.user-initiated-qos
|
||||
0 libswiftDispatch.dylib 0x7ff82aa3ab8c static OS_dispatch_source.makeProcessSource(identifier:eventMask:queue:) + 28
|
||||
1 PHP Monitor 0x1096907d8 0x10965e000 + 206808
|
||||
| |
|
||||
address load address
|
||||
2 PHP Monitor 0x1096903ac 0x10965e000 + 205740
|
||||
3 PHP Monitor 0x10968f88b 0x10965e000 + 202891
|
||||
```
|
||||
|
||||
You must use the correct order for the the address and load address in the command below:
|
||||
|
||||
```
|
||||
$ atos -arch x86_64 -o '/path/to/PHP Monitor.app.dSYM/Contents/Resources/DWARF/PHP Monitor' -l 0x10965e000 0x1096907d8
|
||||
| | | |
|
||||
architecture path to DSYM load address address
|
||||
```
|
||||
|
||||
This will return the relevant information, for example:
|
||||
|
||||
```
|
||||
FSWatcher.startMonitoring(_:behaviour:) (in PHP Monitor) (PhpConfigWatcher.swift:95)
|
||||
```
|
||||
|
||||
For more information, see [Apple's documentation](https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report).
|
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Nico Verbruggen
|
||||
Copyright (c) 2019-2023 Nico Verbruggen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug.Dev"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C4F7807825D7F84B000DBC97"
|
||||
BuildableName = "Unit Tests.xctest"
|
||||
BlueprintName = "Unit Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7BB28F9B90F0021E251"
|
||||
BuildableName = "UI Tests.xctest"
|
||||
BlueprintName = "UI Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7AC28F9B4940021E251"
|
||||
BuildableName = "Feature Tests.xctest"
|
||||
BlueprintName = "Feature Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug.Dev"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--v"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--cli"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_working.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_working_no_valet.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_broken.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "EXTREME_DOCTOR_MODE"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "PAINT_PHPMON_SWIFTUI_VIEWS"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release.Dev"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug.Dev">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release.Dev"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug.EA"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C4F7807825D7F84B000DBC97"
|
||||
BuildableName = "Unit Tests.xctest"
|
||||
BlueprintName = "Unit Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7BB28F9B90F0021E251"
|
||||
BuildableName = "UI Tests.xctest"
|
||||
BlueprintName = "UI Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7AC28F9B4940021E251"
|
||||
BuildableName = "Feature Tests.xctest"
|
||||
BlueprintName = "Feature Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug.EA"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--v"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--cli"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_working.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_working_no_valet.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
<CommandLineArgument
|
||||
argument = "--configuration:~/.phpmon_fconf_broken.json"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "EXTREME_DOCTOR_MODE"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "PAINT_PHPMON_SWIFTUI_VIEWS"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release.EA"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C41C1B3222B0097F00E7CF16"
|
||||
BuildableName = "PHP Monitor.app"
|
||||
BlueprintName = "PHP Monitor"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug.EA">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release.EA"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@ -14,9 +14,9 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
BlueprintIdentifier = "C406A5EF298AD2CE00B5B85A"
|
||||
BuildableName = "PHP Monitor Self-Updater.app"
|
||||
BlueprintName = "PHP Monitor Self-Updater"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
@ -31,7 +31,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
@ -44,18 +44,12 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
BlueprintIdentifier = "C406A5EF298AD2CE00B5B85A"
|
||||
BuildableName = "PHP Monitor Self-Updater.app"
|
||||
BlueprintName = "PHP Monitor Self-Updater"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "help"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
@ -67,9 +61,9 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
||||
BuildableName = "phpmon-cli"
|
||||
BlueprintName = "phpmon-cli"
|
||||
BlueprintIdentifier = "C406A5EF298AD2CE00B5B85A"
|
||||
BuildableName = "PHP Monitor Self-Updater.app"
|
||||
BlueprintName = "PHP Monitor Self-Updater"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@ -27,14 +27,42 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<TestPlans>
|
||||
<TestPlanReference
|
||||
reference = "container:PHP Monitor.xcodeproj/PHP Monitor.xctestplan"
|
||||
default = "YES">
|
||||
</TestPlanReference>
|
||||
</TestPlans>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C4F7807825D7F84B000DBC97"
|
||||
BuildableName = "phpmon-tests.xctest"
|
||||
BlueprintName = "phpmon-tests"
|
||||
BuildableName = "Unit Tests.xctest"
|
||||
BlueprintName = "Unit Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7AC28F9B4940021E251"
|
||||
BuildableName = "Feature Tests.xctest"
|
||||
BlueprintName = "Feature Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
<TestableReference
|
||||
skipped = "NO"
|
||||
parallelizable = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C471E7BB28F9B90F0021E251"
|
||||
BuildableName = "UI Tests.xctest"
|
||||
BlueprintName = "UI Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
@ -61,6 +89,29 @@
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--v"
|
||||
isEnabled = "NO">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "EXTREME_DOCTOR_MODE"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "SLOW_SHELL_MODE"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
<EnvironmentVariable
|
||||
key = "PAINT_PHPMON_SWIFTUI_VIEWS"
|
||||
value = ""
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1430"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "C4F7807825D7F84B000DBC97"
|
||||
BuildableName = "Unit Tests.xctest"
|
||||
BlueprintName = "Unit Tests"
|
||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
441
README.md
@ -1,15 +1,13 @@
|
||||
# PHP Monitor
|
||||
> **Note**
|
||||
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider [sponsoring](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
|
||||
|
||||
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project.
|
||||
> You can also send me [feedback](https://twitter.com/nicoverbruggen) if the app came in handy.<br>**Thank you!** ⭐️
|
||||
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
||||
|
||||
<img src="./phpmon/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png" alt="phpmon icon" width="128px" />
|
||||
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up if you want to use all of the functionality of the app</u> (consult the FAQ below with info about how to set up your environment).
|
||||
|
||||
**PHP Monitor** (or phpmon) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so you need to have it set up before you can use this.
|
||||
<img src="./docs/screenshot.jpg" width="1280px" alt="phpmon screenshot (menu bar app)"/>
|
||||
|
||||
<img src="./docs/screenshot41.jpg" width="800px" alt="phpmon screenshot (menu bar app)"/>
|
||||
|
||||
<small><i>Screenshot: A menu showing all of the functionality of PHP Monitor.</i></small>
|
||||
<small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small>
|
||||
|
||||
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
|
||||
|
||||
@ -17,31 +15,66 @@ It's super convenient to switch between different versions of PHP. You'll even g
|
||||
|
||||
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
|
||||
|
||||
You can also add new domains as links, isolate sites, manage various services, and perform First Aid to fix all kinds of common PHP link issues.
|
||||
|
||||
## 🖥 System requirements
|
||||
|
||||
PHP Monitor is a universal application that runs on Apple Silicon **and** Intel-based Macs.
|
||||
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
|
||||
|
||||
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
||||
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
||||
* The brew formula `php` has to be installed (which version is detected)
|
||||
* Laravel Valet 2.16.2 or higher (older versions might be compatible but are not supported)
|
||||
* Your user account can administer your computer (required for some functionality, e.g. certificate generation)
|
||||
* macOS 12.4 or later (Monterey, Ventura and Sonoma are supported)
|
||||
* Homebrew is installed in the default location (`/usr/local/homebrew` or `/opt/homebrew`)
|
||||
* Homebrew `php` formula is installed
|
||||
* Optional but recommended: Laravel Valet
|
||||
|
||||
_You may need to update your Valet installation to keep everything working if a major version update of PHP has been released. You can do this by running `composer global update && valet install`._
|
||||
_Starting with PHP Monitor 6.0, you do not need to have Laravel Valet installed for PHP Monitor to work. To get access to all features of PHP Monitor however, installing Valet is **recommended**._
|
||||
|
||||
For more information, please see [SECURITY.md](./SECURITY.md) to find out which version of the app is currently supported.
|
||||
|
||||
## 🚀 How to install
|
||||
|
||||
You can install via Homebrew (recommended), or may download the latest release on GitHub.
|
||||
Again, if you want to have access to *all features* of PHP Monitor, I recommend installing **[Laravel Valet](https://laravel.com/docs/master/valet)** first:
|
||||
|
||||
To install via Homebrew, run:
|
||||
```sh
|
||||
composer global require laravel/valet
|
||||
valet install
|
||||
valet trust
|
||||
```
|
||||
|
||||
Currently, PHP Monitor is compatible with Laravel Valet v2, v3 and v4. Each of these versions of Valet support slightly different PHP versions, which is why legacy versions remain supported. Please note that some features are not available in older versions of Valet, like site isolation.
|
||||
|
||||
#### Manual installation (recommended, first time only)
|
||||
|
||||
Once that's done, you can [download the latest release](https://github.com/nicoverbruggen/phpmon/releases/latest), unzip it and place it in `/Applications`.
|
||||
|
||||
#### Installation via Homebrew
|
||||
|
||||
*Prior to version 5.8, this was the recommended way of installing PHP Monitor.*
|
||||
|
||||
If you prefer to install the app via Homebrew, you can also run the following:
|
||||
|
||||
```sh
|
||||
brew tap nicoverbruggen/homebrew-cask
|
||||
brew install --cask phpmon
|
||||
```
|
||||
|
||||
To upgrade your existing installation, run:
|
||||
## ⬆️ How to update
|
||||
|
||||
brew upgrade phpmon
|
||||
The recommended method of updating the app to the latest version is to use **the built-in updater**.
|
||||
|
||||
_The app is signed and notarized, meaning all you have to do is approve its first launch._
|
||||
If you have a very slow internet connection, the updater may report that the download has timed out. In that case, you may wish to manually update by [downloading the latest release](https://github.com/nicoverbruggen/phpmon/releases/latest) and placing the app in `/Applications`.
|
||||
|
||||
(You may also use Homebrew to update PHP Monitor, but this will require you to approve the app every time an update is installed. If you use the built-in updater, this won't be necessary.)
|
||||
|
||||
## ⚡️ Launchers (Alfred, Raycast)
|
||||
|
||||
If you would like to integrate with your launcher of choice, you can also download an [Alfred workflow](https://github.com/nicoverbruggen/phpmon/raw/main/integrations/phpmon.alfredworkflow) or [Raycast extension](https://www.raycast.com/nicoverbruggen/php-monitor) that works with PHP Monitor.
|
||||
|
||||
The app must be running in the background for these to work, and the _Allow third-party integrations_ checkbox must be enabled in Preferences (it is by default).
|
||||
|
||||
## 🔑 Is the app signed & notarized?
|
||||
|
||||
Yes, the app is signed and notarized, meaning all you have to do is approve its first launch (or whenever it updates).
|
||||
|
||||
## 👨💻 Why build this?
|
||||
|
||||
@ -49,12 +82,20 @@ I wanted to be able to **see at a glance** which version of PHP was linked, and
|
||||
|
||||
Initially, I had an Alfred workflow for this — but it has now been replaced with this utility, which also does a good job at displaying additional information at a glance, like the current PHP version, memory limits, and more.
|
||||
|
||||
## 🐘 Why not use Laravel Herd?
|
||||
|
||||
If you don't need to customize your local PHP setup and just want an easy and ready-to-go environment to start coding, [Laravel Herd](https://herd.laravel.com) is probably more than sufficient for many use cases.
|
||||
|
||||
If you need more customization and flexibility I encourage you to consider PHP Monitor in combination with Laravel Valet or some other solution like Docker (with Laravel Sail, for example).
|
||||
|
||||
## 🤬 The app won't start?!
|
||||
|
||||
PHP Monitor performs some integrity checks to ensure a good experience when using the app. You'll get a message telling you that PHP Monitor won't work correctly in a variety of scenarios.
|
||||
|
||||
**Follow instructions as specified in the alert in order to resolve any issues.**
|
||||
|
||||
(If the app crashes at launch without showing you any of these messages, you might have a non-standard Homebrew and Valet setup. Those are not supported.)
|
||||
|
||||
## 🙋♂️ FAQ & Troubleshooting
|
||||
|
||||
> If you are having issues, the first thing you should be doing is installing the latest version of PHP Monitor _and_ Laravel Valet. This can resolve a variety of issues. To upgrade Valet, run `composer global update`. Don't forget to run `valet install` after upgrading.
|
||||
@ -64,41 +105,61 @@ If you're still having issues, here's a few common questions & answers, as well
|
||||
<details>
|
||||
<summary><strong>Which versions of PHP are supported?</strong></summary>
|
||||
|
||||
<ul>
|
||||
<li>PHP 5.6</li>
|
||||
<li>PHP 7.0</li>
|
||||
<li>PHP 7.1</li>
|
||||
<li>PHP 7.2</li>
|
||||
<li>PHP 7.3</li>
|
||||
<li>PHP 7.4</li>
|
||||
<li>PHP 8.0</li>
|
||||
<li>PHP 8.1</li>
|
||||
<li>PHP 8.2 (experimental)</li>
|
||||
</ul>
|
||||
All stable and supported PHP versions are also supported by PHP Monitor. However, depending on which version of Valet you have installed, which versions of PHP that are made available for switching purposes may differ.
|
||||
|
||||
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.
|
||||
> **Note**
|
||||
> If you have versions of PHP installed that can be detected by PHP Monitor but is *not* supported by the currently active version of Valet, you will be alerted by an item in the menu with an exclamation mark emoji. (⚠️)
|
||||
|
||||
Backports that are installable via PHP Monitor's **PHP Version Manager** functionality are subject to availability via [this tap](https://github.com/shivammathur/homebrew-php).
|
||||
|
||||
For maximum compatibility with older PHP versions, you may wish to keep using Valet 2 or 3. For more information, please see [SECURITY.md](./SECURITY.md) to find out which versions of PHP are supported with different versions of Valet.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How do I install additional versions of PHP, including legacy versions?</strong></summary>
|
||||
|
||||
Assuming you have installed the `php` formula, the latest stable version of PHP is installed. At the time of writing, this is PHP 8.2.
|
||||
|
||||
You can install other supported versions of PHP via PHP Monitor's **PHP Version Manager**. (You can manually install or upgrade PHP versions too, but this is not recommended.)
|
||||
|
||||
Please keep in mind that installing or updating PHP versions, even when done via PHP Monitor's **PHP Version Manager**, may cause other required formula dependencies (required software needed to keep those PHP versions functional) to be upgraded. It might not be very transparent when this happens, but this is likely the cause if installing a PHP version takes longer than expected: usually other dependencies are also being installed.
|
||||
|
||||
Additionally, upgrading one specific version of PHP may also cause other installed versions of PHP to *also* be updated in one go, if the dependencies for that one version also apply to the other (newer) version(s) of PHP. It's a bit tricky to manage PHP versions via Homebrew, and even PHP Monitor may encounter some difficulties.
|
||||
|
||||
If you encounter a strange scenario or a malfunction, please open an issue on the issue tracker and get in touch. I'd like to keep enhancing this process to make it as foolproof as possible.
|
||||
|
||||
> *Note*: Using PHP Monitor when managing PHP versions may cause [temporary alias conflicts](https://github.com/nicoverbruggen/phpmon/issues/54#issuecomment-979789724) while the core tap alias and the tap's alias refer to a different version of PHP, but this is generally speaking a minor inconvenience, since this normally only applies when a new PHP version releases.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>I want PHP Monitor to start up when I boot my Mac!</strong></summary>
|
||||
|
||||
You can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account.
|
||||
If you are running macOS Ventura or newer, there's an option in the Settings menu that you can select: "Start PHP Monitor at login".
|
||||
|
||||
If you are on an older version of macOS, you can do this by dragging *PHP Monitor.app* into the **Login Items** section in **System Preferences > Users & Groups** for your account.
|
||||
|
||||
Super convenient!
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What features are unavailable in Standalone Mode?</strong></summary>
|
||||
|
||||
The services manager is disabled, and all other obvious Laravel Valet integrations (configuration finder, domains list, Fix My Valet) are also disabled.
|
||||
|
||||
(Most other features remain available.)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>I want to set up PHP Monitor from scratch! I don't have Homebrew installed either, where do I begin?</strong></summary>
|
||||
|
||||
If you want to set up your computer for the very first time with PHP Monitor, here's how I do it:
|
||||
If you want to set up your computer for the very first time with PHP Monitor, here's how I do it.
|
||||
|
||||
Install [Homebrew](https://brew.sh) first.
|
||||
**I have also created [a video tutorial](https://www.youtube.com/watch?v=fO3hVhkvm3w) which may be easier to follow. If you just want the terminal commands, keep reading.**
|
||||
|
||||
Install PHP, composer, add to path:
|
||||
Install [Homebrew](https://brew.sh) first. Follow the instructions there first!
|
||||
|
||||
Then, you'll need to set up your PATH.
|
||||
|
||||
brew install php
|
||||
brew install composer
|
||||
nano .zshrc
|
||||
|
||||
Make sure the following line is not in the comments:
|
||||
@ -111,30 +172,96 @@ If you're on an Apple Silicon-based Mac, you'll need to add:
|
||||
# on an M1 Mac
|
||||
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
||||
|
||||
and add the following to your .zshrc, but add this BEFORE the homebrew PATH additions:
|
||||
and add the following to your `.zshrc` file, but add this BEFORE the homebrew PATH additions:
|
||||
|
||||
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
||||
|
||||
If you're adding composer and Homebrew binaries, ensure that Homebrew binaries are preferred by adding these to the path last. On my system, that looks like this:
|
||||
If you're adding `composer` and Homebrew binaries, ensure that Homebrew binaries are preferred by adding these to the path last. On my system, that looks like this:
|
||||
|
||||
export PATH=$HOME/bin:/usr/local/bin:$PATH
|
||||
export PATH=$HOME/bin:~/.composer/vendor/bin:$PATH
|
||||
export PATH=$HOME/bin:/opt/homebrew/bin:$PATH
|
||||
|
||||
If you are *not* on Apple Silicon, you should remove the third line.
|
||||
|
||||
Install the `php` and `composer` formulae:
|
||||
|
||||
brew install php composer
|
||||
|
||||
Make sure PHP is linked correctly:
|
||||
|
||||
which php
|
||||
|
||||
should return: `/usr/local/bin/php` (or `/opt/homebrew/bin/php`)
|
||||
should return: `/usr/local/bin/php` (or `/opt/homebrew/bin/php` if you are on Apple Silicon)
|
||||
|
||||
**If you don't need Laravel Valet, you can stop here. PHP Monitor will work like this in Standalone Mode.**
|
||||
|
||||
If you'd like to have Valet as well, continue and install Valet with Composer, like this.
|
||||
|
||||
composer global require laravel/valet
|
||||
|
||||
For optimal results, you should lock your PHP platform for global dependencies to the oldest version of PHP you intend to run. If that version is PHP 7.0, your `~/.composer/composer.json` file could look like this (please adjust the version accordingly!):
|
||||
|
||||
```
|
||||
{
|
||||
"require": {
|
||||
"laravel/valet": "^3.0",
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "7.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Run `composer global update` again. This ensures that when you switch to a different global PHP version, [Valet won't break](https://github.com/nicoverbruggen/phpmon/issues/178). If it does, PHP Monitor will let you know what you can do about this.
|
||||
|
||||
Then, install Valet:
|
||||
|
||||
valet install
|
||||
|
||||
This should install `dnsmasq` and set up Valet. Great, almost there!
|
||||
|
||||
valet trust
|
||||
|
||||
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
||||
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work. You will need to approve the initial launch of the app, but you should be ready to go now.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How frequently does PHP Monitor check for updates?</strong></summary>
|
||||
|
||||
PHP Monitor will check if an update is available every time you start the app.
|
||||
|
||||
You can disable this behaviour by going to Preferences (via the PHP Monitor icon in the menu bar) and unchecking "Automatically check for updates". (You can always check for updates manually.)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>I have PHP Monitor installed, and it works. I want to upgrade my PHP installations to the latest version, what's the best way to do this?</strong></summary>
|
||||
|
||||
The easiest way is to simply use the built-in **PHP Version Manager**, which will allow you to upgrade your PHP versions with one click.
|
||||
|
||||
If you want to do this manually, you can follow the instructions below.
|
||||
|
||||
It's easy to make a mistake here, and end up with an unlinked version of PHP or have versions missing from PHP Monitor.
|
||||
|
||||
Here's what I usually do:
|
||||
|
||||
* Open PHP Monitor and select **First Aid & Services** > **Restore Homebrew Permissions**.
|
||||
* Close PHP Monitor after the pop-up tells you the permissions were restored.
|
||||
* Run `brew update-reset`
|
||||
* Run `brew upgrade`
|
||||
|
||||
If after this, any PHP versions are missing in PHP Monitor, please run the following for the versions that are missing:
|
||||
|
||||
* Run `brew uninstall php@x.x` (where `x.x` is the version)
|
||||
* Run `brew cleanup` (if you get any permission issues you may need to manually clean up the folder)
|
||||
* Run `brew install php@x.x` (where `x.x` is the version)
|
||||
|
||||
You may still need to run `brew link php` after upgrading, too.
|
||||
|
||||
That's it. Now start up PHP Monitor again and you should be golden!
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@ -159,6 +286,8 @@ This problem is usually resolved by upgrading Valet and running `valet install`
|
||||
composer global update
|
||||
valet install
|
||||
|
||||
If you are seeing a 502 (Bad Gateway) error after about 30 seconds or so, your request is likely timing out. You may need to solve a performance issue with your own code.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@ -190,15 +319,23 @@ Usually this is a duplicate extension declaration causing issues, or an extensio
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>The option to isolate a site is disabled! What's going on?</strong></summary>
|
||||
|
||||
Make sure you have at least **Valet 3.0** installed, since support for isolation was added in this version of Valet. (Please note that this version of Valet drops support for PHP 5.6.)
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>One of the limits (memory limit, max POST size, max upload size) shows an exclamation mark!</strong></summary>
|
||||
|
||||
The value you provided in your INI file is invalid. If that is the case, PHP will attempt to parse your value as bytes, which is usually unintended. (`1GB` will resolve to merely a few bytes, and all of your applications will run out of memory!)
|
||||
The value you provided in your `.ini` file is invalid. If that is the case, PHP will attempt to parse your value as bytes, which is usually unintended. (`1GB` will resolve to merely a few bytes, and all of your applications will run out of memory!)
|
||||
|
||||
You must a provide a value like so: `1024K`, `256M`, `1G`. Alternatively, `-1` is also allowed, or just an integer (which will result in N amount of bytes being the limit).
|
||||
|
||||
**Example**: Trying to use `1GB` as the memory limit, for example, will result in this exclamation mark. The correct way to set a 1GB limit is by using `1G` as the value. (Note: The displayed value will append `B` for clarity, so if you set `1G`, the value reported by PHP Monitor will be 1 GB.)
|
||||
|
||||
(If you are using Valet, you can adjust these limits in the `.conf.d/php-memory-limits.ini` file. Otherwise, you may need to adjust `php.ini`.)
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
@ -223,16 +360,119 @@ Since v3.4 all of the loaded .ini files are sourced to determine which extension
|
||||
<details>
|
||||
<summary><strong>I've got two Homebrew installations on my Apple Silicon Mac, can I choose which installation to use with PHP Monitor?</strong></summary>
|
||||
|
||||
Not at this time, no. PHP Monitor will prefer the `/opt/homebrew` installation over the classic installation directory.
|
||||
If you are using PHP Monitor on an Intel machine or on an Apple Silicon machine with Rosetta enabled, PHP Monitor expects the main Homebrew binary in `/usr/local/bin/brew`.
|
||||
|
||||
If you are using PHP Monitor on Apple Silicon without Rosetta, PHP Monitor expects the main Homebrew binary in `/opt/homebrew/bin/brew`.
|
||||
|
||||
If there's an issue here, you'll get an alert at launch.
|
||||
|
||||
Make sure that the version of Homebrew that you are running normally is the same as the one that PHP Monitor expects. If you are on M1 hardware for example, but still using Rosetta for Homebrew, you'll need to run PHP Monitor under Rosetta as well.
|
||||
|
||||
PHP Monitor is a universal app and supports both architectures, so [find out here](https://support.apple.com/en-us/HT211861) how to enable Rosetta with PHP Monitor.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Why is the app doing network requests?</strong></summary>
|
||||
|
||||
It's Homebrew. I can't prevent `brew` from doing things via the network when I invoke it.
|
||||
The app will automatically check for updates, which is the most likely culprit.
|
||||
|
||||
PHP Monitor itself doesn't do any network requests. Feel free to check the source code or intercept the traffic, if you don't believe me.
|
||||
This happens at launch (unless disabled), and the app directly checks the Caskfile hosted on GitHub. This data is not, and will not be used for analytics (and, as far as I can tell, cannot).
|
||||
|
||||
I also can't prevent `brew` from doing things via the network when PHP Monitor uses the binary.
|
||||
|
||||
The app includes an Internet Access Policy file, so if you're using something like Little Snitch there should be a description why these calls occur.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How do I various presets to show up?</strong></summary>
|
||||
|
||||
You must set these presets up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||
|
||||
You must have set up at least one valid preset for this presets to work in PHP Monitor.
|
||||
|
||||
Here's an example of a working preset:
|
||||
|
||||
<pre>
|
||||
{
|
||||
"scan_apps": [],
|
||||
"services": [],
|
||||
"presets": [
|
||||
{
|
||||
"name": "Legacy Project",
|
||||
"php": "8.0",
|
||||
"extensions": {
|
||||
"xdebug": false
|
||||
},
|
||||
"configuration": {
|
||||
"memory_limit": "128M",
|
||||
"upload_max_filesize": "128M",
|
||||
"post_max_size": "128M"
|
||||
}
|
||||
}
|
||||
],
|
||||
"export": {}
|
||||
}
|
||||
</pre>
|
||||
|
||||
You can omit the `php` key in the preset if you do not wish for the preset to switch to a given PHP version.
|
||||
|
||||
> **Warning**
|
||||
> You must restart PHP Monitor for these changes to be detected.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How do I ensure additional Homebrew services are shown in the app?</strong></summary>
|
||||
|
||||
> **Info**
|
||||
> Homebrew services aren't displayed if you are using Valet in Standalone Mode.
|
||||
|
||||
You must set these services up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||
|
||||
You can specify custom services in the configuration file for Homebrew services that run as your own user (not root).
|
||||
|
||||
> **Info**
|
||||
> If your service must run as root, it cannot currently be added to PHP Monitor.
|
||||
|
||||
You can find out which services are available by running `brew services list`.
|
||||
|
||||
Here's an example where we add the `mailhog` and `mysql` services to PHP Monitor:
|
||||
|
||||
<pre>
|
||||
{
|
||||
"scan_apps": [],
|
||||
"services": ["mailhog", "mysql"],
|
||||
"presets": [],
|
||||
"export": {}
|
||||
}
|
||||
</pre>
|
||||
|
||||
> **Warning**
|
||||
> You must restart PHP Monitor for these changes to be detected.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How do I set custom environment variables?</strong></summary>
|
||||
|
||||
You must configure these custom environment variables up in a JSON file, located in `~/.config/phpmon/config.json`.
|
||||
|
||||
PHP Monitor uses a default Shell environment, with no custom environment variables. You need to set custom environment variables manually. These are then used for e.g. Composer.
|
||||
|
||||
Here's an example of a working `COMPOSER_HOME` environment variable which is respected:
|
||||
|
||||
<pre>
|
||||
{
|
||||
"scan_apps": [],
|
||||
"services": [],
|
||||
"presets": [],
|
||||
"export": {
|
||||
"COMPOSER_HOME": "/absolute/path/to/composer/folder"
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
> **Warning**
|
||||
> You must restart PHP Monitor for these changes to be detected.
|
||||
|
||||
</details>
|
||||
|
||||
@ -247,7 +487,7 @@ All of these apps should just be detected correctly, no matter their location on
|
||||
|
||||
To see which files are checked to determine availability, see [this file](./phpmon/Domain/Helpers/Application.swift).
|
||||
|
||||
You can add your own apps by creating and editing a `~/.phpmon.conf.json` file, with the following entry:
|
||||
You can add your own apps by creating and editing a `~/.config/phpmon/config.json` file, and make sure the `scan_apps` key is set:
|
||||
|
||||
<pre>
|
||||
{
|
||||
@ -256,14 +496,70 @@ You can add your own apps by creating and editing a `~/.phpmon.conf.json` file,
|
||||
</pre>
|
||||
|
||||
You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor will check for the existence of these apps. You do not need to set the full path, just the name of the app should work. Not all apps support opening a folder, though, so your success might vary.
|
||||
|
||||
> **Warning**
|
||||
> You must restart PHP Monitor for these changes to be detected.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How can the app integrate with third party tools, like Alfred or Raycast?</strong></summary>
|
||||
|
||||
PHP Monitor supports third party app integrations by default, and this feature is enabled in Preferences unless you disable it.
|
||||
|
||||
You can grab the official [Alfred workflow](https://github.com/nicoverbruggen/phpmon/raw/main/integrations/phpmon.alfredworkflow) or [Raycast extension](https://www.raycast.com/nicoverbruggen/php-monitor).
|
||||
|
||||
If you'd like to integrate something yourself, all you need to to is use the `phpmon://` protocol and ensure that third party app integrations are enabled in Preferences (in PHP Monitor).
|
||||
|
||||
Using app callbacks, macOS and PHP Monitor allow for the following to be called:
|
||||
|
||||
* phpmon://list
|
||||
* phpmon://services/stop
|
||||
* phpmon://services/restart/all
|
||||
* phpmon://services/restart/nginx
|
||||
* phpmon://services/restart/php
|
||||
* phpmon://services/restart/dnsmasq
|
||||
* phpmon://locate/config
|
||||
* phpmon://locate/composer
|
||||
* phpmon://locate/valet
|
||||
* phpmon://phpinfo
|
||||
* phpmon://switch/php/{version}
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>How does the app know what PHP version is required for my app?</strong></summary>
|
||||
|
||||
The `composer.json` file in the root of the folder (if it exists) is scanned and interpreted.
|
||||
|
||||
If the version is set in `platform`, it takes precendence.
|
||||
If the version is not set in `platform` but it is in `require` (most common) then that version is used.
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>What do the checkmarks next to the PHP version mean in the site list?</strong></summary>
|
||||
|
||||
You'll see a checkmark next to the version number if the currently enabled PHP version is compatible with the version required to run the site.
|
||||
|
||||
This is determined by evaluating the PHP requirement constraint (e.g. `^8.0`, `~8.0` or a specific version: `8.0`).
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>Why can't I see the driver type any more? It says "Project Type" now.</strong></summary>
|
||||
|
||||
PHP Monitor currently checks your `composer.json` file to try to figure out what project you are running.
|
||||
|
||||
This approach is a lot faster than asking for a driver when you have many sites linked, but is slightly less reliable since the framework or type of project inferred via `composer.json` might not be 100% accurate.
|
||||
|
||||
You can always still ask Valet using the command line, should it be necessary. In my experience fetching the drivers slowed down the app unnecessarily.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><strong>After running PHP Monitor, Homebrew sometimes has issues with `brew upgrade` or `brew cleanup`!</strong></summary>
|
||||
|
||||
<strike>This is a security feature of Homebrew. When you start a service as an administrator, the root user becomes the owner of relevant binaries. You will need to manually clean up those folders yourself using `rm -rf` (or by manually removing those folders via Finder).</strike>
|
||||
You can now use **First Aid & Services > Restore Homebrew Permissions** to (temporarily) resolve this issue and allow for a clean and painless `brew upgrade` or `brew cleanup` process.
|
||||
|
||||
**Update**: If you are using the Valet switcher (currently not available in the latest stable build) you will not encounter this issue. For more information on this, see [this issue](https://github.com/nicoverbruggen/phpmon/issues/17) and also [this issue about switching to Valet's switcher](https://github.com/nicoverbruggen/phpmon/issues/34).
|
||||
If you would like to know more, consult [this issue](https://github.com/nicoverbruggen/phpmon/issues/85) for more information about why this is needed.
|
||||
|
||||
</details>
|
||||
|
||||
@ -272,6 +568,14 @@ You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor
|
||||
|
||||
Please get in touch and open an issue. PHP Monitor shouldn't crash... (unless you are actually removing PHP *while* the app is running, that’s considered normal behaviour!)
|
||||
|
||||
If you would like to report a crash, please include the associated **log files** so I can find out what exactly went wrong.
|
||||
|
||||
To find the logs, take a look in `~/Library/Logs/DiagnosticReports` (in Finder) and see if there's any (log) files that start with "PHP Monitor".
|
||||
|
||||
Additionally, you can help me figure out even more information by sending me your verbose log for your latest session of PHP Monitor. Logging is disabled by default.
|
||||
|
||||
You can start extra verbose logging by running: `touch ~/.config/phpmon/verbose` and restarting PHP Monitor. You can find the latest log in: `~/.config/phpmon/last_session.log`. Please attach it to the relevant bug report.
|
||||
|
||||
</details>
|
||||
|
||||
## 📝 Having another issue?
|
||||
@ -288,15 +592,14 @@ Donations really help with the Apple Developer Program cost, and keep me motivat
|
||||
|
||||
## 😎 Acknowledgements
|
||||
|
||||
While I did make this application during my own free time, I have been lucky enough to do various experiments during work hours at [DIVE](https://dive.be). I'd also like to shout out the following folks:
|
||||
Special thanks go out to:
|
||||
|
||||
* My colleagues at [DIVE](https://dive.be)
|
||||
* The [Homebrew](https://brew.sh/) team who maintain
|
||||
* The [developers & maintainers of Valet](https://github.com/laravel/valet/graphs/contributors)
|
||||
* Everyone in the Laravel community who shared the app (thanks!)
|
||||
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot)
|
||||
* Everyone who left feedback via issues
|
||||
* Everyone who donated to keep the project up and running
|
||||
* Everyone supporting me via [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen)
|
||||
* Everyone who has donated via [my sponsor page](https://nicoverbruggen.be/sponsor)
|
||||
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
|
||||
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
|
||||
* Everyone who has left feedback and reported bugs
|
||||
* Everyone in the Laravel community who shared the app, especially on Twitter
|
||||
|
||||
Thank you very much for your contributions, kind words and support.
|
||||
|
||||
@ -304,15 +607,17 @@ Thank you very much for your contributions, kind words and support.
|
||||
|
||||
### 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 app runs `php-config --version` in the background periodically, usually whenever your Homebrew configuration is modified. A filesystem watcher is used to determine if anything changes in your Homebrew's `bin` directory.
|
||||
|
||||
In order to save power, this only happens once every 60 seconds.
|
||||
PHP Monitor also checks your `.ini` files for extensions and loads more information about your limits (memory limit, POST limit, upload limit). See also the section on *Config change detection* below.
|
||||
|
||||
### Switching PHP versions
|
||||
|
||||
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
|
||||
|
||||
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be much faster than Valet’s switcher.
|
||||
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be a bit faster than Valet’s switcher.
|
||||
|
||||
If you're using Valet 3 or newer, versions of PHP-FPM required to keep isolated sites up and running will also be started or stopped as needed.
|
||||
|
||||
### Config change detection
|
||||
|
||||
@ -326,23 +631,17 @@ If an extension or other process writes to a single file a bunch of times in a s
|
||||
|
||||
1. **Location of your sites**: PHP Monitor uses the Valet configuration file to determine which folders to look into. Each folder is scanned and then PHP Monitor will validate if a composer.json file exists to determine the desired PHP version.
|
||||
1. **Sites secured or not secured**: Whether the directory has been secured is determined by checking if a matching certificate exists under Valet's `Certificates` directory for that site name.
|
||||
1. **Site drivers**: PHP Monitor runs `valet which` to determine which driver is currently in use for each individual site. This command is executed once for each site whenever the site list is refreshed.
|
||||
1. **Project type**: PHP Monitor checks your `composer.json` file for "notable dependencies". If you have `laravel/framework` in your `require`, there's a good chance the project type is `Laravel`, after all.
|
||||
|
||||
*Note*: If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly.
|
||||
> **Note**
|
||||
> If you have linked a folder in Documents, Desktop or Downloads you might need to grant PHP Monitor access to those directories for PHP Monitor to work correctly.
|
||||
|
||||
### Want to know more?
|
||||
|
||||
If you want to know more about how this works, I recommend you check out the source code.
|
||||
|
||||
## 🔧 Build instructions
|
||||
I have done my best to annotate as much as humanly possible, and have avoided using an overly complicated architecture to keep the code as easy to maintain as possible. The code isn't perfect by a long shot (lots of cleanup can still happen!) but the application works well.
|
||||
|
||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||
I also have a few tests for key parts of the application that I found needed to be tested. In the future, I would like to add even more tests for some of the UI stuff, but for now the tests are more unit tests than feature tests.
|
||||
|
||||
If you'd like to build PHP Monitor yourself, you need:
|
||||
|
||||
* Xcode (usually the latest version)
|
||||
* The contents of this repository
|
||||
|
||||
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to immediately build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
||||
|
||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||
For more detailed information for developers, please see [the documentation file for developers](./DEVELOPER.md).
|
||||
|
13
RELEASE.md
@ -1,13 +0,0 @@
|
||||
# Release Procedure
|
||||
|
||||
1. Merge into `main`
|
||||
2. Create tag
|
||||
3. Add changes to changelog + update security document
|
||||
4. Archive
|
||||
5. Notarize and prepare for own distribution
|
||||
6. After notarization, export .app
|
||||
7. Create zipped version
|
||||
8. Calculate SHA256: `openssl dgst -sha256 phpmon.zip`
|
||||
9. Upload to GitHub and add to tagged release
|
||||
10. Update Cask with new version + hash
|
||||
11. Check new version can be installed via Cask
|
17
SECURITY.md
@ -4,19 +4,24 @@
|
||||
|
||||
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
||||
|
||||
| Version | Apple silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
|
||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | TBD |
|
||||
| 6.2 | ✅ Universal binary | ✅ Yes | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4 | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
|
||||
## Legacy versions
|
||||
|
||||
These versions of PHP Monitor are no longer supported, but if you’re using an older computer with an older version of Homebrew, Valet or macOS, you might want to use one of these versions.
|
||||
|
||||
| Version | Apple silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||
| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
||||
| 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||
| 3.5 | ✅ Universal binary | ❌ | Big Sur (11.0) and Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||
| 6.1 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+)<br/>Sonoma (14.0) | macOS 12.4 | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.4 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.4 (w/ Valet 4.x)| 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 6.0 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+) | macOS 12.4 | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.2 (w/ Valet 4.x) | 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 5.8 | ✅ Universal binary | ❌ | Monterey (12.4+)<br/>Ventura (13.0+) | macOS 12.4 | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.2 (w/ Valet 4.x) | 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 5.7 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0) | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x)<br/>PHP 7.1-PHP 8.2 (w/ Valet 4.x) | 3.0 or higher recommended<br/> 2.16.2 minimum |
|
||||
| 5.6 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0)<br/>Ventura (13.0) | macOS 11+ | PHP 5.6—PHP 8.2 (w/ Valet 2.x)<br/>PHP 7.0—PHP 8.2 (w/ Valet 3.x) | 3.0 recommended<br/> 2.16.2 minimum |
|
||||
| 4.1 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
||||
| 4.0 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||
| 3.5 | ✅ Universal binary | ❌ | Big Sur (11.0)<br/>Monterey (12.0) | macOS 10.14+ | PHP 5.6—PHP 8.2 | 2.13 |
|
||||
| 3.0—3.4 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.1 | 2.13 |
|
||||
| 2.6 | ✅ Universal binary | ❌ | Big Sur (11.0) | macOS 10.14+ | PHP 5.6—PHP 8.0 | 2.13 |
|
||||
| 2.5 | ✴️ Universal binary<br/>`/usr/local/homebrew` installations only | ❌ | Big Sur (11.0)<br/>Catalina (10.15) | macOS 10.14+ | not applicable | not applicable |
|
||||
|
BIN
assets/affinity/icon-dev.afdesign
Normal file
BIN
assets/affinity/icon-eap.afdesign
Normal file
BIN
assets/affinity/icon-updater.afdesign
Normal file
BIN
assets/affinity/icon.afdesign
Normal file
BIN
assets/affinity/icons.afdesign
Normal file
BIN
docs/logo.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshot.jpg
Normal file
After Width: | Height: | Size: 674 KiB |
Before Width: | Height: | Size: 396 KiB |
BIN
integrations/phpmon.alfredworkflow
Normal file
@ -1,26 +0,0 @@
|
||||
//
|
||||
// AllowedArguments.swift
|
||||
// phpmon-cli
|
||||
//
|
||||
// Created by Nico Verbruggen on 20/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum AllowedArguments: String, CaseIterable {
|
||||
case use = "use"
|
||||
case performSwitch = "switch"
|
||||
case fix = "fix"
|
||||
case help = "help"
|
||||
|
||||
static func has(_ string: String) -> Bool {
|
||||
return Self.allCases.contains { arg in
|
||||
return arg.rawValue == string
|
||||
}
|
||||
}
|
||||
|
||||
static var rawValues: [String] {
|
||||
return Self.allCases.map { $0.rawValue }
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
//
|
||||
// main.swift
|
||||
// phpmon-cli
|
||||
//
|
||||
// Created by Nico Verbruggen on 20/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let toolver = "0.1 (early access)"
|
||||
|
||||
let log = Log.shared
|
||||
log.verbosity = .info
|
||||
|
||||
if CommandLine.arguments.contains("-q") || CommandLine.arguments.contains("--quiet") {
|
||||
Log.shared.verbosity = .warning
|
||||
}
|
||||
if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") {
|
||||
Log.shared.verbosity = .performance
|
||||
}
|
||||
|
||||
var argument = "help"
|
||||
if CommandLine.arguments.count > 1 {
|
||||
argument = CommandLine.arguments[1]
|
||||
}
|
||||
|
||||
if !AllowedArguments.has(argument) {
|
||||
Log.err("The supported arguments are: \(AllowedArguments.rawValues)")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let action = AllowedArguments.init(rawValue: argument)
|
||||
|
||||
switch action {
|
||||
case .use, .performSwitch:
|
||||
if !Shell.fileExists("\(Paths.binPath)/php") {
|
||||
Log.err("PHP is currently not linked. Attempting quick fix...")
|
||||
_ = Shell.user.executeSynchronously("brew link php", requiresPath: true)
|
||||
}
|
||||
|
||||
let phpenv = PhpEnv.shared
|
||||
PhpEnv.detectPhpVersions()
|
||||
|
||||
if CommandLine.arguments.count < 3 {
|
||||
Log.err("You must enter at least two additional arguments when using this command.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
let version = CommandLine.arguments[2].replacingOccurrences(of: "php@", with: "")
|
||||
if phpenv.availablePhpVersions.contains(version) {
|
||||
Log.info("Switching to PHP \(version)...")
|
||||
Actions.switchToPhpVersion(
|
||||
version: version,
|
||||
availableVersions: phpenv.availablePhpVersions,
|
||||
completed: {
|
||||
Log.info("The switch has been completed.")
|
||||
exit(0)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Log.err("A PHP installation with version \(version) is not installed.")
|
||||
Log.err("The installed versions are: \(phpenv.availablePhpVersions.joined(separator: ", ")).")
|
||||
Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.")
|
||||
exit(1)
|
||||
}
|
||||
case .fix:
|
||||
Log.info("Fixing your PHP installation...")
|
||||
Actions.fixMyPhp()
|
||||
Log.info("All operations completed. You can check which version of PHP is linked by using `php -v`.")
|
||||
exit(0)
|
||||
case .help:
|
||||
print("""
|
||||
===============================================================
|
||||
PHP MONITOR CLI \(toolver)
|
||||
by Nico Verbruggen
|
||||
===============================================================
|
||||
|
||||
Gives access to the quick version switcher from PHP Monitor,
|
||||
but without the GUI and 100% of the speed!
|
||||
|
||||
SUPPORTED COMMANDS
|
||||
|
||||
* use {version}: Switch to a specific version of PHP.
|
||||
(e.g. `phpmon-cli use 8.0`)
|
||||
* switch {version}: Alias for the `use` command.
|
||||
* fix Attempts to unlink all PHP versions,
|
||||
and link the latest version of PHP.
|
||||
* help: Show this help.
|
||||
|
||||
SUPPORTED FLAGS
|
||||
|
||||
* `-q / --quiet`: Silences all logs except for warnings and exceptions.
|
||||
* `-p / --perf`: Enables performance mode.
|
||||
|
||||
""")
|
||||
exit(0)
|
||||
case .none:
|
||||
Log.err("Action not recognized!")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
RunLoop.main.run()
|
@ -1,121 +0,0 @@
|
||||
//
|
||||
// Services.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
|
||||
class Actions {
|
||||
|
||||
// MARK: - Services
|
||||
|
||||
public static func restartPhpFpm()
|
||||
{
|
||||
brew("services restart \(PhpEnv.phpInstall.formula)", sudo: true)
|
||||
}
|
||||
|
||||
public static func restartNginx()
|
||||
{
|
||||
brew("services restart nginx", sudo: true)
|
||||
}
|
||||
|
||||
public static func restartDnsMasq()
|
||||
{
|
||||
brew("services restart dnsmasq", sudo: true)
|
||||
}
|
||||
|
||||
public static func stopAllServices()
|
||||
{
|
||||
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
|
||||
brew("services stop nginx", sudo: true)
|
||||
brew("services stop dnsmasq", sudo: true)
|
||||
}
|
||||
|
||||
/**
|
||||
Kindly asks Valet to switch to a specific PHP version.
|
||||
*/
|
||||
public static func switchToPhpVersionUsingValet(
|
||||
version: String,
|
||||
availableVersions: [String],
|
||||
completed: @escaping () -> Void
|
||||
) {
|
||||
Log.info("Switching to \(version) using Valet")
|
||||
Log.info(valet("use php@\(version)"))
|
||||
completed()
|
||||
}
|
||||
|
||||
// MARK: - Finding Config Files
|
||||
|
||||
public static func openGenericPhpConfigFolder()
|
||||
{
|
||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php")];
|
||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||
}
|
||||
|
||||
public static func openGlobalComposerFolder()
|
||||
{
|
||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".composer/composer.json")
|
||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||
}
|
||||
|
||||
public static func openPhpConfigFolder(version: String)
|
||||
{
|
||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php/\(version)/php.ini")];
|
||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||
}
|
||||
|
||||
public static func openValetConfigFolder()
|
||||
{
|
||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||
.appendingPathComponent(".config/valet")
|
||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||
}
|
||||
|
||||
// MARK: - Other Actions
|
||||
|
||||
public static func createTempPhpInfoFile() -> URL
|
||||
{
|
||||
// Write a file called `phpmon_phpinfo.php` to /tmp
|
||||
try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8)
|
||||
|
||||
// Tell php-cgi to run the PHP and output as an .html file
|
||||
Shell.run("\(Paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
||||
|
||||
return URL(string: "file:///private/tmp/phpmon_phpinfo.html")!
|
||||
}
|
||||
|
||||
// MARK: - Quick Fix
|
||||
|
||||
/**
|
||||
Detects all currently available PHP versions,
|
||||
and unlinks each and every one of them.
|
||||
|
||||
After this, the brew services are also stopped,
|
||||
the latest PHP version is linked, and php + nginx are restarted.
|
||||
|
||||
If this does not solve the issue, the user may need to install additional
|
||||
extensions and/or run `composer global update`.
|
||||
*/
|
||||
public static func fixMyPhp()
|
||||
{
|
||||
brew("services restart dnsmasq", sudo: true)
|
||||
|
||||
PhpEnv.shared.detectPhpVersions().forEach { (version) in
|
||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||
brew("unlink php@\(version)")
|
||||
brew("services stop \(formula)")
|
||||
brew("services stop \(formula)", sudo: true)
|
||||
}
|
||||
|
||||
brew("services stop php")
|
||||
brew("services stop nginx")
|
||||
brew("link php")
|
||||
brew("services restart dnsmasq", sudo: true)
|
||||
brew("services stop php", sudo: true)
|
||||
brew("services stop nginx", sudo: true)
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
//
|
||||
// Command.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class Command {
|
||||
|
||||
/**
|
||||
Immediately executes a command.
|
||||
|
||||
- Parameter path: The path of the command or program to invoke.
|
||||
- Parameter arguments: A list of arguments that are passed on.
|
||||
- Parameter trimNewlines: Removes empty new line output.
|
||||
*/
|
||||
public static func execute(path: String, arguments: [String], trimNewlines: Bool = false) -> String {
|
||||
let task = Process()
|
||||
task.launchPath = path
|
||||
task.arguments = arguments
|
||||
|
||||
let pipe = Pipe()
|
||||
task.standardOutput = pipe
|
||||
task.launch()
|
||||
|
||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let output: String = String.init(data: data, encoding: String.Encoding.utf8)!
|
||||
|
||||
if (trimNewlines) {
|
||||
return output.components(separatedBy: .newlines)
|
||||
.filter({ !$0.isEmpty })
|
||||
.joined(separator: "\n")
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
//
|
||||
// Constants.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
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 minimum version of Valet that is recommended.
|
||||
If the installed version is older, a notification will be shown
|
||||
every time the app launches (with a recommendation to upgrade).
|
||||
|
||||
The minimum requirement is currently synced to PHP 8.1 compatibility.
|
||||
See also: https://github.com/laravel/valet/releases/tag/v2.16.2
|
||||
*/
|
||||
static let MinimumRecommendedValetVersion = "2.16.2"
|
||||
|
||||
/**
|
||||
* The PHP versions supported by this application.
|
||||
* Versions that do not appear in this array are omitted from the list.
|
||||
*/
|
||||
static let SupportedPhpVersions = [
|
||||
// ====================
|
||||
// STABLE RELEASES
|
||||
// ====================
|
||||
// Versions of PHP that are stable and are supported.
|
||||
"5.6",
|
||||
"7.0",
|
||||
"7.1",
|
||||
"7.2",
|
||||
"7.3",
|
||||
"7.4",
|
||||
"8.0",
|
||||
"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"
|
||||
]
|
||||
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
//
|
||||
// Logger.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 21/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Log {
|
||||
|
||||
static var shared = Log()
|
||||
|
||||
enum Verbosity: Int {
|
||||
case error = 1,
|
||||
warning = 2,
|
||||
info = 3,
|
||||
performance = 4
|
||||
|
||||
public func isApplicable() -> Bool {
|
||||
return Log.shared.verbosity.rawValue >= self.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var verbosity: Verbosity = .warning
|
||||
|
||||
static func err(_ item: Any) {
|
||||
if Verbosity.error.isApplicable() {
|
||||
print(item)
|
||||
}
|
||||
}
|
||||
|
||||
static func warn(_ item: Any) {
|
||||
if Verbosity.warning.isApplicable() {
|
||||
print(item)
|
||||
}
|
||||
}
|
||||
|
||||
static func info(_ item: Any) {
|
||||
if Verbosity.info.isApplicable() {
|
||||
print(item)
|
||||
}
|
||||
}
|
||||
|
||||
static func perf(_ item: Any) {
|
||||
if Verbosity.performance.isApplicable() {
|
||||
print(item)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
//
|
||||
// Paths.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
The `Paths` class is used to locate various binaries on the system,
|
||||
and provides a full
|
||||
*/
|
||||
public class Paths {
|
||||
|
||||
public static let shared = Paths()
|
||||
|
||||
private var baseDir : Paths.HomebrewDir
|
||||
|
||||
init() {
|
||||
baseDir = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
||||
}
|
||||
|
||||
// - MARK: Binaries
|
||||
|
||||
public static var valet: String {
|
||||
return "\(binPath)/valet"
|
||||
}
|
||||
|
||||
public static var brew: String {
|
||||
return "\(binPath)/brew"
|
||||
}
|
||||
|
||||
public static var php: String {
|
||||
return "\(binPath)/php"
|
||||
}
|
||||
|
||||
public static var phpConfig: String {
|
||||
return "\(binPath)/php-config"
|
||||
}
|
||||
|
||||
// - MARK: Paths
|
||||
|
||||
public static var whoami: String {
|
||||
return String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||
}
|
||||
|
||||
public static var binPath: String {
|
||||
return "\(shared.baseDir.rawValue)/bin"
|
||||
}
|
||||
|
||||
public static var optPath: String {
|
||||
return "\(shared.baseDir.rawValue)/opt"
|
||||
}
|
||||
|
||||
public static var etcPath: String {
|
||||
return "\(shared.baseDir.rawValue)/etc"
|
||||
}
|
||||
|
||||
// MARK: - Enum
|
||||
|
||||
public enum HomebrewDir: String {
|
||||
case opt = "/opt/homebrew"
|
||||
case usr = "/usr/local"
|
||||
}
|
||||
|
||||
}
|
@ -1,180 +0,0 @@
|
||||
//
|
||||
// Shell.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
public class Shell {
|
||||
|
||||
// MARK: - Invoke static functions
|
||||
|
||||
public static func run(
|
||||
_ command: String,
|
||||
requiresPath: Bool = false
|
||||
) {
|
||||
Shell.user.run(command, requiresPath: requiresPath)
|
||||
}
|
||||
|
||||
public static func pipe(
|
||||
_ command: String,
|
||||
requiresPath: Bool = false
|
||||
) -> String {
|
||||
return Shell.user.pipe(command, requiresPath: requiresPath)
|
||||
}
|
||||
|
||||
// MARK: - Singleton
|
||||
|
||||
/**
|
||||
We now require macOS 11, so no need to detect which terminal to use.
|
||||
*/
|
||||
public var shell: String = "/bin/sh"
|
||||
|
||||
/**
|
||||
Singleton to access a user shell (with --login)
|
||||
*/
|
||||
public static let user = Shell()
|
||||
|
||||
/**
|
||||
Runs a shell command without using the output.
|
||||
Uses the default shell.
|
||||
|
||||
- Parameter command: The command to run
|
||||
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
||||
*/
|
||||
private func run(
|
||||
_ command: String,
|
||||
requiresPath: Bool = false
|
||||
) {
|
||||
// Equivalent of piping to /dev/null; don't do anything with the string
|
||||
_ = Shell.pipe(command, requiresPath: requiresPath)
|
||||
}
|
||||
|
||||
/**
|
||||
Runs a shell command and returns the output.
|
||||
|
||||
- Parameter command: The command to run
|
||||
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
||||
*/
|
||||
private func pipe(
|
||||
_ command: String,
|
||||
requiresPath: Bool = false
|
||||
) -> String {
|
||||
let shellOutput = self.executeSynchronously(command, requiresPath: requiresPath)
|
||||
let hasError = (
|
||||
shellOutput.standardOutput == ""
|
||||
&& shellOutput.errorOutput.lengthOfBytes(using: .utf8) > 0
|
||||
)
|
||||
return !hasError ? shellOutput.standardOutput : shellOutput.errorOutput
|
||||
}
|
||||
|
||||
/**
|
||||
Runs the command and returns a `ShellOutput` object, which contains info about the process.
|
||||
|
||||
- Parameter command: The command to run
|
||||
- Parameter requiresPath: By default, the PATH is not resolved but some binaries might require this
|
||||
- Parameter waitUntilExit: Waits for the command to complete before returning the `ShellOutput`
|
||||
*/
|
||||
public func executeSynchronously(
|
||||
_ command: String,
|
||||
requiresPath: Bool = false
|
||||
) -> Shell.Output {
|
||||
|
||||
let outputPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
|
||||
let task = self.createTask(for: command, requiresPath: requiresPath)
|
||||
task.standardOutput = outputPipe
|
||||
task.standardError = errorPipe
|
||||
task.launch()
|
||||
task.waitUntilExit()
|
||||
|
||||
return Shell.Output(
|
||||
standardOutput: String(
|
||||
data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
|
||||
encoding: .utf8
|
||||
)!,
|
||||
errorOutput: String(
|
||||
data: errorPipe.fileHandleForReading.readDataToEndOfFile(),
|
||||
encoding: .utf8
|
||||
)!,
|
||||
task: task
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
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 {
|
||||
let escapedPath = path.replacingOccurrences(of: " ", with: "\\ ")
|
||||
return Shell.pipe("if [ -f \(escapedPath) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||
}
|
||||
|
||||
/**
|
||||
Creates a new process with the correct PATH and shell.
|
||||
*/
|
||||
public func createTask(for command: String, requiresPath: Bool) -> Process {
|
||||
let tailoredCommand = requiresPath
|
||||
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
||||
: command
|
||||
|
||||
let task = Process()
|
||||
task.launchPath = self.shell
|
||||
task.arguments = ["--login", "-c", tailoredCommand]
|
||||
|
||||
return task
|
||||
}
|
||||
|
||||
public static func captureOutput(
|
||||
_ task: Process,
|
||||
didReceiveStdOutData: @escaping (String) -> Void,
|
||||
didReceiveStdErrData: @escaping (String) -> Void
|
||||
) {
|
||||
let outputPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
|
||||
task.standardOutput = outputPipe
|
||||
task.standardError = errorPipe
|
||||
|
||||
[(outputPipe, didReceiveStdOutData), (errorPipe, didReceiveStdErrData)].forEach {
|
||||
(pipe: Pipe, callback: @escaping (String) -> Void) in
|
||||
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
||||
object: pipe.fileHandleForReading,
|
||||
queue: nil
|
||||
) { notification in
|
||||
if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) {
|
||||
callback(outputString)
|
||||
}
|
||||
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func haltCapturingOutput(_ task: Process) {
|
||||
if let pipe = task.standardOutput as? Pipe {
|
||||
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
||||
}
|
||||
if let pipe = task.standardError as? Pipe {
|
||||
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
||||
}
|
||||
}
|
||||
|
||||
public class Output {
|
||||
public let standardOutput: String
|
||||
public let errorOutput: String
|
||||
public let task: Process
|
||||
|
||||
init(standardOutput: String,
|
||||
errorOutput: String,
|
||||
task: Process) {
|
||||
self.standardOutput = standardOutput
|
||||
self.errorOutput = errorOutput
|
||||
self.task = task
|
||||
}
|
||||
}
|
||||
}
|
@ -1,167 +0,0 @@
|
||||
//
|
||||
// ActivePhpInstallation.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
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 limits: Limits!
|
||||
var extensions: [PhpExtension]!
|
||||
|
||||
// MARK: - Computed
|
||||
|
||||
var formula: String {
|
||||
return (version.short == PhpEnv.brewPhpVersion) ? "php" : "php@\(version.short)"
|
||||
}
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
init() {
|
||||
// Show information about the current version
|
||||
getVersion()
|
||||
|
||||
// If an error occurred, exit early
|
||||
if (version.error) {
|
||||
limits = Limits()
|
||||
extensions = []
|
||||
return
|
||||
}
|
||||
|
||||
// Load extension information
|
||||
let path = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
|
||||
extensions = PhpExtension.load(from: path)
|
||||
|
||||
// Get configuration values
|
||||
limits = Limits(
|
||||
memory_limit: getByteCount(key: "memory_limit"),
|
||||
upload_max_filesize: getByteCount(key: "upload_max_filesize"),
|
||||
post_max_size: getByteCount(key: "post_max_size")
|
||||
)
|
||||
|
||||
// Return a list of .ini files parsed after php.ini
|
||||
let paths = Command.execute(path: Paths.php, arguments: ["-r", "echo php_ini_scanned_files();"])
|
||||
.replacingOccurrences(of: "\n", with: "")
|
||||
.split(separator: ",")
|
||||
.map { String($0) }
|
||||
|
||||
// See if any extensions are present in said .ini files
|
||||
paths.forEach { (iniFilePath) in
|
||||
let exts = PhpExtension.load(from: URL(fileURLWithPath: iniFilePath))
|
||||
if exts.count > 0 {
|
||||
extensions.append(contentsOf: exts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
When the app tries to retrieve the version, the installation is considered broken if the output is nothing,
|
||||
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
|
||||
*/
|
||||
private func getVersion() -> Void {
|
||||
self.version = Version()
|
||||
|
||||
let version = Command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||
|
||||
if (version == "" || version.contains("Warning") || version.contains("Error")) {
|
||||
self.version.short = "💩 BROKEN"
|
||||
self.version.long = ""
|
||||
self.version.error = true
|
||||
return
|
||||
}
|
||||
|
||||
// That's the long version
|
||||
self.version.long = version
|
||||
|
||||
// Next up, let's strip away the minor version number
|
||||
let segments = self.version.long.components(separatedBy: ".")
|
||||
|
||||
// Get the first two elements
|
||||
self.version.short = segments[0...1].joined(separator: ".")
|
||||
}
|
||||
|
||||
/**
|
||||
Retrieves the display value for a specific key in the `.ini` file.
|
||||
|
||||
The following values are valid:
|
||||
* -1: unlimited (show the infinity icon)
|
||||
* 10000: an integer = amount of bytes
|
||||
* 1K, 1M, 1G = shorthand for kilobytes, megabytes and gigabytes
|
||||
|
||||
If none of these notations are used, the _fallback_ value is used.
|
||||
We'll show an emoji to indicate something has gone wrong here.
|
||||
To clarify, B gets appended to valid values.
|
||||
As a result, "5M" (valid) becomes "5MB", and "5MB" (invalid) becomes ⚠️.
|
||||
|
||||
- Parameter key: The key of the `ini` value that needs to be retrieved. For example, you can use `memory_limit`.
|
||||
*/
|
||||
private func getByteCount(key: String) -> String {
|
||||
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"])
|
||||
|
||||
// Check if the value is unlimited
|
||||
if (value == "-1") {
|
||||
return "∞"
|
||||
}
|
||||
|
||||
// Check if the syntax is valid otherwise
|
||||
let regex = try! NSRegularExpression(pattern: #"^([0-9]*)(K|M|G|)$"#, options: [])
|
||||
let match = regex.matches(in: value, options: [], range: NSMakeRange(0, value.count)).first
|
||||
return (match == nil) ? "⚠️" : "\(value)B"
|
||||
}
|
||||
|
||||
/**
|
||||
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.
|
||||
*/
|
||||
func checkPhpFpmStatus() -> Bool {
|
||||
if self.version.short == "5.6" {
|
||||
// The main PHP config file should contain `valet.sock` and then we're probably fine?
|
||||
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
|
||||
return Shell.pipe("cat \(fileName)").contains("valet.sock")
|
||||
}
|
||||
|
||||
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
||||
return Shell.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
|
||||
}
|
||||
|
||||
// MARK: - Structs
|
||||
|
||||
/**
|
||||
Struct containing information about the version number of the current PHP installation.
|
||||
Also includes information about whether the install is considered "broken" or not.
|
||||
If an error was found in the terminal output, `error` is set to `true` and the installation
|
||||
can be considered broken. (The app will display this as well.)
|
||||
*/
|
||||
struct Version {
|
||||
var short = "???"
|
||||
var long = "???"
|
||||
var error = false
|
||||
}
|
||||
|
||||
/**
|
||||
Struct containing information about the limits of the current PHP installation.
|
||||
Includes: memory limit, max upload size and max post size.
|
||||
*/
|
||||
struct Limits {
|
||||
var memory_limit = "???"
|
||||
var upload_max_filesize = "???"
|
||||
var post_max_size = "???"
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
//
|
||||
// HomebrewService.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 11/01/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct HomebrewService: Decodable, Equatable {
|
||||
let name: String
|
||||
let service_name: String
|
||||
let running: Bool
|
||||
let loaded: Bool
|
||||
let pid: Int?
|
||||
let user: String?
|
||||
let status: String?
|
||||
let log_path: String?
|
||||
let error_log_path: String?
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
//
|
||||
// PhpSwitcher.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 21/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol PhpSwitcherDelegate: AnyObject {
|
||||
func switcherDidStartSwitching()
|
||||
func switcherDidCompleteSwitch()
|
||||
}
|
||||
|
||||
class PhpEnv {
|
||||
|
||||
// MARK: - Initializer
|
||||
|
||||
init() {
|
||||
self.currentInstall = ActivePhpInstallation()
|
||||
|
||||
let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json");
|
||||
|
||||
self.homebrewPackage = try! JSONDecoder().decode(
|
||||
[HomebrewPackage].self,
|
||||
from: brewPhpAlias.data(using: .utf8)!
|
||||
).first!
|
||||
|
||||
Log.info("When on your system, the `php` formula means version \(homebrewPackage.version)!")
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
/** The delegate that is informed of updates. */
|
||||
weak var delegate: PhpSwitcherDelegate?
|
||||
|
||||
/** The static app instance. Accessible at any time. */
|
||||
static let shared = PhpEnv()
|
||||
|
||||
/** Whether the switcher is busy performing any actions. */
|
||||
var isBusy: Bool = false
|
||||
|
||||
/** All available versions of PHP. */
|
||||
var availablePhpVersions: [String] = []
|
||||
|
||||
/** Cached information about the PHP installations. */
|
||||
var cachedPhpInstallations: [String: PhpInstallation] = [:]
|
||||
|
||||
/** Information about the currently linked PHP installation. */
|
||||
var currentInstall: ActivePhpInstallation
|
||||
|
||||
/**
|
||||
The version that the `php` formula via Brew is aliased to on the current system.
|
||||
|
||||
If you're up to date, `php` will be aliased to the latest version,
|
||||
but that might not be the case since not everyone keeps their
|
||||
software up-to-date.
|
||||
|
||||
As such, we take that information from Homebrew.
|
||||
*/
|
||||
static var brewPhpVersion: String {
|
||||
return Self.shared.homebrewPackage.version
|
||||
}
|
||||
|
||||
/**
|
||||
The currently linked and active PHP installation.
|
||||
*/
|
||||
static var phpInstall: ActivePhpInstallation {
|
||||
return Self.shared.currentInstall
|
||||
}
|
||||
|
||||
/**
|
||||
Information we were able to discern from the Homebrew info command.
|
||||
*/
|
||||
var homebrewPackage: HomebrewPackage! = nil
|
||||
|
||||
// MARK: - Methods
|
||||
|
||||
public static var switcher: PhpSwitcher {
|
||||
return InternalSwitcher()
|
||||
}
|
||||
|
||||
public static func detectPhpVersions() -> Void {
|
||||
_ = Self.shared.detectPhpVersions()
|
||||
}
|
||||
|
||||
/**
|
||||
Detects which versions of PHP are installed.
|
||||
*/
|
||||
public func detectPhpVersions() -> [String]
|
||||
{
|
||||
let files = Shell.pipe("ls \(Paths.optPath) | grep php@")
|
||||
|
||||
var versionsOnly = extractPhpVersions(from: files.components(separatedBy: "\n"))
|
||||
|
||||
// Make sure the aliased version is detected
|
||||
// The user may have `php` installed, but not e.g. `php@8.0`
|
||||
// We should also detect that as a version that is installed
|
||||
let phpAlias = homebrewPackage.version
|
||||
|
||||
// Avoid inserting a duplicate
|
||||
if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) {
|
||||
versionsOnly.append(phpAlias)
|
||||
}
|
||||
|
||||
Log.info("The PHP versions that were detected are: \(versionsOnly)")
|
||||
|
||||
availablePhpVersions = versionsOnly
|
||||
|
||||
var mappedVersions: [String: PhpInstallation] = [:]
|
||||
|
||||
availablePhpVersions.forEach { version in
|
||||
mappedVersions[version] = PhpInstallation(version)
|
||||
}
|
||||
|
||||
cachedPhpInstallations = mappedVersions
|
||||
|
||||
return versionsOnly
|
||||
}
|
||||
|
||||
/**
|
||||
Extracts valid PHP versions from an array of strings.
|
||||
This array of strings is usually retrieved from `grep`.
|
||||
*/
|
||||
public func extractPhpVersions(
|
||||
from versions: [String],
|
||||
checkBinaries: Bool = true
|
||||
) -> [String] {
|
||||
var output : [String] = []
|
||||
|
||||
versions.filter { (version) -> Bool in
|
||||
// Omit everything that doesn't start with php@
|
||||
// (e.g. something-php@8.0 won't be detected)
|
||||
return version.starts(with: "php@")
|
||||
}.forEach { (string) in
|
||||
let version = string.components(separatedBy: "php@")[1]
|
||||
// Only append the version if it doesn't already exist (avoid dupes),
|
||||
// is supported and where the binary exists (avoids broken installs)
|
||||
if !output.contains(version)
|
||||
&& Constants.SupportedPhpVersions.contains(version)
|
||||
&& (checkBinaries ? Shell.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
||||
{
|
||||
output.append(version)
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
public func validVersions(for constraint: String) -> [PhpVersionNumber] {
|
||||
constraint.split(separator: "|").flatMap {
|
||||
return PhpVersionNumberCollection
|
||||
.make(from: self.availablePhpVersions)
|
||||
.matching(constraint: $0.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
//
|
||||
// PhpVersionNumber.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/01/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct PhpVersionNumberCollection: Equatable {
|
||||
let versions: [PhpVersionNumber]
|
||||
|
||||
public static func make(from versions: [String]) -> Self {
|
||||
return PhpVersionNumberCollection(
|
||||
versions: versions.map { PhpVersionNumber.make(from: $0)! }
|
||||
)
|
||||
}
|
||||
|
||||
public var first: PhpVersionNumber? {
|
||||
return self.versions.first
|
||||
}
|
||||
|
||||
public var all: [PhpVersionNumber] {
|
||||
return self.versions
|
||||
}
|
||||
|
||||
/**
|
||||
Checks if any versions of PHP are valid for the constraint provided.
|
||||
Due to the complexity of evaluating these, a important test is maintained.
|
||||
More information on these constraints can be found here:
|
||||
https://getcomposer.org/doc/articles/versions.md#writing-version-constraints
|
||||
|
||||
- Parameter constraint: The full constraint as a string (e.g. "^7.0")
|
||||
- Parameter strict: Whether the minor version check is strict. See more below.
|
||||
|
||||
The strict mode does not matter if a patch version is provided for all versions in the collection.
|
||||
|
||||
Strict mode assumes that any PHP version lacking precise patch information, e.g. inferred
|
||||
from Homebrew corresponds to the .0 patch version of that version. The default, which is imprecise,
|
||||
assumes that the patch version is .999, which means that in all cases the patch version check is
|
||||
always going to pass.
|
||||
|
||||
**STRICT MODE (= patch precision on)**
|
||||
|
||||
Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in strict mode only 8.1.? will
|
||||
be considered valid (8.0 translates to 8.0.0 and as such is older than 8.0.1, 8.1.0 is OK).
|
||||
When checking against actual PHP versions installed by the user, use strict mode.
|
||||
|
||||
**NON-STRICT MODE (= patch precision off)**
|
||||
|
||||
Given versions 8.0.? and 8.1.?, but the requirement is ^8.0.1, in non-strict mode version 8.0
|
||||
is assumed to be equal to version 8.0.999, which is actually fine if 8.0.1 is the required version.
|
||||
In non-strict mode, the patch version is ignored for regular version checks (no caret / tilde).
|
||||
If checking compatibility with general Homebrew versions of PHP, do NOT use strict mode, since
|
||||
the patch version there is not used. (The formula php@8.0 suffices for ^8.0.1.)
|
||||
*/
|
||||
public func matching(constraint: String, strict: Bool = false) -> [PhpVersionNumber] {
|
||||
if let version = PhpVersionNumber.make(from: constraint, type: .versionOnly) {
|
||||
// Strict constraint (e.g. "7.0") -> returns specific version
|
||||
return self.versions.filter {
|
||||
$0.major == version.major
|
||||
&& $0.minor == version.minor
|
||||
&& (strict ? $0.patch(strict, version) == version.patch(strict) : true)
|
||||
}
|
||||
}
|
||||
|
||||
if let version = PhpVersionNumber.make(from: constraint, type: .caretVersionRange) {
|
||||
// Caret range means that the major version is never higher but minor version can be higher
|
||||
// ^7.2 will be compatible with all versions between 7.2 and 8.0
|
||||
return self.versions.filter {
|
||||
$0.major == version.major &&
|
||||
(
|
||||
// Either the minor version is the same and the patch is higher or equal
|
||||
$0.minor == version.minor && $0.patch(strict) >= version.patch(strict, $0)
|
||||
// or the minor version number has been bumped
|
||||
|| $0.minor > version.minor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if let version = PhpVersionNumber.make(from: constraint, type: .tildeVersionRange) {
|
||||
// Tilde range means that most specific digit is used as the basis.
|
||||
if version.patch != nil {
|
||||
// If a patch is provided then the minor version cannot be bumped.
|
||||
return self.versions.filter {
|
||||
$0.major == version.major && $0.minor == version.minor
|
||||
&& $0.patch(strict, version) >= version.patch!
|
||||
}
|
||||
} else {
|
||||
// If a patch is not provided then the major version cannot be bumped.
|
||||
return self.versions.filter {
|
||||
$0.major == version.major && $0.minor >= version.minor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
public struct PhpVersionNumber: Equatable {
|
||||
let major: Int
|
||||
let minor: Int
|
||||
let patch: Int?
|
||||
|
||||
public func patch(_ strictFallback: Bool, _ constraint: PhpVersionNumber? = nil) -> Int {
|
||||
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
||||
}
|
||||
|
||||
public var homebrewVersion: String {
|
||||
return "\(major).\(minor)"
|
||||
}
|
||||
|
||||
public enum MatchType: String {
|
||||
case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||
}
|
||||
|
||||
public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? {
|
||||
let regex = try! NSRegularExpression(pattern: type.rawValue, options: [])
|
||||
let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first
|
||||
|
||||
if match != nil {
|
||||
let major = Int(
|
||||
versionString[Range(match!.range(withName: "major"), in: versionString)!]
|
||||
)!
|
||||
let minor = Int(
|
||||
versionString[Range(match!.range(withName: "minor"), in: versionString)!]
|
||||
)!
|
||||
var patch: Int? = nil
|
||||
if let minorRange = Range(match!.range(withName: "patch"), in: versionString) {
|
||||
patch = Int(versionString[minorRange])
|
||||
}
|
||||
return Self(major: major, minor: minor, patch: patch)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
//
|
||||
// PhpInstallation.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 28/11/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PhpInstallation {
|
||||
|
||||
var longVersion: PhpVersionNumber
|
||||
|
||||
/**
|
||||
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
||||
in the relevant directory.
|
||||
*/
|
||||
init(_ version: String) {
|
||||
|
||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||
self.longVersion = PhpVersionNumber.make(from: version)!
|
||||
|
||||
if Shell.fileExists(phpConfigExecutablePath) {
|
||||
let longVersionString = Command.execute(
|
||||
path: phpConfigExecutablePath,
|
||||
arguments: ["--version"]
|
||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
self.longVersion = PhpVersionNumber.make(
|
||||
from: String(longVersionString.split(separator: "-")[0])
|
||||
)!
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
//
|
||||
// InternalSwitcher.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 24/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class InternalSwitcher: PhpSwitcher {
|
||||
|
||||
/**
|
||||
Switching to a new PHP version involves:
|
||||
- unlinking the current version
|
||||
- stopping the active services
|
||||
- linking the new desired version
|
||||
|
||||
Please note that depending on which version is installed,
|
||||
the version that is switched to may or may not be identical to `php`
|
||||
(without @version).
|
||||
*/
|
||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
||||
{
|
||||
Log.info("Switching to \(version), unlinking all versions...")
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
||||
group.enter()
|
||||
|
||||
DispatchQueue.global(qos: .userInitiated).async {
|
||||
let formula = (available == PhpEnv.brewPhpVersion)
|
||||
? "php" : "php@\(available)"
|
||||
|
||||
brew("unlink \(formula)")
|
||||
brew("services stop \(formula)", sudo: true)
|
||||
|
||||
Log.perf("Unlinked and stopped services for \(formula)")
|
||||
|
||||
group.leave()
|
||||
}
|
||||
}
|
||||
|
||||
group.notify(queue: .global(qos: .userInitiated)) {
|
||||
Log.info("All versions have been unlinked!")
|
||||
Log.info("Linking the new version!")
|
||||
|
||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||
brew("link \(formula) --overwrite --force")
|
||||
brew("services start \(formula)", sudo: true)
|
||||
|
||||
Log.info("The new version has been linked!")
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
//
|
||||
// PhpVersionSwitchContract.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 24/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol PhpSwitcher {
|
||||
|
||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
@ -1,29 +0,0 @@
|
||||
//
|
||||
// PhpVersionDetectionTest.swift
|
||||
// phpmon-tests
|
||||
//
|
||||
// Created by Nico Verbruggen on 01/04/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class PhpVersionDetectionTest: XCTestCase {
|
||||
|
||||
func testCanDetectValidPhpVersions() throws {
|
||||
let outcome = PhpEnv.shared.extractPhpVersions(from: [
|
||||
"", // empty lines should be omitted
|
||||
"php@8.0",
|
||||
"php@8.0", // should only be detected once
|
||||
"meta-php@8.0", // should be omitted, invalid
|
||||
"php@8.0-coolio", // should be omitted, invalid
|
||||
"php@7.0",
|
||||
"",
|
||||
"unrelatedphp@1.0", // should be omitted, invalid
|
||||
"php@5.6",
|
||||
"php@5.4" // should be omitted, not supported
|
||||
], checkBinaries: false)
|
||||
|
||||
XCTAssertEqual(outcome, ["8.0", "7.0", "5.6"])
|
||||
}
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
//
|
||||
// PhpVersionNumberTest.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/01/2022.
|
||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class PhpVersionNumberTest: XCTestCase {
|
||||
|
||||
func testCanDeconstructPhpVersion() throws {
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "8.0.11"),
|
||||
PhpVersionNumber(major: 8, minor: 0, patch: 11)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7.4.2"),
|
||||
PhpVersionNumber(major: 7, minor: 4, patch: 2)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7.4"),
|
||||
PhpVersionNumber(major: 7, minor: 4, patch: nil)
|
||||
)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumber.make(from: "7"),
|
||||
nil
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckFixedConstraints() throws {
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0"),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.3", "7.3.3", "7.2.3", "7.1.3", "7.0.3"])
|
||||
.matching(constraint: "7.0.3"),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0.3"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0.3", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "7.0.3", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: []).all
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckCaretConstraints() throws {
|
||||
// 1. Imprecise checks
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0.1", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 3. Imprecise check with precise constraint (strict mode)
|
||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "^7.0.1", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
||||
)
|
||||
|
||||
// 4. Precise members and constraint all around
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0.1", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 5. Precise members but imprecise constraint (strict mode)
|
||||
// In strict mode the constraint's patch version is assumed to be 0
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 6. Precise members but imprecise constraint (lenient mode)
|
||||
// In lenient mode the constraint's patch version is assumed to be equal
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "^7.0", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
}
|
||||
|
||||
func testCanCheckTildeConstraints() throws {
|
||||
// 1. Imprecise checks
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||
)
|
||||
|
||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0.1", strict: false),
|
||||
// One result because 7.0.1 to 7.0.x is expected.
|
||||
// 7.0.999 (assumed due to no strictness) is valid.
|
||||
// 7.1.0 and up are not valid (minor version is too high).
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0"]).all
|
||||
)
|
||||
|
||||
// 3. Imprecise check with precise constraint (strict mode)
|
||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||
.matching(constraint: "~7.0.1", strict: true),
|
||||
// No results because 7.0.1 to 7.0.x is expected.
|
||||
// 7.0.0 (assumed due to strictness) is not valid.
|
||||
// 7.1.0 and up are also not valid (minor version is too high).
|
||||
PhpVersionNumberCollection
|
||||
.make(from: []).all
|
||||
)
|
||||
|
||||
// 4. Precise members and constraint all around
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0.1", strict: true),
|
||||
// Only 7.0 with a patch version of .1 or higher is OK.
|
||||
// In this example, 7.0.10 is OK but all other versions are too new.
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.0.10"]).all
|
||||
)
|
||||
|
||||
// 5. Precise members but imprecise constraint (strict mode)
|
||||
// In strict mode the constraint's patch version is assumed to be 0.
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0", strict: true),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
|
||||
// 6. Precise members but imprecise constraint (lenient mode)
|
||||
// In lenient mode the constraint's patch version is assumed to be equal.
|
||||
// (Strictness does not make any difference here, but both should be tested.)
|
||||
XCTAssertEqual(
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"])
|
||||
.matching(constraint: "~7.0", strict: false),
|
||||
PhpVersionNumberCollection
|
||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||
)
|
||||
}
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name":"php",
|
||||
"full_name":"php",
|
||||
"tap":"homebrew/core",
|
||||
"oldname":null,
|
||||
"aliases":[
|
||||
"php@8.0"
|
||||
],
|
||||
"versioned_formulae":[
|
||||
"php@7.4",
|
||||
"php@7.3",
|
||||
"php@7.2"
|
||||
],
|
||||
"desc":"General-purpose scripting language",
|
||||
"license":"PHP-3.01",
|
||||
"homepage":"https://www.php.net/",
|
||||
"versions":{
|
||||
"stable":"8.0.2",
|
||||
"head":"HEAD",
|
||||
"bottle":true
|
||||
},
|
||||
"urls":{
|
||||
"stable":{
|
||||
"url":"https://www.php.net/distributions/php-8.0.2.tar.xz",
|
||||
"tag":null,
|
||||
"revision":null
|
||||
}
|
||||
},
|
||||
"revision":0,
|
||||
"version_scheme":0,
|
||||
"bottle":{
|
||||
"stable":{
|
||||
"rebuild":0,
|
||||
"cellar":"/opt/homebrew/Cellar",
|
||||
"prefix":"/opt/homebrew",
|
||||
"root_url":"https://homebrew.bintray.com/bottles",
|
||||
"files":{
|
||||
"arm64_big_sur":{
|
||||
"url":"https://homebrew.bintray.com/bottles/php-8.0.2.arm64_big_sur.bottle.tar.gz",
|
||||
"sha256":"cbefa1db73d08b9af4593a44512b8d727e43033ee8517736bae5f16315501b12"
|
||||
},
|
||||
"big_sur":{
|
||||
"url":"https://homebrew.bintray.com/bottles/php-8.0.2.big_sur.bottle.tar.gz",
|
||||
"sha256":"6857142e12254b15da4e74c2986dd24faca57dac8d467b04621db349e277dd63"
|
||||
},
|
||||
"catalina":{
|
||||
"url":"https://homebrew.bintray.com/bottles/php-8.0.2.catalina.bottle.tar.gz",
|
||||
"sha256":"b651611134c18f93fdf121a4277b51b197a896a19ccb8020289b4e19e0638349"
|
||||
},
|
||||
"mojave":{
|
||||
"url":"https://homebrew.bintray.com/bottles/php-8.0.2.mojave.bottle.tar.gz",
|
||||
"sha256":"9583a51fcc6f804aadbb14e18f770d4fb4973deaed6ddc4770342e62974ffbca"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keg_only":false,
|
||||
"bottle_disabled":false,
|
||||
"options":[
|
||||
|
||||
],
|
||||
"build_dependencies":[
|
||||
"httpd",
|
||||
"pkg-config"
|
||||
],
|
||||
"dependencies":[
|
||||
"apr",
|
||||
"apr-util",
|
||||
"argon2",
|
||||
"aspell",
|
||||
"autoconf",
|
||||
"curl",
|
||||
"freetds",
|
||||
"gd",
|
||||
"gettext",
|
||||
"glib",
|
||||
"gmp",
|
||||
"icu4c",
|
||||
"krb5",
|
||||
"libffi",
|
||||
"libpq",
|
||||
"libsodium",
|
||||
"libzip",
|
||||
"oniguruma",
|
||||
"openldap",
|
||||
"openssl@1.1",
|
||||
"pcre2",
|
||||
"sqlite",
|
||||
"tidy-html5",
|
||||
"unixodbc"
|
||||
],
|
||||
"recommended_dependencies":[
|
||||
|
||||
],
|
||||
"optional_dependencies":[
|
||||
|
||||
],
|
||||
"uses_from_macos":[
|
||||
{
|
||||
"xz":"build"
|
||||
},
|
||||
"bzip2",
|
||||
"libedit",
|
||||
"libxml2",
|
||||
"libxslt",
|
||||
"zlib"
|
||||
],
|
||||
"requirements":[
|
||||
|
||||
],
|
||||
"conflicts_with":[
|
||||
|
||||
],
|
||||
"caveats":"To enable PHP in Apache add the following to httpd.conf and restart Apache:\n LoadModule php_module $(brew --prefix)/opt/php/lib/httpd/modules/libphp.so\n\n <FilesMatch \\.php$>\n SetHandler application/x-httpd-php\n </FilesMatch>\n\nFinally, check DirectoryIndex includes index.php\n DirectoryIndex index.php index.html\n\nThe php.ini and php-fpm.ini file can be found in:\n $(brew --prefix)/etc/php/8.0/\n",
|
||||
"installed":[
|
||||
{
|
||||
"version":"8.0.2",
|
||||
"used_options":[
|
||||
|
||||
],
|
||||
"built_as_bottle":true,
|
||||
"poured_from_bottle":true,
|
||||
"runtime_dependencies":[
|
||||
{
|
||||
"full_name":"apr",
|
||||
"version":"1.7.0"
|
||||
},
|
||||
{
|
||||
"full_name":"openssl@1.1",
|
||||
"version":"1.1.1i"
|
||||
},
|
||||
{
|
||||
"full_name":"apr-util",
|
||||
"version":"1.6.1"
|
||||
},
|
||||
{
|
||||
"full_name":"argon2",
|
||||
"version":"20190702"
|
||||
},
|
||||
{
|
||||
"full_name":"aspell",
|
||||
"version":"0.60.8"
|
||||
},
|
||||
{
|
||||
"full_name":"autoconf",
|
||||
"version":"2.69"
|
||||
},
|
||||
{
|
||||
"full_name":"brotli",
|
||||
"version":"1.0.9"
|
||||
},
|
||||
{
|
||||
"full_name":"gettext",
|
||||
"version":"0.21"
|
||||
},
|
||||
{
|
||||
"full_name":"libunistring",
|
||||
"version":"0.9.10"
|
||||
},
|
||||
{
|
||||
"full_name":"libidn2",
|
||||
"version":"2.3.0"
|
||||
},
|
||||
{
|
||||
"full_name":"libmetalink",
|
||||
"version":"0.1.3"
|
||||
},
|
||||
{
|
||||
"full_name":"libssh2",
|
||||
"version":"1.9.0"
|
||||
},
|
||||
{
|
||||
"full_name":"c-ares",
|
||||
"version":"1.17.1"
|
||||
},
|
||||
{
|
||||
"full_name":"jemalloc",
|
||||
"version":"5.2.1"
|
||||
},
|
||||
{
|
||||
"full_name":"libev",
|
||||
"version":"4.33"
|
||||
},
|
||||
{
|
||||
"full_name":"nghttp2",
|
||||
"version":"1.43.0"
|
||||
},
|
||||
{
|
||||
"full_name":"openldap",
|
||||
"version":"2.4.57"
|
||||
},
|
||||
{
|
||||
"full_name":"rtmpdump",
|
||||
"version":"2.4+20151223"
|
||||
},
|
||||
{
|
||||
"full_name":"zstd",
|
||||
"version":"1.4.8"
|
||||
},
|
||||
{
|
||||
"full_name":"curl",
|
||||
"version":"7.75.0"
|
||||
},
|
||||
{
|
||||
"full_name":"libtool",
|
||||
"version":"2.4.6"
|
||||
},
|
||||
{
|
||||
"full_name":"unixodbc",
|
||||
"version":"2.3.9"
|
||||
},
|
||||
{
|
||||
"full_name":"freetds",
|
||||
"version":"1.2.18"
|
||||
},
|
||||
{
|
||||
"full_name":"libpng",
|
||||
"version":"1.6.37"
|
||||
},
|
||||
{
|
||||
"full_name":"freetype",
|
||||
"version":"2.10.4"
|
||||
},
|
||||
{
|
||||
"full_name":"fontconfig",
|
||||
"version":"2.13.1"
|
||||
},
|
||||
{
|
||||
"full_name":"jpeg",
|
||||
"version":"9d"
|
||||
},
|
||||
{
|
||||
"full_name":"libtiff",
|
||||
"version":"4.2.0"
|
||||
},
|
||||
{
|
||||
"full_name":"webp",
|
||||
"version":"1.2.0"
|
||||
},
|
||||
{
|
||||
"full_name":"gd",
|
||||
"version":"2.3.1"
|
||||
},
|
||||
{
|
||||
"full_name":"libffi",
|
||||
"version":"3.3"
|
||||
},
|
||||
{
|
||||
"full_name":"pcre",
|
||||
"version":"8.44"
|
||||
},
|
||||
{
|
||||
"full_name":"gdbm",
|
||||
"version":"1.18.1"
|
||||
},
|
||||
{
|
||||
"full_name":"readline",
|
||||
"version":"8.1"
|
||||
},
|
||||
{
|
||||
"full_name":"sqlite",
|
||||
"version":"3.34.0"
|
||||
},
|
||||
{
|
||||
"full_name":"tcl-tk",
|
||||
"version":"8.6.11"
|
||||
},
|
||||
{
|
||||
"full_name":"xz",
|
||||
"version":"5.2.5"
|
||||
},
|
||||
{
|
||||
"full_name":"python@3.9",
|
||||
"version":"3.9.1"
|
||||
},
|
||||
{
|
||||
"full_name":"glib",
|
||||
"version":"2.66.6"
|
||||
},
|
||||
{
|
||||
"full_name":"gmp",
|
||||
"version":"6.2.1"
|
||||
},
|
||||
{
|
||||
"full_name":"icu4c",
|
||||
"version":"67.1"
|
||||
},
|
||||
{
|
||||
"full_name":"krb5",
|
||||
"version":"1.19"
|
||||
},
|
||||
{
|
||||
"full_name":"libpq",
|
||||
"version":"13.1"
|
||||
},
|
||||
{
|
||||
"full_name":"libsodium",
|
||||
"version":"1.0.18"
|
||||
},
|
||||
{
|
||||
"full_name":"libzip",
|
||||
"version":"1.7.3"
|
||||
},
|
||||
{
|
||||
"full_name":"oniguruma",
|
||||
"version":"6.9.6"
|
||||
},
|
||||
{
|
||||
"full_name":"pcre2",
|
||||
"version":"10.36"
|
||||
},
|
||||
{
|
||||
"full_name":"tidy-html5",
|
||||
"version":"5.6.0"
|
||||
}
|
||||
],
|
||||
"installed_as_dependency":false,
|
||||
"installed_on_request":true
|
||||
}
|
||||
],
|
||||
"linked_keg":"8.0.2",
|
||||
"pinned":false,
|
||||
"outdated":false,
|
||||
"deprecated":false,
|
||||
"deprecation_date":null,
|
||||
"deprecation_reason":null,
|
||||
"disabled":false,
|
||||
"disable_date":null,
|
||||
"disable_reason":null
|
||||
}
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
//
|
||||
// ValetTest.swift
|
||||
// phpmon-tests
|
||||
//
|
||||
// Created by Nico Verbruggen on 29/11/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class ValetTest: XCTestCase {
|
||||
|
||||
func testDetermineValetVersion() {
|
||||
let version = valet("--version")
|
||||
XCTAssert(version.contains("Laravel Valet 2."))
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 33 KiB |
BIN
phpmon-updater/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 811 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 97 KiB |
BIN
phpmon-updater/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 301 KiB |
6
phpmon-updater/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
46
phpmon-updater/LaunchControl.swift
Normal file
@ -0,0 +1,46 @@
|
||||
//
|
||||
// LaunchControl.swift
|
||||
// PHP Monitor Self-Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 02/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class LaunchControl {
|
||||
public static func smartRestart(priority: [String]) async {
|
||||
for appPath in priority {
|
||||
if FileManager.default.fileExists(atPath: appPath) {
|
||||
let app = await LaunchControl.startApplication(at: appPath)
|
||||
if app != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func terminateApplications(bundleIds: [String]) async {
|
||||
let runningApplications = NSWorkspace.shared.runningApplications
|
||||
|
||||
// Terminate all instances found
|
||||
for id in bundleIds {
|
||||
if let phpmon = runningApplications.first(where: {
|
||||
(application) in return application.bundleIdentifier == id
|
||||
}) {
|
||||
phpmon.terminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func startApplication(at path: String) async -> NSRunningApplication? {
|
||||
await withCheckedContinuation { continuation in
|
||||
let url = NSURL(fileURLWithPath: path, isDirectory: true) as URL
|
||||
let configuration = NSWorkspace.OpenConfiguration()
|
||||
NSWorkspace.shared.openApplication(at: url, configuration: configuration) { phpmon, error in
|
||||
continuation.resume(returning: phpmon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
162
phpmon-updater/Updater.swift
Normal file
@ -0,0 +1,162 @@
|
||||
//
|
||||
// Updater.swift
|
||||
// PHP Monitor Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 01/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
class Updater: NSObject, NSApplicationDelegate {
|
||||
|
||||
var updaterDirectory: String = ""
|
||||
var manifestPath: String = ""
|
||||
var manifest: ReleaseManifest! = nil
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
Task { await self.installUpdate() }
|
||||
}
|
||||
|
||||
func installUpdate() async {
|
||||
print("PHP MONITOR SELF-UPDATER by Nico Verbruggen")
|
||||
print("===========================================")
|
||||
|
||||
self.updaterDirectory = "~/.config/phpmon/updater"
|
||||
.replacingOccurrences(of: "~", with: NSHomeDirectory())
|
||||
|
||||
print("Updater directory set to: \(self.updaterDirectory)")
|
||||
|
||||
self.manifestPath = "\(updaterDirectory)/update.json"
|
||||
|
||||
// Fetch the manifest on the local filesystem
|
||||
let manifest = await parseManifest()!
|
||||
|
||||
// Download the latest file
|
||||
let zipPath = await download(manifest)
|
||||
|
||||
// Terminate all instances of PHP Monitor first
|
||||
await LaunchControl.terminateApplications(bundleIds: [
|
||||
"com.nicoverbruggen.phpmon.eap",
|
||||
"com.nicoverbruggen.phpmon.dev",
|
||||
"com.nicoverbruggen.phpmon"
|
||||
])
|
||||
|
||||
// Install the app based on the zip
|
||||
let appPath = await extractAndInstall(zipPath: zipPath)
|
||||
|
||||
// Restart PHP Monitor, this will also close the updater
|
||||
_ = await LaunchControl.startApplication(at: appPath)
|
||||
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ aNotification: Notification) {
|
||||
exit(1)
|
||||
}
|
||||
|
||||
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
private func parseManifest() async -> ReleaseManifest? {
|
||||
// Read out the correct information from the manifest JSON
|
||||
print("Checking manifest file at \(manifestPath)...")
|
||||
|
||||
do {
|
||||
let manifestText = try String(contentsOfFile: manifestPath)
|
||||
manifest = try JSONDecoder().decode(ReleaseManifest.self, from: manifestText.data(using: .utf8)!)
|
||||
return manifest
|
||||
} catch {
|
||||
print("Parsing the manifest failed (or the manifest file doesn't exist)!")
|
||||
await Alert.show(description: "The manifest file for a potential update was not found. Please try searching for updates again in PHP Monitor.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func download(_ manifest: ReleaseManifest) async -> String {
|
||||
// Remove all zips
|
||||
system_quiet("rm -rf \(updaterDirectory)/*.zip")
|
||||
|
||||
// Download the file (and follow redirects + no output on failure)
|
||||
system_quiet("cd \"\(updaterDirectory)\" && curl \(manifest.url) -fLO --max-time 20")
|
||||
|
||||
// Identify the downloaded file
|
||||
let filename = system("cd \"\(updaterDirectory)\" && ls | grep .zip")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Ensure the zip exists
|
||||
if filename.isEmpty {
|
||||
print("The update has not been downloaded. Sadly, that means that PHP Monitor cannot not updated!")
|
||||
await Alert.show(description: "The update could not be downloaded, or the file was not correctly written to disk. \n\nPlease try again. \n\n(Note that the download will time-out after 20 seconds, so for slow connections it is recommended to manually download the update.)")
|
||||
}
|
||||
|
||||
// Calculate the checksum for the downloaded file
|
||||
let checksum = system("openssl dgst -sha256 \"\(updaterDirectory)/\(filename)\" | awk '{print $NF}'")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
// Compare the checksums
|
||||
print("""
|
||||
Comparing checksums...
|
||||
Expected SHA256: \(manifest.sha256)
|
||||
Actual SHA256: \(checksum)
|
||||
""")
|
||||
|
||||
// Make sure the checksum matches before we do anything with the file
|
||||
if checksum != manifest.sha256 {
|
||||
print("The checksums failed to match. Cancelling!")
|
||||
await Alert.show(description: "The downloaded update failed checksum validation. Please try again. If this issue persists, there may be an issue with the server and I do not recommend upgrading.")
|
||||
}
|
||||
|
||||
// Return the path to the zip
|
||||
return "\(updaterDirectory)/\(filename)"
|
||||
}
|
||||
|
||||
private func extractAndInstall(zipPath: String) async -> String {
|
||||
// Remove the directory that will contain the extracted update
|
||||
system_quiet("rm -rf \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Recreate the directory where we will unzip the .app file
|
||||
system_quiet("mkdir -p \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Make sure the updater directory exists
|
||||
var isDirectory: ObjCBool = true
|
||||
if !FileManager.default.fileExists(atPath: "\(updaterDirectory)/extracted", isDirectory: &isDirectory) {
|
||||
await Alert.show(description: "The updater directory is missing. The automatic updater will quit. Make sure that ` ~/.config/phpmon/updater` is writeable.")
|
||||
}
|
||||
|
||||
// Unzip the file
|
||||
system_quiet("unzip \"\(zipPath)\" -d \"\(updaterDirectory)/extracted\"")
|
||||
|
||||
// Find the .app file
|
||||
let app = system("ls \"\(updaterDirectory)/extracted\" | grep .app")
|
||||
.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
|
||||
print("Finished extracting: \(updaterDirectory)/extracted/\(app)")
|
||||
|
||||
// Make sure the file was extracted
|
||||
if app.isEmpty {
|
||||
await Alert.show(description: "The downloaded file could not be extracted. The automatic updater will quit. Make sure that ` ~/.config/phpmon/updater` is writeable.")
|
||||
}
|
||||
|
||||
// Remove the original app
|
||||
print("Removing \(app) before replacing...")
|
||||
system_quiet("rm -rf \"/Applications/\(app)\"")
|
||||
|
||||
// Move the new app in place
|
||||
system_quiet("mv \"\(updaterDirectory)/extracted/\(app)\" \"/Applications/\(app)\"")
|
||||
|
||||
// Remove the zip
|
||||
system_quiet("rm \"\(zipPath)\"")
|
||||
|
||||
// Remove the manifest
|
||||
system_quiet("rm \"\(manifestPath)\"")
|
||||
|
||||
// Write a file that is only written when we upgraded successfully
|
||||
system_quiet("touch \"\(updaterDirectory)/upgrade.success\"")
|
||||
|
||||
// Return the new location of the app
|
||||
return "/Applications/\(app)"
|
||||
}
|
||||
}
|
34
phpmon-updater/Utility.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Utility.swift
|
||||
// PHP Monitor Self-Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 02/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
class Alert {
|
||||
public static func show(description: String, shouldExit: Bool = true) async {
|
||||
await withUnsafeContinuation { continuation in
|
||||
DispatchQueue.main.async {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "The app could not be updated."
|
||||
alert.informativeText = description
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.alertStyle = .critical
|
||||
alert.runModal()
|
||||
if shouldExit {
|
||||
exit(0)
|
||||
}
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ReleaseManifest: Codable {
|
||||
let url: String
|
||||
let sha256: String
|
||||
}
|
14
phpmon-updater/main.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// PHP Monitor Self-Updater
|
||||
//
|
||||
// Created by Nico Verbruggen on 01/02/2023.
|
||||
// Copyright © 2023 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
|
||||
let app = NSApplication.shared
|
||||
let delegate = Updater()
|
||||
app.delegate = delegate
|
||||
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
|
5
phpmon-updater/phpmon-updater.entitlements
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
38
phpmon/Assets.xcassets/AppColor.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.988",
|
||||
"green" : "0.580",
|
||||
"red" : "0.278"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.988",
|
||||
"green" : "0.723",
|
||||
"red" : "0.277"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 134 KiB |
68
phpmon/Assets.xcassets/AppIconDev.appiconset/Contents.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_128x128.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 632 B |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_256x256.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_512x512.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 163 KiB |
68
phpmon/Assets.xcassets/AppIconEAP.appiconset/Contents.json
Normal file
@ -0,0 +1,68 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_16x16.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_16x16@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "16x16"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_32x32@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "32x32"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_128x128@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "128x128"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_256x256@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "256x256"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "1x",
|
||||
"size" : "512x512"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_512x512@2x.png",
|
||||
"idiom" : "mac",
|
||||
"scale" : "2x",
|
||||
"size" : "512x512"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_128x128.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 644 B |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_256x256.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_512x512.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconEAP.appiconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 162 KiB |
38
phpmon/Assets.xcassets/AppSecondary.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.250",
|
||||
"green" : "0.250",
|
||||
"red" : "0.250"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.750",
|
||||
"green" : "0.750",
|
||||
"red" : "0.750"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
38
phpmon/Assets.xcassets/IconColorGreen.colorset/Contents.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.501",
|
||||
"green" : "0.697",
|
||||
"red" : "0.247"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.501",
|
||||
"green" : "0.765",
|
||||
"red" : "0.247"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|