Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.57% covered (success)
99.57%
230 / 231
90.91% covered (success)
90.91%
10 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApplicationController
99.57% covered (success)
99.57%
230 / 231
90.91% covered (success)
90.91%
10 / 11
56
0.00% covered (danger)
0.00%
0 / 1
 init
98.95% covered (success)
98.95%
94 / 95
0.00% covered (danger)
0.00%
0 / 1
20
 env
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 displayEnv
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 setEnv
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
3
 status
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 down
100.00% covered (success)
100.00%
52 / 52
100.00% covered (success)
100.00%
1 / 1
14
 up
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 createCommand
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 createController
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 createModel
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 createView
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/pop-bootstrap
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Kettle\Controller;
16
17use Pop\Console\Color;
18use Pop\Kettle\Event;
19use Pop\Kettle\Model;
20use Pop\App;
21
22/**
23 * Console application controller class
24 *
25 * @category   Pop\Kettle
26 * @package    Pop\Kettle
27 * @author     Nick Sagona, III <nick@popphp.org>
28 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
29 * @license    https://www.popphp.org/license     New BSD License
30 * @version    3.0.0
31 */
32class ApplicationController extends AbstractController
33{
34
35    /**
36     * Init command
37     *
38     * @return void
39     */
40    public function init(): void
41    {
42        $namespace = $this->console->prompt('What is the namespace of your app? [App] ', null, true);
43        if (empty($namespace)) {
44            $namespace = 'App';
45        }
46
47        $parsed    = Model\Application::parseNamespace($namespace);
48        $namespace = $parsed['namespace'];
49        $fullName  = $parsed['fullName'];
50
51        $this->console->write();
52        $name = $this->console->prompt('What is the name of your app? [' . $fullName . '] ', null, true);
53        if ($name == '') {
54            $name = $fullName;
55        } else if (str_contains($name, ' ') && !str_starts_with($name, '"') && !str_ends_with($name, '"')) {
56            $name = '"' . $name . '"';
57        }
58
59        $this->console->write();
60        $cliOnlyAnswer = $this->console->prompt('Is this a CLI-only application? [Y/N] ', null, true);
61        $cliOnly       = str_starts_with(strtolower(trim($cliOnlyAnswer)), 'y');
62
63        $url = '';
64
65        if (!$cliOnly) {
66            $this->console->write();
67            $url = $this->console->prompt('What is the URL of your app? [http://localhost] ', null, true);
68            if ($url == '') {
69                $url = 'http://localhost';
70            }
71        }
72
73        $this->console->write();
74        $createCliApp = $this->console->prompt(
75            'Initialize a stand-alone CLI application? [Y/N] ', ['y', 'n']
76        );
77        $cliApp = (strtolower($createCliApp) == 'y');
78
79        $appModel = new Model\Application();
80        $dbModel  = new Model\Database();
81        $location = getcwd();
82
83        $this->console->write();
84        $configDb = $this->console->prompt(
85            'Would you like to configure a database? [Y/N] ', ['y', 'n']
86        );
87        $createDb = (strtolower($configDb) == 'y');
88
89        $frontend = null;
90
91        if (!$cliOnly) {
92            $this->console->write();
93            $installFrontend = $this->console->prompt(
94                'Would you like to install a front-end? [Y/N] ', ['y', 'n']
95            );
96
97            if (strtolower($installFrontend) == 'y') {
98                $frameworks = [
99                    'AlpineJS' => 1,
100                    'Vue'      => 2,
101                    'React'    => 3,
102                ];
103
104                $this->console->write();
105                foreach ($frameworks as $label => $i) {
106                    $this->console->write($i . ': ' . $label);
107                }
108                $this->console->write();
109
110                $f     = $this->console->prompt('Please select a front-end framework from above: ', $frameworks);
111                $label = array_search($f, $frameworks);
112                $frontend = match ($label) {
113                    'AlpineJS' => 'alpine',
114                    'Vue'      => 'vue',
115                    'React'    => 'react',
116                    default    => null,
117                };
118            }
119        }
120
121        $appModel->init($location, $namespace, $cliOnly, $name, $url, $cliApp, $createDb, $frontend);
122
123        $this->console->write();
124        $this->console->write("Installing files for '" . $namespace ."'...");
125        $this->console->write();
126
127        $composerModel = new Model\Composer();
128        if ($composerModel->isAvailable()) {
129            $this->console->write("Registering application autoloader for '" . $namespace . "'...");
130            $this->console->write();
131            $composerModel->dumpAutoload($location);
132            $this->console->write();
133        } else {
134            $this->console->alertWarning(
135                'Composer not found -- run `composer dump-autoload` yourself before using the app.',
136                60
137            );
138            $this->console->write();
139        }
140
141        if ($createDb) {
142            $this->console->write("Configuring database for '" . $namespace ."'...");
143            $this->console->write();
144            $dbModel->configure($this->console, $location);
145            $this->console->write();
146        }
147
148        if ($frontend !== null) {
149            $assetModel = new Model\Asset();
150            if ($assetModel->isNpmAvailable()) {
151                $this->console->write("Installing front-end dependencies for '" . $namespace . "'...");
152                $this->console->write();
153                $installResult = $assetModel->install($location);
154
155                if ($installResult === 0) {
156                    $this->console->write();
157                    $this->console->write("Building front-end assets for '" . $namespace . "'...");
158                    $this->console->write();
159                    $assetModel->build($location);
160                }
161            } else {
162                $this->console->alertWarning(
163                    'Node/npm not found -- install Node, then run `npm install` before using web:watch/web:build.',
164                    60
165                );
166            }
167            $this->console->write();
168        }
169
170        $this->console->write('Done!');
171    }
172
173    /**
174     * Env command
175     *
176     * @param  array $options
177     * @return void
178     */
179    public function env(array $options = []): void
180    {
181        if (isset($options['set'])) {
182            $this->setEnv();
183            return;
184        }
185
186        $this->displayEnv((string)App::environment());
187    }
188
189    /**
190     * Display the colorized environment alert box for the given environment
191     *
192     * @param  string $env
193     * @return void
194     */
195    protected function displayEnv(string $env): void
196    {
197        if (str_starts_with($env, 'prod')) {
198            $this->console->alertWarning('Application in Production', 40);
199        } else if ($env == 'staging') {
200            $this->console->alertPrimary('Application in Staging', 40);
201        } else if ($env == 'testing') {
202            $this->console->alertSecondary('Application in Testing', 40);
203        } else if ($env == 'dev') {
204            $this->console->alertDark('Application in Dev', 40);
205        } else if ($env == 'local') {
206            $this->console->alertLight('Application in Local', 40);
207        }
208    }
209
210    /**
211     * Set application environment
212     *
213     * @return void
214     */
215    protected function setEnv(): void
216    {
217        $location = getcwd();
218
219        if (!file_exists($location . '/.env')) {
220            $this->console->write('No .env file found.');
221            return;
222        }
223
224        $envs = [
225            'local'      => 1,
226            'dev'        => 2,
227            'testing'    => 3,
228            'staging'    => 4,
229            'production' => 5,
230        ];
231
232        $this->console->write();
233        foreach ($envs as $env => $i) {
234            $this->console->write($i . ': ' . $env);
235        }
236        $this->console->write();
237
238        $e   = $this->console->prompt('Please select an app environment from above: ', $envs);
239        $env = array_search($e, $envs);
240
241        $current = App::environment();
242
243        $contents = str_replace(
244            [
245                'APP_ENV=' . $current,
246                'APP_ENV="' . $current . '"',
247            ],
248            'APP_ENV=' . $env,
249            file_get_contents($location . '/.env')
250        );
251
252        file_put_contents($location . '/.env', $contents);
253
254        $this->console->write();
255        $this->displayEnv($env);
256    }
257
258    /**
259     * Status command
260     *
261     * @return void
262     */
263    public function status(): void
264    {
265        if (App::isUp()) {
266            $this->console->alertSuccess('Application is Live', 40);
267        }
268    }
269
270    /**
271     * Down command
272     *
273     * @param  array $options
274     * @return void
275     */
276    public function down(array $options = []): void
277    {
278        $location = getcwd();
279        $secret   = null;
280
281        if (array_key_exists('secret', $options)) {
282            $secret = (($options['secret'] === null) || ($options['secret'] == '')) ? sha1((string)time()) : $options['secret'];
283        }
284
285        if (file_exists($location . '/.env') && (App::isUp())) {
286            $e = file_get_contents($location . '/.env');
287            $e = str_replace(
288                [
289                    'MAINTENANCE_MODE=false',
290                    'MAINTENANCE_MODE="false"',
291                    'MAINTENANCE_MODE=(false)',
292                ],
293                'MAINTENANCE_MODE=true',
294                $e
295            );
296
297            $currentSecret = App::env('MAINTENANCE_MODE_SECRET');
298            if (!empty($secret)) {
299                $e = str_replace(
300                    [
301                        'MAINTENANCE_MODE_SECRET=' . $currentSecret,
302                        'MAINTENANCE_MODE_SECRET="' . $currentSecret . '"',
303                    ],
304                    'MAINTENANCE_MODE_SECRET=' . $secret,
305                    $e
306                );
307            } else if (!empty($currentSecret)) {
308                $secret = $currentSecret;
309            }
310
311            file_put_contents($location . '/.env', $e);
312
313            $this->console->alertInfo('Application in Maintenance', 40);
314            $this->console->write('Application has been switched to maintenance mode.');
315            if (!empty($secret)) {
316                $this->console->write('');
317                $this->console->write('The secret is ' . $this->console->colorize($secret, Color::BOLD_GREEN));
318            }
319        } else if (file_exists($location . '/.env') && (App::isDown())) {
320            $this->console->write('Application is currently in maintenance mode. No action to take.');
321            $currentSecret = App::env('MAINTENANCE_MODE_SECRET');
322            if (!empty($secret)) {
323                $e = str_replace(
324                    [
325                        'MAINTENANCE_MODE_SECRET=' . $currentSecret,
326                        'MAINTENANCE_MODE_SECRET="' . $currentSecret . '"',
327                    ],
328                    'MAINTENANCE_MODE_SECRET=' . $secret,
329                    file_get_contents($location . '/.env')
330                );
331
332                file_put_contents($location . '/.env', $e);
333            } else if (!empty($currentSecret)) {
334                $secret = $currentSecret;
335            }
336            if (!empty($secret)) {
337                $this->console->write('');
338                $this->console->write('The secret is ' . $this->console->colorize($secret, Color::BOLD_GREEN));
339            }
340        } else {
341            $this->console->write('No .env file found.');
342        }
343    }
344
345    /**
346     * Up command
347     *
348     * @return void
349     */
350    public function up(): void
351    {
352        $location = getcwd();
353
354        if (file_exists($location . '/.env') && (App::isDown())) {
355            $e = str_replace(
356                [
357                    'MAINTENANCE_MODE=true',
358                    'MAINTENANCE_MODE="true"',
359                    'MAINTENANCE_MODE=(true)',
360                ],
361                'MAINTENANCE_MODE=false',
362                file_get_contents($location . '/.env')
363            );
364
365            file_put_contents($location . '/.env', $e);
366
367            $this->console->alertSuccess('Application is Live', 40);
368            $this->console->write('Application has been made live.');
369        } else if (App::isUp()) {
370            $this->console->alertSuccess('Application is Live', 40);
371            $this->console->write('Application is currently live. No action to take.');
372        } else {
373            $this->console->write('No .env file found.');
374        }
375    }
376
377    /**
378     * Create custom command
379     *
380     * @param  string $command
381     * @return void
382     */
383    public function createCommand(string $command, array $options = []): void
384    {
385        $appModel = new Model\Application();
386        $appModel->createCommand($command, getcwd(), (isset($options['app'])));
387
388        $this->console->write("Command '" . $command ."' created.");
389
390        $this->console->write();
391        $this->console->write('Done!');
392    }
393
394    /**
395     * Create controller command
396     *
397     * @param  string $ctrl
398     * @param  array  $options
399     * @return void
400     */
401    public function createController(string $ctrl, array $options = []): void
402    {
403        $cli = (isset($options['cli'])) ? true : null;
404
405        $appModel  = new Model\Application();
406        $ctrlClass = $appModel->createController($ctrl, getcwd(), $cli);
407
408        $this->console->write("Controller class '" . $ctrlClass ."' created.");
409
410        $this->console->write();
411        $this->console->write('Done!');
412    }
413
414    /**
415     * Create model command
416     *
417     * @param  string $model
418     * @param  array  $options
419     * @return void
420     */
421    public function createModel(string $model, array $options = []): void
422    {
423        $appModel   = new Model\Application();
424        $modelClass = $appModel->createModel($model, getcwd(), (isset($options['data'])));
425
426        $this->console->write("Model class '" . $modelClass ."' created.");
427        $this->console->write();
428        $this->console->write('Done!');
429    }
430
431    /**
432     * Create view command
433     *
434     * @param  string $view
435     * @return void
436     */
437    public function createView(string $view): void
438    {
439        $appModel = new Model\Application();
440        $viewFile = $appModel->createView($view, getcwd());
441
442        $this->console->write("View file '" . $viewFile ."' created.");
443        $this->console->write();
444        $this->console->write('Done!');
445    }
446
447}