@@ -11,7 +11,31 @@ class Ci4msSetup extends BaseCommand
1111 protected $ group = 'Ci4MS ' ;
1212 protected $ name = 'ci4ms:setup ' ;
1313 protected $ description = 'Runs the full CI4MS installation process via CLI. ' ;
14- protected $ usage = 'php spark ci4ms:setup ' ;
14+ protected $ usage = 'php spark ci4ms:setup [options] ' ;
15+
16+ protected $ options = [
17+ '--fname ' => 'Admin first name ' ,
18+ '--sname ' => 'Admin last name ' ,
19+ '--email ' => 'Admin email address ' ,
20+ '--username ' => 'Admin username ' ,
21+ '--password ' => 'Admin password ' ,
22+ '--dbHost ' => 'Database hostname (default: localhost) ' ,
23+ '--dbName ' => 'Database name ' ,
24+ '--dbUser ' => 'Database username ' ,
25+ '--dbPass ' => 'Database password ' ,
26+ '--dbDriver ' => 'Database driver (default: MySQLi) ' ,
27+ '--dbPrefix ' => 'Database table prefix (default: ci4ms_) ' ,
28+ '--dbPort ' => 'Database port (default: 3306) ' ,
29+ '--siteName ' => 'Site name ' ,
30+ '--baseUrl ' => 'Base URL (e.g. https://example.com) ' ,
31+ '--slogan ' => 'Site slogan (optional) ' ,
32+ ];
33+
34+ /**
35+ * Non-interactive modda mı çalışıyoruz?
36+ * Tüm zorunlu argümanlar CLI'dan verilmişse interaktif prompt atlanır.
37+ */
38+ private bool $ nonInteractive = false ;
1539
1640 public function run (array $ params )
1741 {
@@ -29,22 +53,33 @@ public function run(array $params)
2953 return ;
3054 }
3155
56+ // ─────────────────────────────────────────────────────────────
57+ // CLI argümanlarını oku — non-interactive mod kontrolü
58+ // ─────────────────────────────────────────────────────────────
59+ $ cliArgs = $ this ->parseCliOptions ();
60+ $ this ->nonInteractive = $ this ->hasAllRequired ($ cliArgs );
61+
62+ if ($ this ->nonInteractive ) {
63+ CLI ::write (' Running in non-interactive mode... ' , 'light_gray ' );
64+ CLI ::write ('' );
65+ }
66+
3267 // ─────────────────────────────────────────────────────────────
3368 // 1. KULLANICI BİLGİLERİ
3469 // ─────────────────────────────────────────────────────────────
3570 CLI ::write ('[ Step 1/6 ] Admin User Information ' , 'yellow ' );
3671 CLI ::write ('───────────────────────────────────── ' , 'dark_gray ' );
3772
38- $ name = $ this ->promptRequired ('First Name ' );
39- $ surname = $ this ->promptRequired ('Last Name ' );
40- $ email = $ this ->promptValidated ('Email ' , function ($ val ) {
73+ $ name = $ cliArgs [ ' fname ' ] ?? $ this ->promptRequired ('First Name ' );
74+ $ surname = $ cliArgs [ ' sname ' ] ?? $ this ->promptRequired ('Last Name ' );
75+ $ email = $ cliArgs [ ' email ' ] ?? $ this ->promptValidated ('Email ' , function ($ val ) {
4176 return filter_var ($ val , FILTER_VALIDATE_EMAIL ) ? null : 'Please enter a valid email address. ' ;
4277 });
43- $ username = $ this ->promptValidated ('Username (alphanumeric, 3-50 chars) ' , function ($ val ) {
78+ $ username = $ cliArgs [ ' username ' ] ?? $ this ->promptValidated ('Username (alphanumeric, 3-50 chars) ' , function ($ val ) {
4479 if (!preg_match ('/^[a-zA-Z0-9]{3,50}$/ ' , $ val )) return 'Username must be alphanumeric, 3-50 characters. ' ;
4580 return null ;
4681 });
47- $ password = $ this ->promptSecret ('Password (min 8 chars) ' , function ($ val ) {
82+ $ password = $ cliArgs [ ' password ' ] ?? $ this ->promptSecret ('Password (min 8 chars) ' , function ($ val ) {
4883 if (strlen ($ val ) < 8 ) return 'Password must be at least 8 characters. ' ;
4984 return null ;
5085 });
@@ -56,22 +91,34 @@ public function run(array $params)
5691 CLI ::write ('[ Step 2/6 ] Database Configuration ' , 'yellow ' );
5792 CLI ::write ('───────────────────────────────────── ' , 'dark_gray ' );
5893
59- $ dbHost = CLI ::prompt ('DB Host ' , 'localhost ' );
60- $ dbName = $ this ->promptValidated ('DB Name (alphanumeric/dash) ' , function ($ val ) {
61- if (!preg_match ('/^[a-zA-Z0-9_-]{1,100}$/ ' , $ val )) return 'DB name must be alphanumeric (max 100 chars). ' ;
62- return null ;
63- });
64- $ dbUsername = $ this ->promptValidated ('DB Username ' , function ($ val ) {
65- if (!preg_match ('/^[a-zA-Z0-9_-]{1,100}$/ ' , $ val )) return 'DB username must be alphanumeric (max 100 chars). ' ;
66- return null ;
67- });
68- $ dbPassword = CLI ::prompt ('DB Password (leave blank if none) ' , '' );
69- $ dbDriver = CLI ::prompt ('DB Driver ' , 'MySQLi ' );
70- $ dbPrefix = CLI ::prompt ('DB Prefix ' , 'ci4ms_ ' );
71- $ dbPort = $ this ->promptValidated ('DB Port ' , function ($ val ) {
72- if (!ctype_digit ($ val ) || (int )$ val < 1 || (int )$ val > 65535 ) return 'Port must be a number between 1-65535. ' ;
73- return null ;
74- }, '3306 ' );
94+ if ($ this ->nonInteractive ) {
95+ // Non-interactive: .env'deki mevcut DB ayarlarını kullan veya CLI argümanlarını al
96+ $ dbHost = $ cliArgs ['dbHost ' ] ?? $ this ->getEnvValue ('database.default.hostname ' , 'localhost ' );
97+ $ dbName = $ cliArgs ['dbName ' ] ?? $ this ->getEnvValue ('database.default.database ' , 'ci4ms ' );
98+ $ dbUsername = $ cliArgs ['dbUser ' ] ?? $ this ->getEnvValue ('database.default.username ' , 'root ' );
99+ $ dbPassword = $ cliArgs ['dbPass ' ] ?? $ this ->getEnvValue ('database.default.password ' , '' );
100+ $ dbDriver = $ cliArgs ['dbDriver ' ] ?? $ this ->getEnvValue ('database.default.DBDriver ' , 'MySQLi ' );
101+ $ dbPrefix = $ cliArgs ['dbPrefix ' ] ?? $ this ->getEnvValue ('database.default.DBPrefix ' , 'ci4ms_ ' );
102+ $ dbPort = $ cliArgs ['dbPort ' ] ?? $ this ->getEnvValue ('database.default.port ' , '3306 ' );
103+ CLI ::write (" Using DB: {$ dbHost }: {$ dbPort } / {$ dbName }" , 'light_gray ' );
104+ } else {
105+ $ dbHost = CLI ::prompt ('DB Host ' , 'localhost ' );
106+ $ dbName = $ this ->promptValidated ('DB Name (alphanumeric/dash) ' , function ($ val ) {
107+ if (!preg_match ('/^[a-zA-Z0-9_-]{1,100}$/ ' , $ val )) return 'DB name must be alphanumeric (max 100 chars). ' ;
108+ return null ;
109+ });
110+ $ dbUsername = $ this ->promptValidated ('DB Username ' , function ($ val ) {
111+ if (!preg_match ('/^[a-zA-Z0-9_-]{1,100}$/ ' , $ val )) return 'DB username must be alphanumeric (max 100 chars). ' ;
112+ return null ;
113+ });
114+ $ dbPassword = CLI ::prompt ('DB Password (leave blank if none) ' , '' );
115+ $ dbDriver = CLI ::prompt ('DB Driver ' , 'MySQLi ' );
116+ $ dbPrefix = CLI ::prompt ('DB Prefix ' , 'ci4ms_ ' );
117+ $ dbPort = $ this ->promptValidated ('DB Port ' , function ($ val ) {
118+ if (!ctype_digit ($ val ) || (int )$ val < 1 || (int )$ val > 65535 ) return 'Port must be a number between 1-65535. ' ;
119+ return null ;
120+ }, '3306 ' );
121+ }
75122
76123 // ─────────────────────────────────────────────────────────────
77124 // 3. SİTE BİLGİLERİ
@@ -80,20 +127,23 @@ public function run(array $params)
80127 CLI ::write ('[ Step 3/6 ] Site Information ' , 'yellow ' );
81128 CLI ::write ('───────────────────────────────────── ' , 'dark_gray ' );
82129
83- $ siteName = $ this ->promptValidated ('Site Name ' , function ($ val ) {
130+ $ siteName = $ cliArgs [ ' siteName ' ] ?? $ this ->promptValidated ('Site Name ' , function ($ val ) {
84131 if (empty (trim ($ val )) || strlen ($ val ) > 255 ) return 'Site name is required (max 255 chars). ' ;
85132 if (preg_match ('/[<>{}=]/ ' , $ val )) return 'Site name contains invalid characters. ' ;
86133 return null ;
87134 });
88- $ baseUrl = $ this ->promptValidated ('Base URL (e.g. https://example.com) ' , function ($ val ) {
135+ $ baseUrl = $ cliArgs [ ' baseUrl ' ] ?? $ this ->promptValidated ('Base URL (e.g. https://example.com) ' , function ($ val ) {
89136 if (!filter_var ($ val , FILTER_VALIDATE_URL )) return 'Please enter a valid URL. ' ;
90137 return null ;
91138 });
92- $ slogan = CLI ::prompt ('Site Slogan (optional, leave blank to skip) ' , '' );
93- if ($ slogan !== '' ) {
94- while (strlen ($ slogan ) > 255 || preg_match ('/[<>{}=]/ ' , $ slogan )) {
95- CLI ::error ('Slogan must be max 255 chars and cannot contain < > { } = characters. ' );
96- $ slogan = CLI ::prompt ('Site Slogan (optional) ' , '' );
139+ $ slogan = $ cliArgs ['slogan ' ] ?? '' ;
140+ if (!$ this ->nonInteractive && $ slogan === '' ) {
141+ $ slogan = CLI ::prompt ('Site Slogan (optional, leave blank to skip) ' , '' );
142+ if ($ slogan !== '' ) {
143+ while (strlen ($ slogan ) > 255 || preg_match ('/[<>{}=]/ ' , $ slogan )) {
144+ CLI ::error ('Slogan must be max 255 chars and cannot contain < > { } = characters. ' );
145+ $ slogan = CLI ::prompt ('Site Slogan (optional) ' , '' );
146+ }
97147 }
98148 }
99149
@@ -110,10 +160,12 @@ public function run(array $params)
110160 CLI ::write (" Slogan : " . ($ slogan !== '' ? $ slogan : '(not set) ' ));
111161 CLI ::write ('' );
112162
113- $ confirm = CLI ::prompt ('Everything looks correct? Proceed with installation? ' , ['y ' , 'n ' ]);
114- if (strtolower ($ confirm ) !== 'y ' ) {
115- CLI ::write ('Setup cancelled by user. ' , 'red ' );
116- return ;
163+ if (!$ this ->nonInteractive ) {
164+ $ confirm = CLI ::prompt ('Everything looks correct? Proceed with installation? ' , ['y ' , 'n ' ]);
165+ if (strtolower ($ confirm ) !== 'y ' ) {
166+ CLI ::write ('Setup cancelled by user. ' , 'red ' );
167+ return ;
168+ }
117169 }
118170
119171 // ─────────────────────────────────────────────────────────────
@@ -122,9 +174,12 @@ public function run(array $params)
122174 CLI ::write ('' );
123175 CLI ::write ('[ Step 4/6 ] Writing .env file... ' , 'yellow ' );
124176
125- if (!$ this ->copyEnvFile ()) {
126- CLI ::error ('Could not copy env → .env. Aborting. ' );
127- return ;
177+ // Non-interactive modda .env zaten mevcutsa kopyalama atla
178+ if (!file_exists (ROOTPATH . '.env ' )) {
179+ if (!$ this ->copyEnvFile ()) {
180+ CLI ::error ('Could not copy env → .env. Aborting. ' );
181+ return ;
182+ }
128183 }
129184
130185 $ updates = [
@@ -142,7 +197,7 @@ public function run(array $params)
142197 'cookie.path ' => '\'/ \'' ,
143198 'cookie.domain ' => '\'\'' ,
144199 'cookie.secure ' => 'false #Don \'t forget to set it to true when buying production mode. ' ,
145- 'cookie.httponly ' => 'true ' ,
200+ 'cookie.httponly ' => 'true ' ,
146201 'cookie.samesite ' => '\'Lax \'' ,
147202 'cookie.raw ' => 'false ' ,
148203 'honeypot.hidden ' => '\'true \'' ,
@@ -163,7 +218,7 @@ public function run(array $params)
163218 'app.supportedLocales ' => '["ar","de","en","es","fr","hi","ja","pt","ru","tr","zh"] ' ,
164219 'app.negotiateLocale ' => 'true ' ,
165220 'app.appTimezone ' => '\'Europe/Istanbul \'' ,
166- 'app.version ' => '0.31.2 .0 ' ,
221+ 'app.version ' => '0.31.3 .0 ' ,
167222 ];
168223
169224 if (!$ this ->updateEnvSettings ($ updates )) {
@@ -243,6 +298,70 @@ public function run(array $params)
243298 CLI ::write ('' );
244299 }
245300
301+ // ═════════════════════════════════════════════════════════════════
302+ // CLI OPTION PARSER
303+ // ═════════════════════════════════════════════════════════════════
304+
305+ /**
306+ * $_SERVER['argv'] üzerinden --key=value formatındaki argümanları parse et.
307+ * CI4'ün BaseCommand::$params dizisi bu formatta çalışmadığı için
308+ * doğrudan argv'den okuyoruz.
309+ */
310+ private function parseCliOptions (): array
311+ {
312+ $ options = [];
313+ $ argv = $ _SERVER ['argv ' ] ?? [];
314+
315+ foreach ($ argv as $ arg ) {
316+ if (str_starts_with ($ arg , '-- ' ) && str_contains ($ arg , '= ' )) {
317+ [$ key , $ value ] = explode ('= ' , substr ($ arg , 2 ), 2 );
318+ $ options [$ key ] = $ value ;
319+ }
320+ }
321+
322+ return $ options ;
323+ }
324+
325+ /**
326+ * Non-interactive mod için gerekli tüm zorunlu argümanlar var mı?
327+ */
328+ private function hasAllRequired (array $ args ): bool
329+ {
330+ $ required = ['fname ' , 'sname ' , 'email ' , 'username ' , 'password ' , 'siteName ' , 'baseUrl ' ];
331+
332+ foreach ($ required as $ key ) {
333+ if (empty ($ args [$ key ] ?? '' )) {
334+ return false ;
335+ }
336+ }
337+
338+ return true ;
339+ }
340+
341+ /**
342+ * Mevcut .env dosyasından bir değer oku
343+ */
344+ private function getEnvValue (string $ key , string $ default = '' ): string
345+ {
346+ // Önce $_ENV / $_SERVER dene (CI4 .env loader tarafından yüklenmiş olabilir)
347+ $ envKey = str_replace ('. ' , '_ ' , $ key );
348+ if (!empty ($ _ENV [$ key ])) return $ _ENV [$ key ];
349+ if (!empty ($ _SERVER [$ key ])) return $ _SERVER [$ key ];
350+
351+ // .env dosyasından doğrudan oku
352+ $ envPath = ROOTPATH . '.env ' ;
353+ if (!file_exists ($ envPath )) return $ default ;
354+
355+ $ contents = file_get_contents ($ envPath );
356+ $ pattern = '/^ ' . preg_quote ($ key , '/ ' ) . '\s*=\s*(.+)$/m ' ;
357+
358+ if (preg_match ($ pattern , $ contents , $ matches )) {
359+ return trim ($ matches [1 ], " \t\n\r\0\x0B' \"" );
360+ }
361+
362+ return $ default ;
363+ }
364+
246365 // ═════════════════════════════════════════════════════════════════
247366 // PRIVATE HELPERS
248367 // ═════════════════════════════════════════════════════════════════
@@ -363,7 +482,7 @@ private function writeRoutesFile(): bool
363482 }
364483
365484 // ═════════════════════════════════════════════════════════════════
366- // CLI PROMPT HELPERS
485+ // CLI PROMPT HELPERS (sadece interaktif modda kullanılır)
367486 // ═════════════════════════════════════════════════════════════════
368487
369488 /**
0 commit comments