Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.81% covered (success)
97.81%
312 / 319
87.50% covered (success)
87.50%
7 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Application
97.81% covered (success)
97.81%
312 / 319
87.50% covered (success)
87.50%
7 / 8
94
0.00% covered (danger)
0.00%
0 / 1
 init
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
19
 install
100.00% covered (success)
100.00%
101 / 101
100.00% covered (success)
100.00%
1 / 1
18
 createCommand
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
6
 createController
100.00% covered (success)
100.00%
88 / 88
100.00% covered (success)
100.00%
1 / 1
24
 createModel
85.42% covered (success)
85.42%
41 / 48
0.00% covered (danger)
0.00%
0 / 1
12.45
 createView
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 getNamespace
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
7
 resolveAppInstance
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (http://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    http://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Kettle\Model;
16
17use Pop\Code\Generator;
18use Pop\Dir\Dir;
19use Pop\Utils\AbstractModel;
20use Pop\Kettle\Exception;
21use Pop\Utils\Str;
22
23/**
24 * Application model class
25 *
26 * @category   Pop\Kettle
27 * @package    Pop\Kettle
28 * @author     Nick Sagona, III <dev@noladev.com>
29 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
30 * @license    http://www.popphp.org/license     New BSD License
31 * @version    3.0.0
32 */
33class Application extends AbstractModel
34{
35
36    /**
37     * Init application
38     *
39     * @param  string $location
40     * @param  string $namespace
41     * @param  ?bool  $web
42     * @param  ?bool  $api
43     * @param  ?bool  $cli
44     * @param  string $name
45     * @param  string $env
46     * @param  string $url
47     * @param  bool   $cliApp
48     * @param  bool   $createDb
49     * @return void
50     */
51    public function init(
52        string $location, string $namespace, ?bool $web = null, ?bool $api = null, ?bool $cli = null,
53        string $name = 'MyApp', string $env = 'local', string $url = '', bool $cliApp = false, bool $createDb = false
54    ): void
55    {
56        // API-only
57        if (($api === true) && empty($web) && empty($cli)) {
58            $install = 'api';
59        // Web+API
60        } else if (($web === true) && ($api === true) && empty($cli)) {
61            $install = 'web-api';
62        // API+CLI
63        } else if (($api === true) && ($cli === true) && empty($web)) {
64            $install = 'api-cli';
65        // Web+CLI
66        } else if (($web === true) && ($cli === true) && empty($api)) {
67            $install = 'web-cli';
68        // CLI-only
69        } else if (($cli === true) && empty($web) && empty($api)) {
70            $install = 'cli';
71        // Install all
72        } else if (($web === true) && ($api === true) && ($cli === true)) {
73            $install = 'web-api-cli';
74        // Default to web-only
75        } else {
76            $install = 'web';
77        }
78
79        $this->install($install, $location, $namespace, $name, $env, $url, $cliApp, $createDb);
80    }
81
82    /**
83     * Install application files
84     *
85     * @param  string $install
86     * @param  string $location
87     * @param  string $namespace
88     * @param  string $name
89     * @param  string $env
90     * @param  string $url
91     * @param  bool   $cliApp
92     * @param  bool   $createDb
93     * @return void
94     */
95    public function install(
96        string $install, string $location, string $namespace, string $name = 'MyApp',
97        string $env = 'local', string $url = '', bool $cliApp = false, bool $createDb = false
98    ): void
99    {
100        copy($location . DIRECTORY_SEPARATOR . 'kettle.inc.orig.php', $location . DIRECTORY_SEPARATOR . 'kettle.inc.php');
101
102        $script = strtolower(str_replace('\\', '-', $namespace));
103        $path   = realpath(__DIR__ . '/../../config/templates/codebase/' . $install);
104        $dir    = new Dir($path);
105        foreach ($dir as $entry) {
106            if (is_dir($path . DIRECTORY_SEPARATOR . $entry)) {
107                $d = new Dir($path . DIRECTORY_SEPARATOR . $entry);
108                $d->copyTo($location);
109            }
110        }
111
112        $dir = new Dir($location . '/app', [
113            'filesOnly' => true,
114            'recursive' => true,
115            'absolute'  => true
116        ]);
117
118        foreach ($dir as $file) {
119            file_put_contents($file, str_replace(['MyApp', 'myapp'], [$namespace, $script], file_get_contents($file)));
120        }
121
122        // Set up web /public folder and files
123        if (str_contains($install, 'web')) {
124            mkdir($location . DIRECTORY_SEPARATOR . 'public');
125            copy(__DIR__ . '/../../config/templates/public/.htaccess', $location . DIRECTORY_SEPARATOR . 'public/.htaccess');
126            copy(__DIR__ . '/../../config/templates/public/index.php', $location . DIRECTORY_SEPARATOR . 'public/index.php');
127
128            file_put_contents(
129                $location . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php',
130                str_replace(
131                    ['MyApp', 'myapp'], [$namespace, $script],
132                    file_get_contents($location . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php')
133                )
134            );
135
136            // Copy view files
137            mkdir($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'view');
138            copy(__DIR__ . '/../../config/templates/view/index.phtml', $location . DIRECTORY_SEPARATOR . 'app/view/index.phtml');
139            copy(__DIR__ . '/../../config/templates/view/error.phtml', $location . DIRECTORY_SEPARATOR . 'app/view/error.phtml');
140            copy(__DIR__ . '/../../config/templates/view/exception.phtml', $location . DIRECTORY_SEPARATOR . 'app/view/exception.phtml');
141            copy(__DIR__ . '/../../config/templates/view/maintenance.phtml', $location . DIRECTORY_SEPARATOR . 'app/view/maintenance.phtml');
142        }
143
144        // Set up CLI /script folder and application script
145        if ($cliApp) {
146            mkdir($location . DIRECTORY_SEPARATOR . 'script');
147            copy(__DIR__ . '/../../config/templates/script/myapp', $location . DIRECTORY_SEPARATOR . 'script/myapp');
148
149            file_put_contents(
150                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp',
151                str_replace(
152                    ['MyApp', 'myapp'], [$namespace, $script],
153                    file_get_contents($location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp')
154                )
155            );
156
157            rename(
158                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp',
159                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . $script
160            );
161            chmod($location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . $script, 0755);
162        } else if (file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Console' . DIRECTORY_SEPARATOR . 'Controller')) {
163            $dir = new Dir($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Console' . DIRECTORY_SEPARATOR . 'Controller');
164            $dir->emptyDir(true);
165        }
166
167        // Add writable /data folder
168        mkdir($location . DIRECTORY_SEPARATOR . 'data');
169        chmod($location . DIRECTORY_SEPARATOR . 'data', 0777);
170        touch($location . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . '.empty');
171
172        // Set up database folder
173        if ($createDb) {
174            mkdir($location . DIRECTORY_SEPARATOR . 'database');
175            $dbPath = realpath(__DIR__ . '/../../config/templates/database');
176            $dir    = new Dir($dbPath);
177            foreach ($dir as $entry) {
178                if (is_dir($dbPath . DIRECTORY_SEPARATOR . $entry)) {
179                    $d = new Dir($dbPath . DIRECTORY_SEPARATOR . $entry);
180                    $d->copyTo($location . DIRECTORY_SEPARATOR . 'database');
181                }
182            }
183
184            copy(
185                __DIR__ . '/../../config/templates/database.php',
186                $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'database.php'
187            );
188        } else {
189            if (file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.http.php')) {
190                file_put_contents(
191                    $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.http.php',
192                    str_replace(
193                        "    'database' => include __DIR__ . '/database.php',", "",
194                        file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.http.php')
195                    )
196                );
197            }
198            if (file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.console.php')) {
199                file_put_contents(
200                    $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.console.php',
201                    str_replace(
202                        "    'database' => include __DIR__ . '/database.php',", "",
203                        file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.console.php')
204                    )
205                );
206            }
207        }
208
209        // Populate kettle.inc.php file autoloader for application
210        if (file_exists($location . DIRECTORY_SEPARATOR . 'kettle.inc.php')) {
211            $autoloader = "\$autoloader->addPsr4('{$namespace}\\\\', __DIR__ . '/app/src');" . PHP_EOL;
212            if (!str_contains(file_get_contents($location . DIRECTORY_SEPARATOR . 'kettle.inc.php'), $autoloader)) {
213                file_put_contents($location . DIRECTORY_SEPARATOR . 'kettle.inc.php', $autoloader, FILE_APPEND);
214            }
215        }
216
217        // Copy .env file over and populate with values
218        if (!file_exists($location . DIRECTORY_SEPARATOR . '/.env')) {
219            copy(
220                __DIR__ . '/../../config/templates/orig.env',
221                $location . DIRECTORY_SEPARATOR . '/.env'
222            );
223        }
224
225        if (str_contains($name, ' ') && !str_starts_with($name, '"') && !str_ends_with($name, '"')) {
226            $name = '"' . $name . '"';
227        }
228
229        $env = str_replace([
230            'APP_NAME=MyApp',
231            'APP_ENV=local',
232            'APP_URL=http://localhost',
233        ], [
234            'APP_NAME=' . $name,
235            'APP_ENV=' . $env,
236            'APP_URL=' . $url,
237        ], file_get_contents($location . DIRECTORY_SEPARATOR . '/.env'));
238
239        file_put_contents($location . DIRECTORY_SEPARATOR . '/.env', $env);
240    }
241
242    /**
243     * Create command method
244     *
245     * @param  string $command
246     * @param  string $location
247     * @param  bool   $app
248     * @throws Exception
249     * @return void
250     */
251    public function createCommand(string $command, string $location, bool $app = false): void
252    {
253        $command   = strtolower($command);
254        $namespace = $this->getNamespace($location);
255
256        $commandFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
257            'Console' . DIRECTORY_SEPARATOR . 'Command';
258
259        if (!$app) {
260            $commandFolder .= DIRECTORY_SEPARATOR . 'Kettle';
261        }
262
263        if (!file_exists($commandFolder)) {
264            throw new Exception('Error: The command folder and namespace has not been created');
265        }
266
267        $classCommandName = (str_contains($command, ':')) ?
268            substr($command, (strrpos($command, ':') + 1)) : $command;
269
270        if (!file_exists($commandFolder . DIRECTORY_SEPARATOR . $classCommandName . '.php')) {
271            $classCommandName = ucfirst(Str::convertToCamelCase($classCommandName, '-'));
272
273            $commandNamespace = $namespace . "\\Console\\Command";
274
275            if (!$app) {
276                $commandNamespace .= '\\Kettle';
277            }
278
279            $namespaceObject  = new Generator\NamespaceGenerator($commandNamespace);
280
281            $commandClassObject = new Generator\ClassGenerator($classCommandName);
282            $commandClassObject->setParent("\\Pop\\Console\\Command\\AbstractCommand");
283
284            $nameProperty   = new Generator\PropertyGenerator('name', '?string', $command);
285            $paramsProperty = new Generator\PropertyGenerator('params', '?string');
286            $helpProperty   = new Generator\PropertyGenerator('help', '?string', "This is the " . $command . " command");
287            $handleMethod   = new Generator\MethodGenerator('handle', 'public');
288            $handleMethod->setBody('/** Add command code here. */')
289                ->setDocblock(new Generator\DocblockGenerator("  \$this->application and \$this->console are both available."));
290
291            $commandClassObject->addProperties([$nameProperty, $paramsProperty, $helpProperty])
292                ->addMethod($handleMethod);
293
294            $code = new Generator();
295            $code->addCodeObjects([$namespaceObject, $commandClassObject]);
296            $code->writeToFile($commandFolder . DIRECTORY_SEPARATOR . $classCommandName . '.php');
297        }
298    }
299
300    /**
301     * Create controller method
302     *
303     * @param  string $ctrl
304     * @param  string $location
305     * @param  ?bool  $web
306     * @param  ?bool  $api
307     * @param  ?bool  $cli
308     * @throws Exception
309     * @return array
310     */
311    public function createController(
312        string $ctrl, string $location, ?bool $web = null, ?bool $api = null, ?bool $cli = null
313    ): array
314    {
315        $namespace = $this->getNamespace($location);
316
317        $cliFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
318            'Console' . DIRECTORY_SEPARATOR . 'Controller';
319
320        $httpFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
321            'Http' . DIRECTORY_SEPARATOR . 'Controller';
322
323        $webFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
324            'Http' . DIRECTORY_SEPARATOR . 'Web' . DIRECTORY_SEPARATOR . 'Controller';
325
326        $apiFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
327            'Http' . DIRECTORY_SEPARATOR . 'Api' . DIRECTORY_SEPARATOR . 'Controller';
328
329        $createdCtrls = [];
330
331        // Create CLI controller
332        if ($cli === true) {
333            $scriptFolder = $location . DIRECTORY_SEPARATOR . 'script';
334
335            if (!file_exists($cliFolder) || !file_exists($scriptFolder)) {
336                throw new Exception('Error: This application was not initialized with a stand-alone console application.');
337            }
338            $cliNamespace = $namespace . "\\Console\\Controller";
339
340            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
341                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
342                $ctrl    = array_pop($folders);
343
344                foreach ($folders as $folder) {
345                    $cliNamespace .= "\\" . $folder;
346                    $cliFolder    .= DIRECTORY_SEPARATOR . $folder;
347                    if (!file_exists($cliFolder)) {
348                        mkdir($cliFolder);
349                    }
350                }
351            }
352            $cliCtrlClassObject = new Generator\ClassGenerator($ctrl);
353            $cliCtrlClassObject->setParent("\\" . $namespace . "\\Console\\Controller\\AbstractController");
354
355            $namespaceObject = new Generator\NamespaceGenerator($cliNamespace);
356
357            $code = new Generator();
358            $code->addCodeObjects([$namespaceObject, $cliCtrlClassObject]);
359            $code->writeToFile($cliFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
360
361            $createdCtrls[] = $cliNamespace . "\\" . $ctrl;
362        }
363
364        // Create HTTP Web controller
365        if ($web === true) {
366            if (!file_exists($webFolder)) {
367                throw new Exception('Error: The HTTP web folder and namespace has not been created');
368            }
369            $webNamespace = $namespace . "\\Http\\Web\\Controller";
370
371            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
372                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
373                $ctrl    = array_pop($folders);
374
375                foreach ($folders as $folder) {
376                    $webNamespace .= "\\" . $folder;
377                    $webFolder    .= DIRECTORY_SEPARATOR . $folder;
378                    if (!file_exists($webFolder)) {
379                        mkdir($webFolder);
380                    }
381                }
382            }
383            $webCtrlClassObject = new Generator\ClassGenerator($ctrl);
384            $webCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Web\\Controller\\AbstractController");
385
386            $namespaceObject = new Generator\NamespaceGenerator($webNamespace);
387
388            $code = new Generator();
389            $code->addCodeObjects([$namespaceObject, $webCtrlClassObject]);
390            $code->writeToFile($webFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
391
392            $createdCtrls[] = $webNamespace . "\\" . $ctrl;
393        }
394
395        // Create HTTP API controller
396        if ($api === true) {
397            if (!file_exists($apiFolder)) {
398                throw new Exception('Error: The HTTP API folder and namespace has not been created');
399            }
400            $apiNamespace = $namespace . "\\Http\\Api\\Controller";
401
402            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
403                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
404                $ctrl    = array_pop($folders);
405
406                foreach ($folders as $folder) {
407                    $apiNamespace .= "\\" . $folder;
408                    $apiFolder    .= DIRECTORY_SEPARATOR . $folder;
409                    if (!file_exists($apiFolder)) {
410                        mkdir($apiFolder);
411                    }
412                }
413            }
414            $apiCtrlClassObject = new Generator\ClassGenerator($ctrl);
415            $apiCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Api\\Controller\\AbstractController");
416
417            $namespaceObject = new Generator\NamespaceGenerator($apiNamespace);
418
419            $code = new Generator();
420            $code->addCodeObjects([$namespaceObject, $apiCtrlClassObject]);
421            $code->writeToFile($apiFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
422
423            $createdCtrls[] = $apiNamespace . "\\" . $ctrl;
424        }
425
426        // Default to creating HTTP controller
427        if (($web === null) && ($api == null) && ($cli === null)) {
428            if (!file_exists($httpFolder)) {
429                throw new Exception('Error: The HTTP folder and namespace has not been created');
430            }
431            $httpNamespace = $namespace . "\\Http\\Controller";
432            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
433                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
434                $ctrl    = array_pop($folders);
435
436                foreach ($folders as $folder) {
437                    $httpNamespace .= "\\" . $folder;
438                    $httpFolder    .= DIRECTORY_SEPARATOR . $folder;
439                    if (!file_exists($httpFolder)) {
440                        mkdir($httpFolder);
441                    }
442                }
443            }
444            $httpCtrlClassObject = new Generator\ClassGenerator($ctrl);
445            $httpCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Controller\\AbstractController");
446
447            $namespaceObject = new Generator\NamespaceGenerator($httpNamespace);
448
449            $code = new Generator();
450            $code->addCodeObjects([$namespaceObject, $httpCtrlClassObject]);
451            $code->writeToFile($httpFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
452
453            $createdCtrls[] = $httpNamespace . "\\" . $ctrl;
454        }
455
456        return $createdCtrls;
457    }
458
459    /**
460     * Create model method
461     *
462     * @param  string $model
463     * @param  string $location
464     * @param  bool   $data
465     * @return string
466     */
467    public function createModel(string $model, string $location, bool $data = false): string
468    {
469        $namespace   = $this->getNamespace($location) . "\\Model";
470        $modelFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Model';
471        if (!file_exists($modelFolder)) {
472            mkdir($modelFolder);
473        }
474
475        if (strpos($model, DIRECTORY_SEPARATOR)) {
476            $folders = explode(DIRECTORY_SEPARATOR, $model);
477            $model   = array_pop($folders);
478
479            foreach ($folders as $folder) {
480                $namespace   .= "\\" . $folder;
481                $modelFolder .= DIRECTORY_SEPARATOR . $folder;
482                if (!file_exists($modelFolder)) {
483                    mkdir($modelFolder);
484                }
485            }
486        }
487
488        if ($data) {
489            $abstractModel = 'AbstractDataModel';
490            $useNamespace  = 'Pop\Db\Model\AbstractDataModel';
491
492        } else {
493            $abstractModel = 'AbstractModel';
494            $useNamespace  = 'Pop\Utils\AbstractModel';
495        }
496
497        $modelClassObject = new Generator\ClassGenerator($model);
498        $modelClassObject->setParent($abstractModel);
499
500        $namespaceObject = new Generator\NamespaceGenerator($namespace);
501        $namespaceObject->addUse($useNamespace);
502
503        $code = new Generator();
504        $code->addCodeObjects([$namespaceObject, $modelClassObject]);
505        $code->writeToFile($modelFolder . DIRECTORY_SEPARATOR . $model . '.php');
506
507        if ($data) {
508            $table = $model;
509
510            if (!str_ends_with($model, 's')) {
511                $table .= 's';
512            }
513
514            $tableNamespace = $this->getNamespace($location) . "\\Table";
515            $tableFolder    = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Table';
516            if (!file_exists($tableFolder)) {
517                mkdir($tableFolder);
518            }
519
520            if (strpos($table, DIRECTORY_SEPARATOR)) {
521                $folders = explode(DIRECTORY_SEPARATOR, $table);
522                $table   = array_pop($folders);
523
524                foreach ($folders as $folder) {
525                    $namespace   .= "\\" . $folder;
526                    $tableFolder .= DIRECTORY_SEPARATOR . $folder;
527                    if (!file_exists($tableFolder)) {
528                        mkdir($tableFolder);
529                    }
530                }
531            }
532
533            $tableClassObject = new Generator\ClassGenerator($table);
534            $tableClassObject->setParent('Record');
535
536            $tableNamespaceObject = new Generator\NamespaceGenerator($tableNamespace);
537            $tableNamespaceObject->addUse('Pop\Db\Record');
538
539            $code = new Generator();
540            $code->addCodeObjects([$tableNamespaceObject, $tableClassObject]);
541            $code->writeToFile($tableFolder . DIRECTORY_SEPARATOR . $table . '.php');
542        }
543
544        return $namespace . "\\" . $model;
545    }
546
547    /**
548     * Create view method
549     *
550     * @param  string $view
551     * @param  string $location
552     * @return string
553     */
554    public function createView(string $view, string $location): string
555    {
556        $origView   = $view;
557        $viewFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'view';
558        if (!file_exists($viewFolder)) {
559            mkdir($viewFolder);
560        }
561
562        if (strpos($view, DIRECTORY_SEPARATOR)) {
563            $folders = explode(DIRECTORY_SEPARATOR, $view);
564            $view    = array_pop($folders);
565
566            foreach ($folders as $folder) {
567                $viewFolder .= DIRECTORY_SEPARATOR . $folder;
568                if (!file_exists($viewFolder)) {
569                    mkdir($viewFolder);
570                }
571            }
572        }
573
574        touch($viewFolder . DIRECTORY_SEPARATOR . $view);
575        $index = file_get_contents(realpath(__DIR__ . '/../../config/templates/view/index.phtml'));
576        file_put_contents($viewFolder . DIRECTORY_SEPARATOR . $view, $index);
577
578        return $origView;
579    }
580
581    /**
582     * Get namespace
583     *
584     * @param  string $location
585     * @throws Exception
586     * @return string
587     */
588    public function getNamespace(string $location): string
589    {
590        if (file_exists($location . DIRECTORY_SEPARATOR . 'app') &&
591            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src') &&
592            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Application.php')) {
593            $fileContents = file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Application.php');
594            $namespace    = substr($fileContents, (strpos($fileContents, 'namespace ') + 10));
595            $namespace    = substr($namespace, 0, strpos($namespace, ';'));
596
597            return $namespace;
598        } else if (file_exists($location . DIRECTORY_SEPARATOR . 'app') &&
599            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src') &&
600            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Module.php')) {
601            $fileContents = file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Module.php');
602            $namespace    = substr($fileContents, (strpos($fileContents, 'namespace ') + 10));
603            $namespace    = substr($namespace, 0, strpos($namespace, ';'));
604
605            return $namespace;
606        } else {
607            throw new Exception('Error: Unable to detect namespace.');
608        }
609    }
610
611    /**
612     * Resolve a full instance of the consuming application, if one is scaffolded, merging
613     * $baseRoutes with its own app/src/Console/Command routes
614     *
615     * @param  string $dir
616     * @param  mixed  $autoloader
617     * @param  array  $baseRoutes
618     * @throws Exception
619     * @return ?\Pop\Application
620     */
621    public function resolveAppInstance(string $dir, mixed $autoloader, array $baseRoutes): ?\Pop\Application
622    {
623        $namespace = $this->getNamespace($dir);
624        $appClass  = $namespace . '\Application';
625
626        if (!class_exists($appClass)) {
627            return null;
628        }
629
630        $config = file_exists($dir . '/app/config/app.console.php')
631            ? include $dir . '/app/config/app.console.php'
632            : [];
633
634        $config['routes'] = \Pop\Console\CommandRegistry::loadRoutes($baseRoutes, $dir . '/app/src/Console/Command');
635
636        return new $appClass($autoloader, $config);
637    }
638
639}