Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
75.77% covered (success)
75.77%
172 / 227
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Application
75.77% covered (success)
75.77%
172 / 227
16.67% covered (danger)
16.67%
1 / 6
155.01
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
98.08% covered (success)
98.08%
51 / 52
0.00% covered (danger)
0.00%
0 / 1
10
 createController
63.22% covered (warning)
63.22%
55 / 87
0.00% covered (danger)
0.00%
0 / 1
46.08
 createModel
68.18% covered (warning)
68.18%
30 / 44
0.00% covered (danger)
0.00%
0 / 1
16.64
 createView
53.33% covered (warning)
53.33%
8 / 15
0.00% covered (danger)
0.00%
0 / 1
7.54
 getNamespace
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
7.01
1<?php
2/**
3 * Pop PHP Framework (http://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@nolainteractive.com>
7 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
8 * @license    http://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Kettle\Model;
15
16use Pop\Code\Generator;
17use Pop\Dir\Dir;
18use Pop\Model\AbstractModel;
19use Pop\Kettle\Exception;
20
21/**
22 * Application model class
23 *
24 * @category   Pop\Kettle
25 * @package    Pop\Kettle
26 * @author     Nick Sagona, III <dev@nolainteractive.com>
27 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
28 * @license    http://www.popphp.org/license     New BSD License
29 * @version    2.3.2
30 */
31class Application extends AbstractModel
32{
33
34    /**
35     * Init application
36     *
37     * @param  string $location
38     * @param  string $namespace
39     * @param  ?bool  $web
40     * @param  ?bool  $api
41     * @param  ?bool  $cli
42     * @param  string $name
43     * @param  string $env
44     * @param  string $url
45     * @return void
46     */
47    public function init(
48        string $location, string $namespace, ?bool $web = null, ?bool $api = null, ?bool $cli = null,
49        string $name = 'Pop', string $env = 'local', string $url = 'http://localhost'
50    ): void
51    {
52        // API-only
53        if (($api === true) && empty($web) && empty($cli)) {
54            $install = 'api';
55        // Web+API
56        } else if (($web === true) && ($api === true) && empty($cli)) {
57            $install = 'web-api';
58        // API+CLI
59        } else if (($api === true) && ($cli === true) && empty($web)) {
60            $install = 'api-cli';
61        // Web+CLI
62        } else if (($web === true) && ($cli === true) && empty($api)) {
63            $install = 'web-cli';
64        // CLI-only
65        } else if (($cli === true) && empty($web) && empty($api)) {
66            $install = 'cli';
67        // Install all
68        } else if (($web === true) && ($api === true) && ($cli === true)) {
69            $install = 'web-api-cli';
70        // Default to web-only
71        } else {
72            $install = 'web';
73        }
74
75        $this->install($install, $location, $namespace, $name, $env, $url);
76    }
77
78    /**
79     * Install application files
80     *
81     * @param  string $install
82     * @param  string $location
83     * @param  string $namespace
84     * @param  string $name
85     * @param  string $env
86     * @param  string $url
87     * @return void
88     */
89    public function install(
90        string $install, string $location, string $namespace,
91        string $name = 'Pop', string $env = 'local', string $url = 'http://localhost'
92    ): void
93    {
94        $script = strtolower(str_replace('\\', '-', $namespace));
95        $path   = realpath(__DIR__ . '/../../config/templates/' . $install);
96        $dir    = new Dir($path);
97        foreach ($dir as $entry) {
98            if ($path . DIRECTORY_SEPARATOR . $entry) {
99                $d = new Dir($path . DIRECTORY_SEPARATOR . $entry);
100                $d->copyTo($location);
101            }
102        }
103
104        $dir = new Dir($location . '/app', [
105            'filesOnly' => true,
106            'recursive' => true,
107            'absolute'  => true
108        ]);
109
110        foreach ($dir as $file) {
111            file_put_contents($file, str_replace(['MyApp', 'myapp'], [$namespace, $script], file_get_contents($file)));
112        }
113
114        if (file_exists($location . DIRECTORY_SEPARATOR . 'public')) {
115            file_put_contents(
116                $location . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php',
117                str_replace(
118                    ['MyApp', 'myapp'], [$namespace, $script],
119                    file_get_contents($location . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR . 'index.php')
120                )
121            );
122        }
123
124        if (file_exists($location . DIRECTORY_SEPARATOR . 'script')) {
125            file_put_contents(
126                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp',
127                str_replace(
128                    ['MyApp', 'myapp'], [$namespace, $script],
129                    file_get_contents($location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp')
130                )
131            );
132            rename(
133                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . 'myapp',
134                $location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . $script
135            );
136            chmod($location . DIRECTORY_SEPARATOR . 'script' . DIRECTORY_SEPARATOR . $script, 0755);
137        }
138
139        if (!file_exists($location . DIRECTORY_SEPARATOR . '/.env')) {
140            copy(
141                __DIR__ . '/../../config/templates/orig.env',
142                $location . DIRECTORY_SEPARATOR . '/.env'
143            );
144        }
145
146        if (str_contains($name, ' ') && !str_starts_with($name, '"') && !str_ends_with($name, '"')) {
147            $name = '"' . $name . '"';
148        }
149
150        $env = str_replace([
151            'APP_NAME=Pop',
152            'APP_ENV=local',
153            'APP_URL=http://localhost',
154        ], [
155            'APP_NAME=' . $name,
156            'APP_ENV=' . $env,
157            'APP_URL=' . $url,
158        ], file_get_contents($location . DIRECTORY_SEPARATOR . '/.env'));
159
160        file_put_contents($location . DIRECTORY_SEPARATOR . '/.env', $env);
161    }
162
163    /**
164     * Create controller method
165     *
166     * @param  string $ctrl
167     * @param  string $location
168     * @param  ?bool  $web
169     * @param  ?bool  $api
170     * @param  ?bool  $cli
171     * @throws Exception
172     * @return array
173     */
174    public function createController(
175        string $ctrl, string $location, ?bool $web = null, ?bool $api = null, ?bool $cli = null
176    ): array
177    {
178        $namespace = $this->getNamespace($location);
179
180        $cliFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
181            'Console' . DIRECTORY_SEPARATOR . 'Controller';
182
183        $httpFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
184            'Http' . DIRECTORY_SEPARATOR . 'Controller';
185
186        $webFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
187            'Http' . DIRECTORY_SEPARATOR . 'Web' . DIRECTORY_SEPARATOR . 'Controller';
188
189        $apiFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR .
190            'Http' . DIRECTORY_SEPARATOR . 'Api' . DIRECTORY_SEPARATOR . 'Controller';
191
192        $createdCtrls = [];
193
194        // Create CLI controller
195        if ($cli === true) {
196            if (!file_exists($cliFolder)) {
197                throw new Exception('Error: The console folder and namespace has not been created');
198            }
199            $cliNamespace = $namespace . "\\Console\\Controller";
200
201            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
202                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
203                $ctrl    = array_pop($folders);
204
205                foreach ($folders as $folder) {
206                    $cliNamespace .= "\\" . $folder;
207                    $cliFolder    .= DIRECTORY_SEPARATOR . $folder;
208                    if (!file_exists($cliFolder)) {
209                        mkdir($cliFolder);
210                    }
211                }
212            }
213            $cliCtrlClassObject = new Generator\ClassGenerator($ctrl);
214            $cliCtrlClassObject->setParent("\\" . $namespace . "\\Console\\Controller\\AbstractController");
215
216            $namespaceObject = new Generator\NamespaceGenerator($cliNamespace);
217
218            $code = new Generator();
219            $code->addCodeObjects([$namespaceObject, $cliCtrlClassObject]);
220            $code->writeToFile($cliFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
221
222            $createdCtrls[] = $cliNamespace . "\\" . $ctrl;
223        }
224
225        // Create HTTP Web controller
226        if ($web === true) {
227            if (!file_exists($webFolder)) {
228                throw new Exception('Error: The HTTP web folder and namespace has not been created');
229            }
230            $webNamespace = $namespace . "\\Http\\Web\\Controller";
231
232            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
233                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
234                $ctrl    = array_pop($folders);
235
236                foreach ($folders as $folder) {
237                    $webNamespace .= "\\" . $folder;
238                    $webFolder    .= DIRECTORY_SEPARATOR . $folder;
239                    if (!file_exists($webFolder)) {
240                        mkdir($webFolder);
241                    }
242                }
243            }
244            $webCtrlClassObject = new Generator\ClassGenerator($ctrl);
245            $webCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Web\\Controller\\AbstractController");
246
247            $namespaceObject = new Generator\NamespaceGenerator($webNamespace);
248
249            $code = new Generator();
250            $code->addCodeObjects([$namespaceObject, $webCtrlClassObject]);
251            $code->writeToFile($webFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
252
253            $createdCtrls[] = $webNamespace . "\\" . $ctrl;
254        }
255
256        // Create HTTP API controller
257        if ($api === true) {
258            if (!file_exists($apiFolder)) {
259                throw new Exception('Error: The HTTP API folder and namespace has not been created');
260            }
261            $apiNamespace = $namespace . "\\Http\\Api\\Controller";
262
263            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
264                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
265                $ctrl    = array_pop($folders);
266
267                foreach ($folders as $folder) {
268                    $apiNamespace .= "\\" . $folder;
269                    $apiFolder    .= DIRECTORY_SEPARATOR . $folder;
270                    if (!file_exists($apiFolder)) {
271                        mkdir($apiFolder);
272                    }
273                }
274            }
275            $apiCtrlClassObject = new Generator\ClassGenerator($ctrl);
276            $apiCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Api\\Controller\\AbstractController");
277
278            $namespaceObject = new Generator\NamespaceGenerator($apiNamespace);
279
280            $code = new Generator();
281            $code->addCodeObjects([$namespaceObject, $apiCtrlClassObject]);
282            $code->writeToFile($apiFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
283
284            $createdCtrls[] = $apiNamespace . "\\" . $ctrl;
285        }
286
287        // Create HTTP controller
288        if (empty($web) && empty($api)) {
289            if (!file_exists($httpFolder)) {
290                throw new Exception('Error: The HTTP folder and namespace has not been created');
291            }
292            $httpNamespace = $namespace . "\\Http\\Controller";
293            if (strpos($ctrl, DIRECTORY_SEPARATOR)) {
294                $folders = explode(DIRECTORY_SEPARATOR, $ctrl);
295                $ctrl    = array_pop($folders);
296
297                foreach ($folders as $folder) {
298                    $httpNamespace .= "\\" . $folder;
299                    $httpFolder    .= DIRECTORY_SEPARATOR . $folder;
300                    if (!file_exists($httpFolder)) {
301                        mkdir($httpFolder);
302                    }
303                }
304            }
305            $httpCtrlClassObject = new Generator\ClassGenerator($ctrl);
306            $httpCtrlClassObject->setParent("\\" . $namespace . "\\Http\\Controller\\AbstractController");
307
308            $namespaceObject = new Generator\NamespaceGenerator($httpNamespace);
309
310            $code = new Generator();
311            $code->addCodeObjects([$namespaceObject, $httpCtrlClassObject]);
312            $code->writeToFile($httpFolder . DIRECTORY_SEPARATOR . $ctrl . '.php');
313
314            $createdCtrls[] = $httpNamespace . "\\" . $ctrl;
315        }
316
317        return $createdCtrls;
318    }
319
320    /**
321     * Create model method
322     *
323     * @param  string $model
324     * @param  string $location
325     * @param  bool   $data
326     * @return string
327     */
328    public function createModel(string $model, string $location, bool $data = false): string
329    {
330        $namespace   = $this->getNamespace($location) . "\\Model";
331        $modelFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Model';
332        if (!file_exists($modelFolder)) {
333            mkdir($modelFolder);
334        }
335
336        if (strpos($model, DIRECTORY_SEPARATOR)) {
337            $folders = explode(DIRECTORY_SEPARATOR, $model);
338            $model   = array_pop($folders);
339
340            foreach ($folders as $folder) {
341                $namespace   .= "\\" . $folder;
342                $modelFolder .= DIRECTORY_SEPARATOR . $folder;
343                if (!file_exists($modelFolder)) {
344                    mkdir($modelFolder);
345                }
346            }
347        }
348
349        $abstractModel = ($data) ? 'AbstractDataModel' : 'AbstractModel';
350
351        $modelClassObject = new Generator\ClassGenerator($model);
352        $modelClassObject->setParent($abstractModel);
353
354        $namespaceObject = new Generator\NamespaceGenerator($namespace);
355        $namespaceObject->addUse('Pop\Model\\' . $abstractModel);
356
357        $code = new Generator();
358        $code->addCodeObjects([$namespaceObject, $modelClassObject]);
359        $code->writeToFile($modelFolder . DIRECTORY_SEPARATOR . $model . '.php');
360
361        if ($data) {
362            $table = $model;
363
364            if (!str_ends_with($model, 's')) {
365                $table .= 's';
366            }
367
368            $tableNamespace = $this->getNamespace($location) . "\\Table";
369            $tableFolder    = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Table';
370            if (!file_exists($tableFolder)) {
371                mkdir($tableFolder);
372            }
373
374            if (strpos($table, DIRECTORY_SEPARATOR)) {
375                $folders = explode(DIRECTORY_SEPARATOR, $table);
376                $table   = array_pop($folders);
377
378                foreach ($folders as $folder) {
379                    $namespace   .= "\\" . $folder;
380                    $tableFolder .= DIRECTORY_SEPARATOR . $folder;
381                    if (!file_exists($tableFolder)) {
382                        mkdir($tableFolder);
383                    }
384                }
385            }
386
387            $tableClassObject = new Generator\ClassGenerator($table);
388            $tableClassObject->setParent('Record');
389
390            $tableNamespaceObject = new Generator\NamespaceGenerator($tableNamespace);
391            $tableNamespaceObject->addUse('Pop\Db\Record');
392
393            $code = new Generator();
394            $code->addCodeObjects([$tableNamespaceObject, $tableClassObject]);
395            $code->writeToFile($tableFolder . DIRECTORY_SEPARATOR . $table . '.php');
396        }
397
398        return $namespace . "\\" . $model;
399    }
400
401    /**
402     * Create view method
403     *
404     * @param  string $view
405     * @param  string $location
406     * @return string
407     */
408    public function createView(string $view, string $location): string
409    {
410        $origView   = $view;
411        $viewFolder = $location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'view';
412        if (!file_exists($viewFolder)) {
413            mkdir($viewFolder);
414        }
415
416        if (strpos($view, DIRECTORY_SEPARATOR)) {
417            $folders = explode(DIRECTORY_SEPARATOR, $view);
418            $view    = array_pop($folders);
419
420            foreach ($folders as $folder) {
421                $viewFolder .= DIRECTORY_SEPARATOR . $folder;
422                if (!file_exists($viewFolder)) {
423                    mkdir($viewFolder);
424                }
425            }
426        }
427
428        touch($viewFolder . DIRECTORY_SEPARATOR . $view);
429        $index = file_get_contents(realpath(__DIR__ . '/../../config/templates/web/app/view/index.phtml'));
430        file_put_contents($viewFolder . DIRECTORY_SEPARATOR . $view, $index);
431
432        return $origView;
433    }
434
435    /**
436     * Get namespace
437     *
438     * @param  string $location
439     * @throws Exception
440     * @return string
441     */
442    public function getNamespace(string $location): string
443    {
444        if (file_exists($location . DIRECTORY_SEPARATOR . 'app') &&
445            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src') &&
446            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Application.php')) {
447            $fileContents = file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Application.php');
448            $namespace    = substr($fileContents, (strpos($fileContents, 'namespace ') + 10));
449            $namespace    = substr($namespace, 0, strpos($namespace, ';'));
450
451            return $namespace;
452        } else if (file_exists($location . DIRECTORY_SEPARATOR . 'app') &&
453            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src') &&
454            file_exists($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Module.php')) {
455            $fileContents = file_get_contents($location . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'Module.php');
456            $namespace    = substr($fileContents, (strpos($fileContents, 'namespace ') + 10));
457            $namespace    = substr($namespace, 0, strpos($namespace, ';'));
458
459            return $namespace;
460        } else {
461            throw new Exception('Error: Unable to detect namespace.');
462        }
463    }
464
465}