FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
typephp-webman/support/Setup.php at main · webman-php/typephp-webman · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
webman-php
/
typephp-webman
Public
forked from
Tinywan/tinywan-typephp-webman
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
typephp-webman
/
support
/
Setup.php
Copy path
More file actions
More file actions
Latest commit
History
History
History
1558 lines (1408 loc) · 76.1 KB
Breadcrumbs
typephp-webman
/
support
/
Setup.php
Copy path
File metadata and controls
1558 lines (1408 loc) · 76.1 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
declare
(strict_types=
1
);
namespace
support
;
use
Composer
\
Console
\
Application
as
ComposerApplication
;
use
Composer
\
IO
\
IOInterface
;
use
Composer
\
Script
\
Event
;
use
Symfony
\
Component
\
Console
\
Cursor
;
use
Symfony
\
Component
\
Console
\
Input
\
ArrayInput
;
use
Symfony
\
Component
\
Console
\
Output
\
ConsoleOutput
;
use
Symfony
\
Component
\
Console
\
Terminal
;
/**
* create-project setup wizard: interactive locale, timezone and optional components selection, then runs composer require.
*/
class
Setup
{
// --- Optional component package names ---
private
const
PACKAGE_CONSOLE
=
'
webman/console
'
;
private
const
PACKAGE_DATABASE
=
'
webman/database
'
;
private
const
PACKAGE_THINK_ORM
=
'
webman/think-orm
'
;
private
const
PACKAGE_REDIS
=
'
webman/redis
'
;
private
const
PACKAGE_ILLUMINATE_EVENTS
=
'
illuminate/events
'
;
private
const
PACKAGE_ILLUMINATE_PAGINATION
=
'
illuminate/pagination
'
;
private
const
PACKAGE_SYMFONY_VAR_DUMPER
=
'
symfony/var-dumper
'
;
private
const
PACKAGE_VALIDATION
=
'
webman/validation
'
;
private
const
PACKAGE_BLADE
=
'
webman/blade
'
;
private
const
PACKAGE_TWIG
=
'
twig/twig
'
;
private
const
PACKAGE_THINK_TEMPLATE
=
'
topthink/think-template
'
;
private
const
SETUP_TITLE
=
'
Webman Setup
'
;
// --- Timezone regions ---
private
const
TIMEZONE_REGIONS
= [
'
Asia
'
=> \DateTimeZone::
ASIA
,
'
Europe
'
=> \DateTimeZone::
EUROPE
,
'
America
'
=> \DateTimeZone::
AMERICA
,
'
Africa
'
=> \DateTimeZone::
AFRICA
,
'
Australia
'
=> \DateTimeZone::
AUSTRALIA
,
'
Pacific
'
=> \DateTimeZone::
PACIFIC
,
'
Atlantic
'
=> \DateTimeZone::
ATLANTIC
,
'
Indian
'
=> \DateTimeZone::
INDIAN
,
'
Antarctica
'
=> \DateTimeZone::
ANTARCTICA
,
'
Arctic
'
=> \DateTimeZone::
ARCTIC
,
'
UTC
'
=> \DateTimeZone::
UTC
,
];
// --- Locale => default timezone ---
private
const
LOCALE_DEFAULT_TIMEZONES
= [
'
zh_CN
'
=>
'
Asia/Shanghai
'
,
'
zh_TW
'
=>
'
Asia/Taipei
'
,
'
en
'
=>
'
UTC
'
,
'
ja
'
=>
'
Asia/Tokyo
'
,
'
ko
'
=>
'
Asia/Seoul
'
,
'
fr
'
=>
'
Europe/Paris
'
,
'
de
'
=>
'
Europe/Berlin
'
,
'
es
'
=>
'
Europe/Madrid
'
,
'
pt_BR
'
=>
'
America/Sao_Paulo
'
,
'
ru
'
=>
'
Europe/Moscow
'
,
'
vi
'
=>
'
Asia/Ho_Chi_Minh
'
,
'
tr
'
=>
'
Europe/Istanbul
'
,
'
id
'
=>
'
Asia/Jakarta
'
,
'
th
'
=>
'
Asia/Bangkok
'
,
];
// --- Locale options (localized display names) ---
private
const
LOCALE_LABELS
= [
'
zh_CN
'
=>
'
简体中文
'
,
'
zh_TW
'
=>
'
繁體中文
'
,
'
en
'
=>
'
English
'
,
'
ja
'
=>
'
日本語
'
,
'
ko
'
=>
'
한국어
'
,
'
fr
'
=>
'
Français
'
,
'
de
'
=>
'
Deutsch
'
,
'
es
'
=>
'
Español
'
,
'
pt_BR
'
=>
'
Português (Brasil)
'
,
'
ru
'
=>
'
Русский
'
,
'
vi
'
=>
'
Tiếng Việt
'
,
'
tr
'
=>
'
Türkçe
'
,
'
id
'
=>
'
Bahasa Indonesia
'
,
'
th
'
=>
'
ไทย
'
,
];
// --- Multilingual messages (%s = placeholder) ---
private
const
MESSAGES
= [
'
zh_CN
'
=> [
'
remove_package_question
'
=>
'
发现以下已安装组件本次未选择,是否将其卸载 ?%s
'
,
'
removing_package
'
=>
'
- 准备移除组件 %s
'
,
'
removing
'
=>
'
卸载:
'
,
'
error_remove
'
=>
'
卸载组件出错,请手动执行:composer remove %s
'
,
'
done_remove
'
=>
'
已卸载组件。
'
,
'
skip
'
=>
'
非交互模式,跳过安装向导。
'
,
'
default_choice
'
=>
'
(默认 %s)
'
,
'
timezone_prompt
'
=>
'
时区 (默认 %s,输入可联想补全):
'
,
'
timezone_title
'
=>
'
时区设置 (默认 %s)
'
,
'
timezone_help
'
=>
'
输入关键字Tab自动补全,可↑↓下选择:
'
,
'
timezone_region
'
=>
'
选择时区区域
'
,
'
timezone_city
'
=>
'
选择时区
'
,
'
timezone_invalid
'
=>
'
无效的时区,已使用默认值 %s
'
,
'
timezone_input_prompt
'
=>
'
输入时区或关键字:
'
,
'
timezone_pick_prompt
'
=>
'
请输入数字编号或关键字:
'
,
'
timezone_no_match
'
=>
'
未找到匹配的时区,请重试。
'
,
'
timezone_invalid_index
'
=>
'
无效的编号,请重新输入。
'
,
'
yes
'
=>
'
是
'
,
'
no
'
=>
'
否
'
,
'
adding_package
'
=>
'
- 添加依赖 %s
'
,
'
console_question
'
=>
'
安装命令行组件 webman/console
'
,
'
db_question
'
=>
'
数据库组件
'
,
'
db_none
'
=>
'
不安装
'
,
'
db_invalid
'
=>
'
请输入有效选项
'
,
'
redis_question
'
=>
'
安装 Redis 组件 webman/redis
'
,
'
events_note
'
=>
'
(Redis 依赖 illuminate/events,已自动包含)
'
,
'
validation_question
'
=>
'
安装验证器组件 webman/validation
'
,
'
template_question
'
=>
'
模板引擎
'
,
'
template_none
'
=>
'
不安装
'
,
'
no_components
'
=>
'
未选择额外组件。
'
,
'
installing
'
=>
'
即将安装:
'
,
'
running
'
=>
'
执行:
'
,
'
error_install
'
=>
'
安装可选组件时出错,请手动执行:composer require %s
'
,
'
done
'
=>
'
可选组件安装完成。
'
,
'
summary_locale
'
=>
'
语言:%s
'
,
'
summary_timezone
'
=>
'
时区:%s
'
,
],
'
zh_TW
'
=> [
'
skip
'
=>
'
非交互模式,跳過安裝嚮導。
'
,
'
default_choice
'
=>
'
(預設 %s)
'
,
'
timezone_prompt
'
=>
'
時區 (預設 %s,輸入可聯想補全):
'
,
'
timezone_title
'
=>
'
時區設定 (預設 %s)
'
,
'
timezone_help
'
=>
'
輸入關鍵字Tab自動補全,可↑↓上下選擇:
'
,
'
timezone_region
'
=>
'
選擇時區區域
'
,
'
timezone_city
'
=>
'
選擇時區
'
,
'
timezone_invalid
'
=>
'
無效的時區,已使用預設值 %s
'
,
'
timezone_input_prompt
'
=>
'
輸入時區或關鍵字:
'
,
'
timezone_pick_prompt
'
=>
'
請輸入數字編號或關鍵字:
'
,
'
timezone_no_match
'
=>
'
未找到匹配的時區,請重試。
'
,
'
timezone_invalid_index
'
=>
'
無效的編號,請重新輸入。
'
,
'
yes
'
=>
'
是
'
,
'
no
'
=>
'
否
'
,
'
adding_package
'
=>
'
- 新增依賴 %s
'
,
'
console_question
'
=>
'
安裝命令列組件 webman/console
'
,
'
db_question
'
=>
'
資料庫組件
'
,
'
db_none
'
=>
'
不安裝
'
,
'
db_invalid
'
=>
'
請輸入有效選項
'
,
'
redis_question
'
=>
'
安裝 Redis 組件 webman/redis
'
,
'
events_note
'
=>
'
(Redis 依賴 illuminate/events,已自動包含)
'
,
'
validation_question
'
=>
'
安裝驗證器組件 webman/validation
'
,
'
template_question
'
=>
'
模板引擎
'
,
'
template_none
'
=>
'
不安裝
'
,
'
no_components
'
=>
'
未選擇額外組件。
'
,
'
installing
'
=>
'
即將安裝:
'
,
'
running
'
=>
'
執行:
'
,
'
error_install
'
=>
'
安裝可選組件時出錯,請手動執行:composer require %s
'
,
'
done
'
=>
'
可選組件安裝完成。
'
,
'
summary_locale
'
=>
'
語言:%s
'
,
'
summary_timezone
'
=>
'
時區:%s
'
,
],
'
en
'
=> [
'
skip
'
=>
'
Non-interactive mode, skipping setup wizard.
'
,
'
default_choice
'
=>
'
(default %s)
'
,
'
timezone_prompt
'
=>
'
Timezone (default=%s, type to autocomplete):
'
,
'
timezone_title
'
=>
'
Timezone (default=%s)
'
,
'
timezone_help
'
=>
'
Type keyword then press Tab to autocomplete, use ↑↓ to choose:
'
,
'
timezone_region
'
=>
'
Select timezone region
'
,
'
timezone_city
'
=>
'
Select timezone
'
,
'
timezone_invalid
'
=>
'
Invalid timezone, using default %s
'
,
'
timezone_input_prompt
'
=>
'
Enter timezone or keyword:
'
,
'
timezone_pick_prompt
'
=>
'
Enter number or keyword:
'
,
'
timezone_no_match
'
=>
'
No matching timezone found, please try again.
'
,
'
timezone_invalid_index
'
=>
'
Invalid number, please try again.
'
,
'
yes
'
=>
'
yes
'
,
'
no
'
=>
'
no
'
,
'
adding_package
'
=>
'
- Adding package %s
'
,
'
console_question
'
=>
'
Install console component webman/console
'
,
'
db_question
'
=>
'
Database component
'
,
'
db_none
'
=>
'
None
'
,
'
db_invalid
'
=>
'
Please enter a valid option
'
,
'
redis_question
'
=>
'
Install Redis component webman/redis
'
,
'
events_note
'
=>
'
(Redis requires illuminate/events, automatically included)
'
,
'
validation_question
'
=>
'
Install validator component webman/validation
'
,
'
template_question
'
=>
'
Template engine
'
,
'
template_none
'
=>
'
None
'
,
'
no_components
'
=>
'
No optional components selected.
'
,
'
installing
'
=>
'
Installing:
'
,
'
running
'
=>
'
Running:
'
,
'
error_install
'
=>
'
Failed to install. Try manually: composer require %s
'
,
'
done
'
=>
'
Optional components installed.
'
,
'
summary_locale
'
=>
'
Language: %s
'
,
'
summary_timezone
'
=>
'
Timezone: %s
'
,
],
'
ja
'
=> [
'
skip
'
=>
'
非対話モードのため、セットアップウィザードをスキップします。
'
,
'
default_choice
'
=>
'
(デフォルト %s)
'
,
'
timezone_prompt
'
=>
'
タイムゾーン (デフォルト=%s、入力で補完):
'
,
'
timezone_title
'
=>
'
タイムゾーン (デフォルト=%s)
'
,
'
timezone_help
'
=>
'
キーワード入力→Tabで補完、↑↓で選択:
'
,
'
timezone_region
'
=>
'
タイムゾーンの地域を選択
'
,
'
timezone_city
'
=>
'
タイムゾーンを選択
'
,
'
timezone_invalid
'
=>
'
無効なタイムゾーンです。デフォルト %s を使用します
'
,
'
timezone_input_prompt
'
=>
'
タイムゾーンまたはキーワードを入力:
'
,
'
timezone_pick_prompt
'
=>
'
番号またはキーワードを入力:
'
,
'
timezone_no_match
'
=>
'
一致するタイムゾーンが見つかりません。再試行してください。
'
,
'
timezone_invalid_index
'
=>
'
無効な番号です。もう一度入力してください。
'
,
'
yes
'
=>
'
はい
'
,
'
no
'
=>
'
いいえ
'
,
'
adding_package
'
=>
'
- パッケージを追加 %s
'
,
'
console_question
'
=>
'
コンソールコンポーネント webman/console をインストール
'
,
'
db_question
'
=>
'
データベースコンポーネント
'
,
'
db_none
'
=>
'
インストールしない
'
,
'
db_invalid
'
=>
'
有効なオプションを入力してください
'
,
'
redis_question
'
=>
'
Redis コンポーネント webman/redis をインストール
'
,
'
events_note
'
=>
'
(Redis は illuminate/events が必要です。自動的に含まれます)
'
,
'
validation_question
'
=>
'
バリデーションコンポーネント webman/validation をインストール
'
,
'
template_question
'
=>
'
テンプレートエンジン
'
,
'
template_none
'
=>
'
インストールしない
'
,
'
no_components
'
=>
'
オプションコンポーネントが選択されていません。
'
,
'
installing
'
=>
'
インストール中:
'
,
'
running
'
=>
'
実行中:
'
,
'
error_install
'
=>
'
インストールに失敗しました。手動で実行してください:composer require %s
'
,
'
done
'
=>
'
オプションコンポーネントのインストールが完了しました。
'
,
'
summary_locale
'
=>
'
言語:%s
'
,
'
summary_timezone
'
=>
'
タイムゾーン:%s
'
,
],
'
ko
'
=> [
'
skip
'
=>
'
비대화형 모드입니다. 설치 마법사를 건너뜁니다.
'
,
'
default_choice
'
=>
'
(기본값 %s)
'
,
'
timezone_prompt
'
=>
'
시간대 (기본값=%s, 입력하여 자동완성):
'
,
'
timezone_title
'
=>
'
시간대 (기본값=%s)
'
,
'
timezone_help
'
=>
'
키워드 입력 후 Tab 자동완성, ↑↓로 선택:
'
,
'
timezone_region
'
=>
'
시간대 지역 선택
'
,
'
timezone_city
'
=>
'
시간대 선택
'
,
'
timezone_invalid
'
=>
'
잘못된 시간대입니다. 기본값 %s 을(를) 사용합니다
'
,
'
timezone_input_prompt
'
=>
'
시간대 또는 키워드 입력:
'
,
'
timezone_pick_prompt
'
=>
'
번호 또는 키워드 입력:
'
,
'
timezone_no_match
'
=>
'
일치하는 시간대를 찾을 수 없습니다. 다시 시도하세요.
'
,
'
timezone_invalid_index
'
=>
'
잘못된 번호입니다. 다시 입력하세요.
'
,
'
yes
'
=>
'
예
'
,
'
no
'
=>
'
아니오
'
,
'
adding_package
'
=>
'
- 패키지 추가 %s
'
,
'
console_question
'
=>
'
콘솔 컴포넌트 webman/console 설치
'
,
'
db_question
'
=>
'
데이터베이스 컴포넌트
'
,
'
db_none
'
=>
'
설치 안 함
'
,
'
db_invalid
'
=>
'
유효한 옵션을 입력하세요
'
,
'
redis_question
'
=>
'
Redis 컴포넌트 webman/redis 설치
'
,
'
events_note
'
=>
'
(Redis는 illuminate/events가 필요합니다. 자동으로 포함됩니다)
'
,
'
validation_question
'
=>
'
검증 컴포넌트 webman/validation 설치
'
,
'
template_question
'
=>
'
템플릿 엔진
'
,
'
template_none
'
=>
'
설치 안 함
'
,
'
no_components
'
=>
'
선택된 추가 컴포넌트가 없습니다.
'
,
'
installing
'
=>
'
설치 예정:
'
,
'
running
'
=>
'
실행 중:
'
,
'
error_install
'
=>
'
설치에 실패했습니다. 수동으로 실행하세요: composer require %s
'
,
'
done
'
=>
'
선택 컴포넌트 설치가 완료되었습니다.
'
,
'
summary_locale
'
=>
'
언어: %s
'
,
'
summary_timezone
'
=>
'
시간대: %s
'
,
],
'
fr
'
=> [
'
skip
'
=>
'
Mode non interactif, assistant d
\'
installation ignoré.
'
,
'
default_choice
'
=>
'
(défaut %s)
'
,
'
timezone_prompt
'
=>
'
Fuseau horaire (défaut=%s, tapez pour compléter) :
'
,
'
timezone_title
'
=>
'
Fuseau horaire (défaut=%s)
'
,
'
timezone_help
'
=>
'
Tapez un mot-clé, Tab pour compléter, ↑↓ pour choisir :
'
,
'
timezone_region
'
=>
'
Sélectionnez la région du fuseau horaire
'
,
'
timezone_city
'
=>
'
Sélectionnez le fuseau horaire
'
,
'
timezone_invalid
'
=>
'
Fuseau horaire invalide, utilisation de %s par défaut
'
,
'
timezone_input_prompt
'
=>
'
Entrez un fuseau horaire ou un mot-clé :
'
,
'
timezone_pick_prompt
'
=>
'
Entrez un numéro ou un mot-clé :
'
,
'
timezone_no_match
'
=>
'
Aucun fuseau horaire correspondant, veuillez réessayer.
'
,
'
timezone_invalid_index
'
=>
'
Numéro invalide, veuillez réessayer.
'
,
'
yes
'
=>
'
oui
'
,
'
no
'
=>
'
non
'
,
'
adding_package
'
=>
'
- Ajout du paquet %s
'
,
'
console_question
'
=>
'
Installer le composant console webman/console
'
,
'
db_question
'
=>
'
Composant base de données
'
,
'
db_none
'
=>
'
Aucun
'
,
'
db_invalid
'
=>
'
Veuillez entrer une option valide
'
,
'
redis_question
'
=>
'
Installer le composant Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis nécessite illuminate/events, inclus automatiquement)
'
,
'
validation_question
'
=>
'
Installer le composant de validation webman/validation
'
,
'
template_question
'
=>
'
Moteur de templates
'
,
'
template_none
'
=>
'
Aucun
'
,
'
no_components
'
=>
'
Aucun composant optionnel sélectionné.
'
,
'
installing
'
=>
'
Installation en cours :
'
,
'
running
'
=>
'
Exécution :
'
,
'
error_install
'
=>
'
Échec de l
\'
installation. Essayez manuellement : composer require %s
'
,
'
done
'
=>
'
Composants optionnels installés.
'
,
'
summary_locale
'
=>
'
Langue : %s
'
,
'
summary_timezone
'
=>
'
Fuseau horaire : %s
'
,
],
'
de
'
=> [
'
skip
'
=>
'
Nicht-interaktiver Modus, Einrichtungsassistent übersprungen.
'
,
'
default_choice
'
=>
'
(Standard %s)
'
,
'
timezone_prompt
'
=>
'
Zeitzone (Standard=%s, Eingabe zur Vervollständigung):
'
,
'
timezone_title
'
=>
'
Zeitzone (Standard=%s)
'
,
'
timezone_help
'
=>
'
Stichwort tippen, Tab ergänzt, ↑↓ auswählen:
'
,
'
timezone_region
'
=>
'
Zeitzone Region auswählen
'
,
'
timezone_city
'
=>
'
Zeitzone auswählen
'
,
'
timezone_invalid
'
=>
'
Ungültige Zeitzone, Standardwert %s wird verwendet
'
,
'
timezone_input_prompt
'
=>
'
Zeitzone oder Stichwort eingeben:
'
,
'
timezone_pick_prompt
'
=>
'
Nummer oder Stichwort eingeben:
'
,
'
timezone_no_match
'
=>
'
Keine passende Zeitzone gefunden, bitte erneut versuchen.
'
,
'
timezone_invalid_index
'
=>
'
Ungültige Nummer, bitte erneut eingeben.
'
,
'
yes
'
=>
'
ja
'
,
'
no
'
=>
'
nein
'
,
'
adding_package
'
=>
'
- Paket hinzufügen %s
'
,
'
console_question
'
=>
'
Konsolen-Komponente webman/console installieren
'
,
'
db_question
'
=>
'
Datenbank-Komponente
'
,
'
db_none
'
=>
'
Keine
'
,
'
db_invalid
'
=>
'
Bitte geben Sie eine gültige Option ein
'
,
'
redis_question
'
=>
'
Redis-Komponente webman/redis installieren
'
,
'
events_note
'
=>
'
(Redis benötigt illuminate/events, automatisch eingeschlossen)
'
,
'
validation_question
'
=>
'
Validierungs-Komponente webman/validation installieren
'
,
'
template_question
'
=>
'
Template-Engine
'
,
'
template_none
'
=>
'
Keine
'
,
'
no_components
'
=>
'
Keine optionalen Komponenten ausgewählt.
'
,
'
installing
'
=>
'
Installation:
'
,
'
running
'
=>
'
Ausführung:
'
,
'
error_install
'
=>
'
Installation fehlgeschlagen. Manuell ausführen: composer require %s
'
,
'
done
'
=>
'
Optionale Komponenten installiert.
'
,
'
summary_locale
'
=>
'
Sprache: %s
'
,
'
summary_timezone
'
=>
'
Zeitzone: %s
'
,
],
'
es
'
=> [
'
skip
'
=>
'
Modo no interactivo, asistente de instalación omitido.
'
,
'
default_choice
'
=>
'
(predeterminado %s)
'
,
'
timezone_prompt
'
=>
'
Zona horaria (predeterminado=%s, escriba para autocompletar):
'
,
'
timezone_title
'
=>
'
Zona horaria (predeterminado=%s)
'
,
'
timezone_help
'
=>
'
Escriba una palabra clave, Tab autocompleta, use ↑↓ para elegir:
'
,
'
timezone_region
'
=>
'
Seleccione la región de zona horaria
'
,
'
timezone_city
'
=>
'
Seleccione la zona horaria
'
,
'
timezone_invalid
'
=>
'
Zona horaria inválida, usando valor predeterminado %s
'
,
'
timezone_input_prompt
'
=>
'
Ingrese zona horaria o palabra clave:
'
,
'
timezone_pick_prompt
'
=>
'
Ingrese número o palabra clave:
'
,
'
timezone_no_match
'
=>
'
No se encontró zona horaria coincidente, intente de nuevo.
'
,
'
timezone_invalid_index
'
=>
'
Número inválido, intente de nuevo.
'
,
'
yes
'
=>
'
sí
'
,
'
no
'
=>
'
no
'
,
'
adding_package
'
=>
'
- Agregando paquete %s
'
,
'
console_question
'
=>
'
Instalar componente de consola webman/console
'
,
'
db_question
'
=>
'
Componente de base de datos
'
,
'
db_none
'
=>
'
Ninguno
'
,
'
db_invalid
'
=>
'
Por favor ingrese una opción válida
'
,
'
redis_question
'
=>
'
Instalar componente Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis requiere illuminate/events, incluido automáticamente)
'
,
'
validation_question
'
=>
'
Instalar componente de validación webman/validation
'
,
'
template_question
'
=>
'
Motor de plantillas
'
,
'
template_none
'
=>
'
Ninguno
'
,
'
no_components
'
=>
'
No se seleccionaron componentes opcionales.
'
,
'
installing
'
=>
'
Instalando:
'
,
'
running
'
=>
'
Ejecutando:
'
,
'
error_install
'
=>
'
Error en la instalación. Intente manualmente: composer require %s
'
,
'
done
'
=>
'
Componentes opcionales instalados.
'
,
'
summary_locale
'
=>
'
Idioma: %s
'
,
'
summary_timezone
'
=>
'
Zona horaria: %s
'
,
],
'
pt_BR
'
=> [
'
skip
'
=>
'
Modo não interativo, assistente de instalação ignorado.
'
,
'
default_choice
'
=>
'
(padrão %s)
'
,
'
timezone_prompt
'
=>
'
Fuso horário (padrão=%s, digite para autocompletar):
'
,
'
timezone_title
'
=>
'
Fuso horário (padrão=%s)
'
,
'
timezone_help
'
=>
'
Digite uma palavra-chave, Tab autocompleta, use ↑↓ para escolher:
'
,
'
timezone_region
'
=>
'
Selecione a região do fuso horário
'
,
'
timezone_city
'
=>
'
Selecione o fuso horário
'
,
'
timezone_invalid
'
=>
'
Fuso horário inválido, usando padrão %s
'
,
'
timezone_input_prompt
'
=>
'
Digite fuso horário ou palavra-chave:
'
,
'
timezone_pick_prompt
'
=>
'
Digite número ou palavra-chave:
'
,
'
timezone_no_match
'
=>
'
Nenhum fuso horário encontrado, tente novamente.
'
,
'
timezone_invalid_index
'
=>
'
Número inválido, tente novamente.
'
,
'
yes
'
=>
'
sim
'
,
'
no
'
=>
'
não
'
,
'
adding_package
'
=>
'
- Adicionando pacote %s
'
,
'
console_question
'
=>
'
Instalar componente de console webman/console
'
,
'
db_question
'
=>
'
Componente de banco de dados
'
,
'
db_none
'
=>
'
Nenhum
'
,
'
db_invalid
'
=>
'
Por favor, digite uma opção válida
'
,
'
redis_question
'
=>
'
Instalar componente Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis requer illuminate/events, incluído automaticamente)
'
,
'
validation_question
'
=>
'
Instalar componente de validação webman/validation
'
,
'
template_question
'
=>
'
Motor de templates
'
,
'
template_none
'
=>
'
Nenhum
'
,
'
no_components
'
=>
'
Nenhum componente opcional selecionado.
'
,
'
installing
'
=>
'
Instalando:
'
,
'
running
'
=>
'
Executando:
'
,
'
error_install
'
=>
'
Falha na instalação. Tente manualmente: composer require %s
'
,
'
done
'
=>
'
Componentes opcionais instalados.
'
,
'
summary_locale
'
=>
'
Idioma: %s
'
,
'
summary_timezone
'
=>
'
Fuso horário: %s
'
,
],
'
ru
'
=> [
'
skip
'
=>
'
Неинтерактивный режим, мастер установки пропущен.
'
,
'
default_choice
'
=>
'
(по умолчанию %s)
'
,
'
timezone_prompt
'
=>
'
Часовой пояс (по умолчанию=%s, введите для автодополнения):
'
,
'
timezone_title
'
=>
'
Часовой пояс (по умолчанию=%s)
'
,
'
timezone_help
'
=>
'
Введите ключевое слово, Tab для автодополнения, ↑↓ для выбора:
'
,
'
timezone_region
'
=>
'
Выберите регион часового пояса
'
,
'
timezone_city
'
=>
'
Выберите часовой пояс
'
,
'
timezone_invalid
'
=>
'
Неверный часовой пояс, используется значение по умолчанию %s
'
,
'
timezone_input_prompt
'
=>
'
Введите часовой пояс или ключевое слово:
'
,
'
timezone_pick_prompt
'
=>
'
Введите номер или ключевое слово:
'
,
'
timezone_no_match
'
=>
'
Совпадающий часовой пояс не найден, попробуйте снова.
'
,
'
timezone_invalid_index
'
=>
'
Неверный номер, попробуйте снова.
'
,
'
yes
'
=>
'
да
'
,
'
no
'
=>
'
нет
'
,
'
adding_package
'
=>
'
- Добавление пакета %s
'
,
'
console_question
'
=>
'
Установить консольный компонент webman/console
'
,
'
db_question
'
=>
'
Компонент базы данных
'
,
'
db_none
'
=>
'
Не устанавливать
'
,
'
db_invalid
'
=>
'
Пожалуйста, введите допустимый вариант
'
,
'
redis_question
'
=>
'
Установить компонент Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis требует illuminate/events, автоматически включён)
'
,
'
validation_question
'
=>
'
Установить компонент валидации webman/validation
'
,
'
template_question
'
=>
'
Шаблонизатор
'
,
'
template_none
'
=>
'
Не устанавливать
'
,
'
no_components
'
=>
'
Дополнительные компоненты не выбраны.
'
,
'
installing
'
=>
'
Установка:
'
,
'
running
'
=>
'
Выполнение:
'
,
'
error_install
'
=>
'
Ошибка установки. Выполните вручную: composer require %s
'
,
'
done
'
=>
'
Дополнительные компоненты установлены.
'
,
'
summary_locale
'
=>
'
Язык: %s
'
,
'
summary_timezone
'
=>
'
Часовой пояс: %s
'
,
],
'
vi
'
=> [
'
skip
'
=>
'
Chế độ không tương tác, bỏ qua trình hướng dẫn cài đặt.
'
,
'
default_choice
'
=>
'
(mặc định %s)
'
,
'
timezone_prompt
'
=>
'
Múi giờ (mặc định=%s, nhập để tự động hoàn thành):
'
,
'
timezone_title
'
=>
'
Múi giờ (mặc định=%s)
'
,
'
timezone_help
'
=>
'
Nhập từ khóa, Tab để tự hoàn thành, dùng ↑↓ để chọn:
'
,
'
timezone_region
'
=>
'
Chọn khu vực múi giờ
'
,
'
timezone_city
'
=>
'
Chọn múi giờ
'
,
'
timezone_invalid
'
=>
'
Múi giờ không hợp lệ, sử dụng mặc định %s
'
,
'
timezone_input_prompt
'
=>
'
Nhập múi giờ hoặc từ khóa:
'
,
'
timezone_pick_prompt
'
=>
'
Nhập số thứ tự hoặc từ khóa:
'
,
'
timezone_no_match
'
=>
'
Không tìm thấy múi giờ phù hợp, vui lòng thử lại.
'
,
'
timezone_invalid_index
'
=>
'
Số không hợp lệ, vui lòng thử lại.
'
,
'
yes
'
=>
'
có
'
,
'
no
'
=>
'
không
'
,
'
adding_package
'
=>
'
- Thêm gói %s
'
,
'
console_question
'
=>
'
Cài đặt thành phần console webman/console
'
,
'
db_question
'
=>
'
Thành phần cơ sở dữ liệu
'
,
'
db_none
'
=>
'
Không cài đặt
'
,
'
db_invalid
'
=>
'
Vui lòng nhập tùy chọn hợp lệ
'
,
'
redis_question
'
=>
'
Cài đặt thành phần Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis cần illuminate/events, đã tự động bao gồm)
'
,
'
validation_question
'
=>
'
Cài đặt thành phần xác thực webman/validation
'
,
'
template_question
'
=>
'
Công cụ mẫu
'
,
'
template_none
'
=>
'
Không cài đặt
'
,
'
no_components
'
=>
'
Không có thành phần tùy chọn nào được chọn.
'
,
'
installing
'
=>
'
Đang cài đặt:
'
,
'
running
'
=>
'
Đang thực thi:
'
,
'
error_install
'
=>
'
Cài đặt thất bại. Thử thủ công: composer require %s
'
,
'
done
'
=>
'
Các thành phần tùy chọn đã được cài đặt.
'
,
'
summary_locale
'
=>
'
Ngôn ngữ: %s
'
,
'
summary_timezone
'
=>
'
Múi giờ: %s
'
,
],
'
tr
'
=> [
'
skip
'
=>
'
Etkileşimsiz mod, kurulum sihirbazı atlanıyor.
'
,
'
default_choice
'
=>
'
(varsayılan %s)
'
,
'
timezone_prompt
'
=>
'
Saat dilimi (varsayılan=%s, otomatik tamamlama için yazın):
'
,
'
timezone_title
'
=>
'
Saat dilimi (varsayılan=%s)
'
,
'
timezone_help
'
=>
'
Anahtar kelime yazın, Tab tamamlar, ↑↓ ile seçin:
'
,
'
timezone_region
'
=>
'
Saat dilimi bölgesini seçin
'
,
'
timezone_city
'
=>
'
Saat dilimini seçin
'
,
'
timezone_invalid
'
=>
'
Geçersiz saat dilimi, varsayılan %s kullanılıyor
'
,
'
timezone_input_prompt
'
=>
'
Saat dilimi veya anahtar kelime girin:
'
,
'
timezone_pick_prompt
'
=>
'
Numara veya anahtar kelime girin:
'
,
'
timezone_no_match
'
=>
'
Eşleşen saat dilimi bulunamadı, tekrar deneyin.
'
,
'
timezone_invalid_index
'
=>
'
Geçersiz numara, tekrar deneyin.
'
,
'
yes
'
=>
'
evet
'
,
'
no
'
=>
'
hayır
'
,
'
adding_package
'
=>
'
- Paket ekleniyor %s
'
,
'
console_question
'
=>
'
Konsol bileşeni webman/console yüklensin mi
'
,
'
db_question
'
=>
'
Veritabanı bileşeni
'
,
'
db_none
'
=>
'
Yok
'
,
'
db_invalid
'
=>
'
Lütfen geçerli bir seçenek girin
'
,
'
redis_question
'
=>
'
Redis bileşeni webman/redis yüklensin mi
'
,
'
events_note
'
=>
'
(Redis, illuminate/events gerektirir, otomatik olarak dahil edildi)
'
,
'
validation_question
'
=>
'
Doğrulama bileşeni webman/validation yüklensin mi
'
,
'
template_question
'
=>
'
Şablon motoru
'
,
'
template_none
'
=>
'
Yok
'
,
'
no_components
'
=>
'
İsteğe bağlı bileşen seçilmedi.
'
,
'
installing
'
=>
'
Yükleniyor:
'
,
'
running
'
=>
'
Çalıştırılıyor:
'
,
'
error_install
'
=>
'
Yükleme başarısız. Manuel olarak deneyin: composer require %s
'
,
'
done
'
=>
'
İsteğe bağlı bileşenler yüklendi.
'
,
'
summary_locale
'
=>
'
Dil: %s
'
,
'
summary_timezone
'
=>
'
Saat dilimi: %s
'
,
],
'
id
'
=> [
'
skip
'
=>
'
Mode non-interaktif, melewati wizard instalasi.
'
,
'
default_choice
'
=>
'
(default %s)
'
,
'
timezone_prompt
'
=>
'
Zona waktu (default=%s, ketik untuk melengkapi):
'
,
'
timezone_title
'
=>
'
Zona waktu (default=%s)
'
,
'
timezone_help
'
=>
'
Ketik kata kunci, Tab untuk melengkapi, gunakan ↑↓ untuk memilih:
'
,
'
timezone_region
'
=>
'
Pilih wilayah zona waktu
'
,
'
timezone_city
'
=>
'
Pilih zona waktu
'
,
'
timezone_invalid
'
=>
'
Zona waktu tidak valid, menggunakan default %s
'
,
'
timezone_input_prompt
'
=>
'
Masukkan zona waktu atau kata kunci:
'
,
'
timezone_pick_prompt
'
=>
'
Masukkan nomor atau kata kunci:
'
,
'
timezone_no_match
'
=>
'
Zona waktu tidak ditemukan, silakan coba lagi.
'
,
'
timezone_invalid_index
'
=>
'
Nomor tidak valid, silakan coba lagi.
'
,
'
yes
'
=>
'
ya
'
,
'
no
'
=>
'
tidak
'
,
'
adding_package
'
=>
'
- Menambahkan paket %s
'
,
'
console_question
'
=>
'
Instal komponen konsol webman/console
'
,
'
db_question
'
=>
'
Komponen database
'
,
'
db_none
'
=>
'
Tidak ada
'
,
'
db_invalid
'
=>
'
Silakan masukkan opsi yang valid
'
,
'
redis_question
'
=>
'
Instal komponen Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis memerlukan illuminate/events, otomatis disertakan)
'
,
'
validation_question
'
=>
'
Instal komponen validasi webman/validation
'
,
'
template_question
'
=>
'
Mesin template
'
,
'
template_none
'
=>
'
Tidak ada
'
,
'
no_components
'
=>
'
Tidak ada komponen opsional yang dipilih.
'
,
'
installing
'
=>
'
Menginstal:
'
,
'
running
'
=>
'
Menjalankan:
'
,
'
error_install
'
=>
'
Instalasi gagal. Coba manual: composer require %s
'
,
'
done
'
=>
'
Komponen opsional terinstal.
'
,
'
summary_locale
'
=>
'
Bahasa: %s
'
,
'
summary_timezone
'
=>
'
Zona waktu: %s
'
,
],
'
th
'
=> [
'
skip
'
=>
'
โหมดไม่โต้ตอบ ข้ามตัวช่วยติดตั้ง
'
,
'
default_choice
'
=>
'
(ค่าเริ่มต้น %s)
'
,
'
timezone_prompt
'
=>
'
เขตเวลา (ค่าเริ่มต้น=%s พิมพ์เพื่อเติมอัตโนมัติ):
'
,
'
timezone_title
'
=>
'
เขตเวลา (ค่าเริ่มต้น=%s)
'
,
'
timezone_help
'
=>
'
พิมพ์คีย์เวิร์ดแล้วกด Tab เพื่อเติมอัตโนมัติ ใช้ ↑↓ เพื่อเลือก:
'
,
'
timezone_region
'
=>
'
เลือกภูมิภาคเขตเวลา
'
,
'
timezone_city
'
=>
'
เลือกเขตเวลา
'
,
'
timezone_invalid
'
=>
'
เขตเวลาไม่ถูกต้อง ใช้ค่าเริ่มต้น %s
'
,
'
timezone_input_prompt
'
=>
'
ป้อนเขตเวลาหรือคำค้น:
'
,
'
timezone_pick_prompt
'
=>
'
ป้อนหมายเลขหรือคำค้น:
'
,
'
timezone_no_match
'
=>
'
ไม่พบเขตเวลาที่ตรงกัน กรุณาลองอีกครั้ง
'
,
'
timezone_invalid_index
'
=>
'
หมายเลขไม่ถูกต้อง กรุณาลองอีกครั้ง
'
,
'
yes
'
=>
'
ใช่
'
,
'
no
'
=>
'
ไม่
'
,
'
adding_package
'
=>
'
- เพิ่มแพ็กเกจ %s
'
,
'
console_question
'
=>
'
ติดตั้งคอมโพเนนต์คอนโซล webman/console
'
,
'
db_question
'
=>
'
คอมโพเนนต์ฐานข้อมูล
'
,
'
db_none
'
=>
'
ไม่ติดตั้ง
'
,
'
db_invalid
'
=>
'
กรุณาป้อนตัวเลือกที่ถูกต้อง
'
,
'
redis_question
'
=>
'
ติดตั้งคอมโพเนนต์ Redis webman/redis
'
,
'
events_note
'
=>
'
(Redis ต้องการ illuminate/events รวมไว้โดยอัตโนมัติ)
'
,
'
validation_question
'
=>
'
ติดตั้งคอมโพเนนต์ตรวจสอบ webman/validation
'
,
'
template_question
'
=>
'
เทมเพลตเอนจิน
'
,
'
template_none
'
=>
'
ไม่ติดตั้ง
'
,
'
no_components
'
=>
'
ไม่ได้เลือกคอมโพเนนต์เสริม
'
,
'
installing
'
=>
'
กำลังติดตั้ง:
'
,
'
running
'
=>
'
กำลังดำเนินการ:
'
,
'
error_install
'
=>
'
ติดตั้งล้มเหลว ลองด้วยตนเอง: composer require %s
'
,
'
done
'
=>
'
คอมโพเนนต์เสริมติดตั้งเรียบร้อยแล้ว
'
,
'
summary_locale
'
=>
'
ภาษา: %s
'
,
'
summary_timezone
'
=>
'
เขตเวลา: %s
'
,
],
];
// --- Interrupt message (Ctrl+C) ---
private
const
INTERRUPTED_MESSAGES
= [
'
zh_CN
'
=>
'
安装中断,可运行 composer setup-webman 可重新设置。
'
,
'
zh_TW
'
=>
'
安裝中斷,可運行 composer setup-webman 重新設置。
'
,
'
en
'
=>
'
Setup interrupted. Run "composer setup-webman" to restart setup.
'
,
'
ja
'
=>
'
セットアップが中断されました。composer setup-webman を実行して再設定できます。
'
,
'
ko
'
=>
'
설치가 중단되었습니다. composer setup-webman 을 실행하여 다시 설정할 수 있습니다.
'
,
'
fr
'
=>
'
Installation interrompue. Exécutez « composer setup-webman » pour recommencer.
'
,
'
de
'
=>
'
Einrichtung abgebrochen. Führen Sie "composer setup-webman" aus, um neu zu starten.
'
,
'
es
'
=>
'
Instalación interrumpida. Ejecute "composer setup-webman" para reiniciar.
'
,
'
pt_BR
'
=>
'
Instalação interrompida. Execute "composer setup-webman" para reiniciar.
'
,
'
ru
'
=>
'
Установка прервана. Выполните «composer setup-webman» для повторной настройки.
'
,
'
vi
'
=>
'
Cài đặt bị gián đoạn. Chạy "composer setup-webman" để cài đặt lại.
'
,
'
tr
'
=>
'
Kurulum kesildi. Yeniden kurmak için "composer setup-webman" komutunu çalıştırın.
'
,
'
id
'
=>
'
Instalasi terganggu. Jalankan "composer setup-webman" untuk mengatur ulang.
'
,
'
th
'
=>
'
การติดตั้งถูกขัดจังหวะ เรียกใช้ "composer setup-webman" เพื่อตั้งค่าใหม่
'
,
];
// --- Signal handling state ---
/** @var string|null Saved stty mode for terminal restoration on interrupt */
private
static
?
string
$
sttyMode
=
null
;
/** @var string Current locale for interrupt message */
private
static
string
$
interruptLocale
=
'
en
'
;
// ═══════════════════════════════════════════════════════════════
// Entry
// ═══════════════════════════════════════════════════════════════
public
static
function
run
(
Event
$
event
):
void
{
$
io
=
$
event
->
getIO
();
// Non-interactive mode: use English for skip message
if
(!
$
io
->
isInteractive
()) {
$
io
->
write
(
'
<comment>
'
.
self
::
MESSAGES
[
'
en
'
][
'
skip
'
] .
'
</comment>
'
);
return
;
}
try
{
self
::
doRun
(
$
event
,
$
io
);
}
catch
(
\
Throwable
$
e
) {
$
io
->
writeError
(
''
);
$
io
->
writeError
(
'
<error>Setup wizard error:
'
.
$
e
->
getMessage
() .
'
</error>
'
);
$
io
->
writeError
(
'
<comment>Run "composer setup-webman" to retry.</comment>
'
);
}
}
private
static
function
doRun
(
Event
$
event
,
IOInterface
$
io
):
void
{
$
io
->
write
(
''
);
// Register Ctrl+C handler
self
::
registerInterruptHandler
();
// Banner title (must be before locale selection)
self
::
renderTitle
();
// 1. Locale selection
$
locale
=
self
::
askLocale
(
$
io
);
self
::
$
interruptLocale
=
$
locale
;
$
defaultTimezone
=
self
::
LOCALE_DEFAULT_TIMEZONES
[
$
locale
] ??
'
UTC
'
;
$
msg
=
fn
(
string
$
key
,
string
...
$
args
):
string
=>
empty
(
$
args
) ?
self
::
MESSAGES
[
$
locale
][
$
key
] :
sprintf
(
self
::
MESSAGES
[
$
locale
][
$
key
], ...
$
args
);
// Write locale config (update when not default)
if
(
$
locale
!==
'
zh_CN
'
) {
self
::
updateConfig
(
$
event
,
'
config/translation.php
'
,
"
'locale'
"
,
$
locale
);
}
$
io
->
write
(
''
);
$
io
->
write
(
''
);
// 2. Timezone selection (default by locale)
$
timezone
=
self
::
askTimezone
(
$
io
,
$
msg
,
$
defaultTimezone
);
if
(
$
timezone
!==
'
Asia/Shanghai
'
) {
self
::
updateConfig
(
$
event
,
'
config/app.php
'
,
"
'default_timezone'
"
,
$
timezone
);
}
// 3. Optional components
$
packages
=
self
::
askComponents
(
$
io
,
$
msg
);
// 4. Remove unselected components
$
removePackages
=
self
::
askRemoveComponents
(
$
event
,
$
packages
,
$
io
,
$
msg
);
// 5. Summary
$
io
->
write
(
''
);
$
io
->
write
(
'
─────────────────────────────────────
'
);
$
io
->
write
(
'
<info>
'
.
$
msg
(
'
summary_locale
'
,
self
::
LOCALE_LABELS
[
$
locale
]) .
'
</info>
'
);
$
io
->
write
(
'
<info>
'
.
$
msg
(
'
summary_timezone
'
,
$
timezone
) .
'
</info>
'
);
// Remove unselected packages first to avoid dependency conflicts
if
(
$
removePackages
!== []) {
$
io
->
write
(
''
);
$
io
->
write
(
'
<info>
'
.
$
msg
(
'
removing
'
) .
'
</info>
'
);
$
secondaryPackages
= [
self
::
PACKAGE_ILLUMINATE_EVENTS
,
self
::
PACKAGE_ILLUMINATE_PAGINATION
,
self
::
PACKAGE_SYMFONY_VAR_DUMPER
,
];
$
displayRemovePackages
=
array_diff
(
$
removePackages
,
$
secondaryPackages
);
foreach
(
$
displayRemovePackages
as
$
pkg
) {
$
io
->
write
(
'
-
'
.
$
pkg
);
}
$
io
->
write
(
''
);
self
::
runComposerRemove
(
$
removePackages
,
$
io
,
$
msg
);
}
// Then install selected packages
if
(
$
packages
!== []) {
$
io
->
write
(
''
);
$
io
->
write
(
'
<info>
'
.
$
msg
(
'
installing
'
) .
'
</info>
'
.
implode
(
'
,
'
,
$
packages
));
$
io
->
write
(
''
);
self
::
runComposerRequire
(
$
packages
,
$
io
,
$
msg
);
}
elseif
(
$
removePackages
=== []) {
$
io
->
write
(
'
<info>
'
.
$
msg
(
'
no_components
'
) .
'
</info>
'
);
}
}
private
static
function
renderTitle
():
void
{
$
output
=
new
ConsoleOutput
();
$
terminalWidth
= (
new
Terminal
())->
getWidth
();
if
(
$
terminalWidth
<=
0
) {
$
terminalWidth
=
80
;
}
$
text
=
'
'
.
self
::
SETUP_TITLE
.
'
'
;
$
minBoxWidth
=
44
;
$
maxBoxWidth
=
min
(
$
terminalWidth
,
96
);
$
boxWidth
=
min
(
$
maxBoxWidth
,
max
(
$
minBoxWidth
,
mb_strwidth
(
$
text
) +
10
));
$
innerWidth
=
$
boxWidth
-
2
;
$
textWidth
=
mb_strwidth
(
$
text
);
$
pad
=
max
(
0
,
$
innerWidth
-
$
textWidth
);
$
left
=
intdiv
(
$
pad
,
2
);
$
right
=
$
pad
-
$
left
;
$
line2
=
'
│
'
.
str_repeat
(
'
'
,
$
left
) .
$
text
.
str_repeat
(
'
'
,
$
right
) .
'
│
'
;
$
line1
=
'
┌
'
.
str_repeat
(
'
─
'
,
$
innerWidth
) .
'
┐
'
;
$
line3
=
'
└
'
.
str_repeat
(
'
─
'
,
$
innerWidth
) .
'
┘
'
;
$
output
->
writeln
(
''
);
$
output
->
writeln
(
'
<fg=blue;options=bold>
'
.
$
line1
.
'
</>
'
);
$
output
->
writeln
(
'
<fg=blue;options=bold>
'
.
$
line2
.
'
</>
'
);
$
output
->
writeln
(
'
<fg=blue;options=bold>
'
.
$
line3
.
'
</>
'
);
$
output
->
writeln
(
''
);
}
// ═══════════════════════════════════════════════════════════════
// Signal handling (Ctrl+C)
// ═══════════════════════════════════════════════════════════════
/**
* Register Ctrl+C (SIGINT) handler to show a friendly message on interrupt.
* Gracefully skipped when the required extensions are unavailable.
*/
private
static
function
registerInterruptHandler
():
void
{
// Unix/Linux/Mac: pcntl extension with async signals for immediate delivery
/*if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) {
pcntl_async_signals(true);
pcntl_signal(\SIGINT, [self::class, 'handleInterrupt']);
return;
}*/
// Windows: sapi ctrl handler (PHP >= 7.4)
if
(
function_exists
(
'
sapi_windows_set_ctrl_handler
'
)) {
sapi_windows_set_ctrl_handler
(
static
function
(
int
$
event
) {
if
(
$
event
=== \
PHP_WINDOWS_EVENT_CTRL_C
) {
self
::
handleInterrupt
();
}
});
}
}
/**
* Handle Ctrl+C: restore terminal, show tip, then exit.
*/
private
static
function
handleInterrupt
():
void
{
// Restore terminal if in raw mode
if
(
self
::
$
sttyMode
!==
null
&&
function_exists
(
'
shell_exec
'
)) {
@
shell_exec
(
'
stty
'
.
self
::
$
sttyMode
);
self
::
$
sttyMode
=
null
;
}
$
output
=
new
ConsoleOutput
();
$
output
->
writeln
(
''
);
$
output
->
writeln
(
'
<comment>
'
. (
self
::
INTERRUPTED_MESSAGES
[
self
::
$
interruptLocale
] ??
self
::
INTERRUPTED_MESSAGES
[
'
en
'
]) .
'
</comment>
'
);
exit
(
1
);
}
// ═══════════════════════════════════════════════════════════════
// Interactive Menu System
// ═══════════════════════════════════════════════════════════════
/**
* Check if terminal supports interactive features (arrow keys, ANSI colors).
*/
private
static
function
supportsInteractive
():
bool
{
return
function_exists
(
'
shell_exec
'
) && Terminal::
hasSttyAvailable
();
}
/**
* Display a selection menu with arrow key navigation (if supported) or text input fallback.
*
* @param IOInterface $io Composer IO
* @param string $title Menu title
* @param array $items Indexed array of ['tag' => string, 'label' => string]
* @param int $default Default selected index (0-based)
* @return int Selected index
*/
private
static
function
selectMenu
(
IOInterface
$
io
,
string
$
title
,
array
$
items
,
int
$
default
=
0
):
int
{
// Append localized "default" hint to avoid ambiguity
// (Template should contain a single %s placeholder for the default tag.)
$
defaultHintTemplate
=
null
;
if
(
isset
(
self
::
MESSAGES
[
self
::
$
interruptLocale
][
'
default_choice
'
])) {
$
defaultHintTemplate
=
self
::
MESSAGES
[
self
::
$
interruptLocale
][
'
default_choice
'
];
}
$
defaultTag
=
$
items
[
$
default
][
'
tag
'
] ??
''
;
if
(
$
defaultHintTemplate
&&
$
defaultTag
!==
''
) {
$
title
.=
sprintf
(
$
defaultHintTemplate
,
$
defaultTag
);
}
elseif
(
$
defaultTag
!==
''
) {
// Fallback for early menus (e.g. locale selection) before locale is chosen.
$
title
.=
sprintf
(
'
(default %s)
'
,
$
defaultTag
);
}
if
(
self
::
supportsInteractive
()) {
return
self
::
arrowKeySelect
(
$
title
,
$
items
,
$
default
);
}
return
self
::
fallbackSelect
(
$
io
,
$
title
,
$
items
,
$
default
);
}
/**
* Display a yes/no confirmation as a selection menu.
*
* @param IOInterface $io Composer IO
* @param string $title Menu title
* @param bool $default Default value (true = yes)
* @return bool User's choice
*/
private
static
function
confirmMenu
(
IOInterface
$
io
,
string
$
title
,
bool
$
default
=
true
):
bool
{
$
locale
=
self
::
$
interruptLocale
;
$
yes
=
self
::
MESSAGES
[
$
locale
][
'
yes
'
] ??
self
::
MESSAGES
[
'
en
'
][
'
yes
'
] ??
'
yes
'
;
$
no
=
self
::
MESSAGES
[
$
locale
][
'
no
'
] ??
self
::
MESSAGES
[
'
en
'
][
'
no
'
] ??
'
no
'
;
$
items
=
$
default
? [[
'
tag
'
=>
'
Y
'
,
'
label
'
=>
$
yes
], [
'
tag
'
=>
'
n
'
,
'
label
'
=>
$
no
]]
: [[
'
tag
'
=>
'
y
'
,
'
label
'
=>
$
yes
], [
'
tag
'
=>
'
N
'
,
'
label
'
=>
$
no
]];
$
defaultIndex
=
$
default
?
0
:
1
;
return
self
::
selectMenu
(
$
io
,
$
title
,
$
items
,
$
defaultIndex
) ===
0
;
}
/**
* Interactive select with arrow key navigation, manual input and ANSI reverse-video highlighting.
* Input area and option list highlighting are bidirectionally linked.
* Requires stty (Unix-like terminals).
*/
private
static
function
arrowKeySelect
(
string
$
title
,
array
$
items
,
int
$
default
):
int
{
$
output
=
new
ConsoleOutput
();
$
count
=
count
(
$
items
);
$
selected
=
$
default
;
$
maxTagWidth
=
max
(
array_map
(
fn
(
array
$
item
) =>
mb_strlen
(
$
item
[
'
tag
'
]),
$
items
));
$
defaultTag
=
$
items
[
$
default
][
'
tag
'
];
$
input
=
$
defaultTag
;
// Print title and initial options
$
output
->
writeln
(
''
);
$
output
->
writeln
(
'
<fg=blue;options=bold>
'
.
$
title
.
'
</>
'
);
self
::
drawMenuItems
(
$
output
,
$
items
,
$
selected
,
$
maxTagWidth
);
$
output
->
write
(
'
>
'
.
$
input
);
// Enter raw mode
self
::
$
sttyMode
=
shell_exec
(
'
stty -g
'
);
shell_exec
(
'
stty -icanon -echo
'
);
try
{
while
(!
feof
(
STDIN
)) {
$
c
=
fread
(
STDIN
,
1
);
if
(
false
===
$
c
||
''
===
$
c
) {
break
;
}
// ── Backspace ──
if
(
"\177"
===
$
c
||
"\010"
===
$
c
) {
if
(
''
!==
$
input
) {
$
input
=
mb_substr
(
$
input
,
0
, -
1
);
}
$
selected
=
self
::
findItemByTag
(
$
items
,
$
input
);
$
output
->
write
(
"\033
[
{
$
count
}
A
"
);
self
::
drawMenuItems
(
$
output
,
$
items
,
$
selected
,
$
maxTagWidth
);
$
output
->
write
(
"\033
[2K
\r
>
"
.
$
input
);
continue
;
}
// ── Escape sequences (arrow keys) ──
if
(
"\033"
===
$
c
) {
$
seq
=
fread
(
STDIN
,
2
);
if
(
isset
(
$
seq
[
1
])) {
$
changed
=
false
;
if
(
'
A
'
===
$
seq
[
1
]) {
// Up
$
selected
= (
$
selected
<=
0
?
$
count
:
$
selected
) -
1
;
$
changed
=
true
;
}
elseif
(
'
B
'
===
$
seq
[
1
]) {
// Down
$
selected
= (
$
selected
+
1
) %
$
count
;
$
changed
=
true
;
}
if
(
$
changed
) {
// Sync input with selected item's tag
$
input
=
$
items
[
$
selected
][
'
tag
'
];
$
output
->
write
(
"\033
[
{
$
count
}
A
"
);
self
::
drawMenuItems
(
$
output
,
$
items
,
$
selected
,
$
maxTagWidth
);
$
output
->
write
(
"\033
[2K
\r
>
"
.
$
input
);
}
}
continue
;
}
// ── Enter: confirm selection ──
if
(
"\n"
===
$
c
||
"\r"
===
$
c
) {
if
(
$
selected
<
0
) {
$
selected
=
$
default
;
}
$
output
->
write
(
"\033
[2K
\r
> <info>
"
.
$
items
[
$
selected
][
'
tag
'
] .
'
'
.
$
items
[
$
selected
][
'
label
'
] .
'
</info>
'
);
$
output
->
writeln
(
''
);
break
;
}
// ── Ignore other control characters ──
if
(
ord
(
$
c
) <
32
) {
continue
;
}
// ── Printable character (with UTF-8 multi-byte support) ──
if
(
"\x80"
<=
$
c
) {
$
extra
= [
"\xC0"
=>
1
,
"\xD0"
=>
1
,
"\xE0"
=>
2
,
"\xF0"
=>
3
];
$
c
.=
fread
(
STDIN
,
$
extra
[
$
c
&
"\xF0"
] ??
0
);
}
$
input
.=
$
c
;
$
selected
=
self
::
findItemByTag
(
$
items
,
$
input
);
$
output
->
write
(
"\033
[
{
$
count
}
A
"
);
self
::
drawMenuItems
(
$
output
,
$
items
,
$
selected
,
$
maxTagWidth
);
$
output
->
write
(
"\033
[2K
\r
>
"
.
$
input
);
}
}
finally
{
if
(
self
::
$
sttyMode
!==
null
) {
shell_exec
(
'
stty
'
.
self
::
$
sttyMode
);
self
::
$
sttyMode
=
null
;
}
}
return
$
selected
<
0
?
$
default
:
$
selected
;
}
/**
* Fallback select for terminals without stty support. Uses plain text input.
*/
private
static
function
fallbackSelect
(
IOInterface
$
io
,
string
$
title
,
array
$
items
,
int
$
default
):
int
{
$
maxTagWidth
=
max
(
array_map
(
fn
(
array
$
item
) =>
mb_strlen
(
$
item
[
'
tag
'
]),
$
items
));
$
defaultTag
=
$
items
[
$
default
][
'
tag
'
];
$
io
->
write
(
''
);
$
io
->
write
(
'
<fg=blue;options=bold>
'
.
$
title
.
'
</>
'
);
foreach
(
$
items
as
$
item
) {
$
tag
=
str_pad
(
$
item
[
'
tag
'
],
$
maxTagWidth
);
$
io
->
write
(
"
[
$
tag
]
"
.
$
item
[
'
label
'
]);
}
while
(
true
) {
$
io
->
write
(
'
>
'
,
false
);
$
line
=
fgets
(
STDIN
);
if
(
$
line
===
false
) {
return
$
default
;
}
$
answer
=
trim
(
$
line
);
if
(
$
answer
===
''
) {
$
io
->
write
(
'
> <info>
'
.
$
items
[
$
default
][
'
tag
'
] .
'
'
.
$
items
[
$
default
][
'
label
'
] .
'
</info>
'
);
return
$
default
;
}
// Match by tag (case-insensitive)
foreach
(
$
items
as
$
i
=>
$
item
) {
if
(
strcasecmp
(
$
item
[
'
tag
'
],
$
answer
) ===
0
) {
$
io
->
write
(
'
> <info>
'
.
$
items
[
$
i
][
'
tag
'
] .
'
'
.
$
items
[
$
i
][
'
label
'
] .
'
</info>
'
);
return
$
i
;
}
}
}
}
/**
* Render menu items with optional ANSI reverse-video highlighting for the selected item.
* When $selected is -1, no item is highlighted.
*/
private
static
function
drawMenuItems
(
ConsoleOutput
$
output
,
array
$
items
,
int
$
selected
,
int
$
maxTagWidth
):
void
{
foreach
(
$
items
as
$
i
=>
$
item
) {
$
tag
=
str_pad
(
$
item
[
'
tag
'
],
$
maxTagWidth
);
$
line
=
"
[
$
tag
]
"
.
$
item
[
'
label
'
];
if
(
$
i
===
$
selected
) {
$
output
->
writeln
(
"\033
[2K
\r\033
[7m
"
.
$
line
.
"\033
[0m
"
);
}
else
{
$
output
->
writeln
(
"\033
[2K
\r"
.
$
line
);
}
}
}
/**
* Find item index by tag (case-insensitive exact match).
* Returns -1 if no match found or input is empty.
*/
private
static
function
findItemByTag
(
array
$
items
,
string
$
input
):
int
{
if
(
$
input
===
''
) {
return
-
1
;
}
foreach
(
$
items
as
$
i
=>
$
item
) {
if
(
strcasecmp
(
$
item
[
'
tag
'
],
$
input
) ===
0
) {
return
$
i
;
}
}
return
-
1
;
}
// ═══════════════════════════════════════════════════════════════
// Locale selection
View remainder of file in raw view
Back
|
FazBrowse Home
|
New Git URL