Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
80 / 80
100.00% covered (success)
100.00%
18 / 18
CRAP
100.00% covered (success)
100.00%
1 / 1
Generator
100.00% covered (success)
100.00%
80 / 80
100.00% covered (success)
100.00%
18 / 18
49
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 addCodeObjects
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 addCodeObject
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 hasCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 code
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setCloseTag
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasCloseTag
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setEnv
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getEnv
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasEnv
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setFilename
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 hasFilename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFilename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 render
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
15
 writeToFile
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 outputToHttp
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
7
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
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\Code;
15
16use Pop\Code\Generator\GeneratorInterface;
17use Pop\Code\Generator\Traits;
18
19/**
20 * Generator code class
21 *
22 * @category   Pop
23 * @package    Pop\Code
24 * @author     Nick Sagona, III <dev@nolainteractive.com>
25 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
26 * @license    http://www.popphp.org/license     New BSD License
27 * @version    5.0.0
28 */
29class Generator extends Generator\AbstractGenerator
30{
31
32    use Traits\DocblockTrait;
33
34    /**
35     * Code generator objects
36     * @var array
37     */
38    protected array $code = [];
39
40    /**
41     * Namespaces for the code generator objects
42     * @var array
43     */
44    protected array $namespaces = [];
45
46    /**
47     * Environment setting, i.e. #!/usr/bin/php
48     * @var ?string
49     */
50    protected ?string $env = null;
51
52    /**
53     * Flag to close the code file with ?>
54     * @var bool
55     */
56    protected bool $close = false;
57
58    /**
59     * Code filename
60     * @var ?string
61     */
62    protected ?string $filename = null;
63
64    /**
65     * Constructor
66     *
67     * Instantiate the code generator object
68     *
69     * @param  mixed $code
70     * @throws Exception
71     */
72    public function __construct(mixed $code = null)
73    {
74        if ($code !== null) {
75            if (is_array($code)) {
76                $this->addCodeObjects($code);
77            } else if ($code instanceof GeneratorInterface) {
78                $this->addCodeObject($code);
79            } else {
80                throw new Exception('Error: The code parameter was not the correct type.');
81            }
82        }
83    }
84
85    /**
86     * Add code generator objects
87     *
88     * @param  array $codeObjects
89     * @return Generator
90     */
91    public function addCodeObjects(array $codeObjects): Generator
92    {
93        foreach ($codeObjects as $namespace => $codeObject) {
94            if (!is_numeric($namespace)) {
95                if (is_array($codeObject)) {
96                    foreach ($codeObject as $code) {
97                        $this->addCodeObject($code, $namespace);
98                    }
99                } else {
100                    $this->addCodeObject($codeObject, $namespace);
101                }
102            } else {
103                $this->addCodeObject($codeObject);
104            }
105        }
106        return $this;
107    }
108
109    /**
110     * Add a code generator object
111     *
112     * @param  Generator\GeneratorInterface $codeObject
113     * @param  ?string                       $namespace
114     * @return Generator
115     */
116    public function addCodeObject(Generator\GeneratorInterface $codeObject, ?string $namespace = null): Generator
117    {
118        $this->code[] = $codeObject;
119
120        if ($namespace !== null) {
121            $key = count($this->code) - 1;
122            $this->namespaces[$key] = $namespace;
123        }
124        return $this;
125    }
126
127    /**
128     * Has code generator objects
129     *
130     * @return bool
131     */
132    public function hasCode(): bool
133    {
134        return (!empty($this->code));
135    }
136
137    /**
138     * Access the code generator object
139     *
140     * @return array
141     */
142    public function getCode(): array
143    {
144        return $this->code;
145    }
146
147    /**
148     * Access the code generator objects (alias method)
149     *
150     * @return array
151     */
152    public function code(): array
153    {
154        return $this->code;
155    }
156
157    /**
158     * Set the code close tag flag
159     *
160     * @param  bool $close
161     * @return Generator
162     */
163    public function setCloseTag(bool $close = false): Generator
164    {
165        $this->close = $close;
166        return $this;
167    }
168
169    /**
170     * Determine if the code close tag flag is set
171     *
172     * @return bool
173     */
174    public function hasCloseTag(): bool
175    {
176        return $this->close;
177    }
178
179    /**
180     * Set the environment
181     *
182     * @param  ?string $env
183     * @return Generator
184     */
185    public function setEnv(?string $env = null): Generator
186    {
187        $this->env = $env;
188        return $this;
189    }
190
191    /**
192     * Get the environment
193     *
194     * @return string|null
195     */
196    public function getEnv(): string|null
197    {
198        return $this->env;
199    }
200
201    /**
202     * Determine if the environment is set
203     *
204     * @return bool
205     */
206    public function hasEnv(): bool
207    {
208        return ($this->env !== null);
209    }
210
211    /**
212     * Set the filename
213     *
214     * @param  string $filename
215     * @return Generator
216     */
217    public function setFilename(string $filename): Generator
218    {
219        $this->filename = $filename;
220        return $this;
221    }
222
223    /**
224     * Has filename
225     *
226     * @return bool
227     */
228    public function hasFilename(): bool
229    {
230        return ($this->filename !== null);
231    }
232
233    /**
234     * Get filename
235     *
236     * @return string|null
237     */
238    public function getFilename(): string|null
239    {
240        return $this->filename;
241    }
242
243    /**
244     * Render method
245     *
246     * @return string
247     */
248    public function render(): string
249    {
250        $this->output = '';
251
252        if ($this->env !== null) {
253            $this->output .= $this->env . PHP_EOL;
254        }
255
256        $this->output    .= '<?php' . PHP_EOL;
257        $this->output    .= ($this->docblock !== null) ? $this->docblock->render() . PHP_EOL : null;
258        $currentNamespace = null;
259        $inNamespace      = false;
260
261        foreach ($this->code as $key => $code) {
262            if (isset($this->namespaces[$key]) && ($currentNamespace != $this->namespaces[$key])) {
263                if ($currentNamespace !== null) {
264                    $this->output .= '}' . PHP_EOL . PHP_EOL;
265                }
266                $namespace        = ($this->namespaces[$key] != '*') ? $this->namespaces[$key] . ' ' : null;
267                $this->output    .= 'namespace ' . $namespace . '{' . PHP_EOL;
268                $currentNamespace = $this->namespaces[$key];
269                $inNamespace      = true;
270            } else if (!isset($this->namespaces[$key]) && ($currentNamespace !== null)) {
271                $this->output .= '}' . PHP_EOL . PHP_EOL;
272                $inNamespace   = false;
273            }
274
275            if ($currentNamespace !== null) {
276                $code->setIndent($code->getIndent() + $this->indent);
277                if ($code->hasDocblock()) {
278                    $code->getDocblock()->setIndent($code->getDocblock()->getIndent() + $this->indent);
279                }
280                if (in_array('Pop\Code\Generator\Traits\BodyTrait', class_uses($code))) {
281                    $code->indentBody($this->indent);
282                }
283            }
284
285            $this->output .= $code;
286        }
287
288        if ($inNamespace) {
289            $this->output .= '}' . PHP_EOL . PHP_EOL;
290        }
291
292        if ($this->close) {
293            $this->output .= '?>' . PHP_EOL;
294        }
295
296        return $this->output;
297    }
298
299    /**
300     * Write to file
301     *
302     * @param  ?string $filename
303     * @throws Exception
304     * @return void
305     */
306    public function writeToFile(?string $filename = null): void
307    {
308        if (($this->filename !== null) && ($filename === null)) {
309            $filename = $this->filename;
310        }
311        if (empty($filename)) {
312            throw new Exception('Error: The filename has not been set.');
313        }
314
315        file_put_contents($filename, $this->render());
316    }
317
318    /**
319     * Output to HTTP
320     *
321     * @param  ?string $filename
322     * @param  bool    $forceDownload
323     * @param  array   $headers
324     * @return void
325     */
326    public function outputToHttp(?string $filename = null, bool $forceDownload = false, array $headers = []): void
327    {
328        if (($this->filename !== null) && ($filename === null)) {
329            $filename = $this->filename;
330        }
331        if (empty($filename)) {
332            $filename = 'code.php';
333        }
334
335        $headers['Content-Type']        = 'text/plain';
336        $headers['Content-Disposition'] = (($forceDownload) ? 'attachment; ' : null) . 'filename=' . $filename;
337
338        // Send the headers and output the PDF
339        if (!headers_sent()) {
340            header('HTTP/1.1 200 OK');
341            foreach ($headers as $name => $value) {
342                header($name . ': ' . $value);
343            }
344        }
345
346        echo $this->render();
347    }
348
349    /**
350     * Print code
351     *
352     * @return string
353     */
354    public function __toString(): string
355    {
356        return $this->render();
357    }
358
359}