Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.55% covered (success)
98.55%
136 / 138
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Parser
98.55% covered (success)
98.55%
136 / 138
66.67% covered (warning)
66.67%
2 / 3
66
0.00% covered (danger)
0.00%
0 / 1
 parseArrays
100.00% covered (success)
100.00%
85 / 85
100.00% covered (success)
100.00%
1 / 1
41
 parseConditionals
94.29% covered (success)
94.29%
33 / 35
0.00% covered (danger)
0.00%
0 / 1
10.02
 parseScalars
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
15
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\View\Template\Stream;
15
16/**
17 * View stream template parser class
18 *
19 * @category   Pop
20 * @package    Pop\View
21 * @author     Nick Sagona, III <dev@nolainteractive.com>
22 * @copyright  Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com)
23 * @license    http://www.popphp.org/license     New BSD License
24 * @version    4.0.0
25 */
26class Parser
27{
28
29    /**
30     * Parse arrays in the template string
31     *
32     * @param  string $template
33     * @param  array  $data
34     * @param  string $output
35     * @return string
36     */
37    public static function parseArrays(string $template, array $data, string $output): string
38    {
39        foreach ($data as $key => $value) {
40            if (is_array($value) || ($value instanceof \ArrayAccess)) {
41                $start = '[{' . $key . '}]';
42                $end   = '[{/' . $key . '}]';
43                if ((str_contains($template, $start)) && (str_contains($template, $end))) {
44                    $loopCode = substr($template, strpos($template, $start));
45                    $loopCode = substr($loopCode, 0, (strpos($loopCode, $end) + strlen($end)));
46
47                    $outputLoop = '';
48                    $i = 0;
49                    foreach ($value as $ky => $val) {
50                        $loop = str_replace($start, '', $loopCode);
51                        $loop = str_replace($end, '', $loop);
52                        if (str_contains($loop, '[{if(')) {
53                            $matches = [];
54                            preg_match_all('/\[{if/mi', $loop, $matches, PREG_OFFSET_CAPTURE);
55
56                            if (isset($matches[0]) && isset($matches[0][0])) {
57                                foreach ($matches[0] as $match) {
58                                    $cond = substr($loop, $match[1]);
59                                    $cond = substr($cond, 0, strpos($cond, '[{/if}]') + 7);
60                                    $var  = substr($cond, strpos($cond, '(') + 1);
61                                    $var  = substr($var, 0, strpos($var, ')'));
62                                    // If var is an array
63                                    if (str_contains($var, '[')) {
64                                        $index  = substr($var, (strpos($var, '[') + 1));
65                                        $index  = substr($index, 0, strpos($index, ']'));
66                                        $var    = substr($var, 0, strpos($var, '['));
67                                        $varSet = (!empty($val[$var][$index]));
68                                    } else {
69                                        $index  = null;
70                                        $varSet = (!empty($val[$var]));
71                                    }
72                                    if (str_contains($cond, '[{else}]')) {
73                                        if ($varSet) {
74                                            $code = substr($cond, (strpos($cond, ')}]') + 3));
75                                            $code = substr($code, 0, strpos($code, '[{else}]'));
76                                            $code = ($index !== null) ?
77                                                str_replace('[{' . $var . '[' . $index . ']}]', $val[$var][$index], $code) :
78                                                str_replace('[{' . $var . '}]', $val[$var], $code);
79                                            $loop = str_replace($cond, $code, $loop);
80                                        } else {
81                                            $code = substr($cond, (strpos($cond, '[{else}]') + 8));
82                                            $code = substr($code, 0, strpos($code, '[{/if}]'));
83                                            $loop = str_replace($cond, $code, $loop);
84                                        }
85                                    } else {
86                                        if ($varSet) {
87                                            $code = substr($cond, (strpos($cond, ')}]') + 3));
88                                            $code = substr($code, 0, strpos($code, '[{/if}]'));
89                                            $code = ($index !== null) ?
90                                                str_replace('[{' . $var . '[' . $index . ']}]', $val[$var][$index], $code) :
91                                                str_replace('[{' . $var . '}]', $val[$var], $code);
92                                            $loop = str_replace($cond, $code, $loop);
93                                        } else {
94                                            $loop = str_replace($cond, '', $loop);
95                                        }
96                                    }
97                                }
98                            }
99                        }
100
101                        // Handle nested array
102                        if (is_array($val) || ($value instanceof \ArrayAccess) || ($val instanceof \ArrayObject)) {
103                            if (is_numeric($ky)) {
104                                $oLoop = $loop;
105                                foreach ($val as $k => $v) {
106                                    // Check is value is stringable
107                                    if ((is_object($v) && method_exists($v, '__toString')) ||
108                                        (!is_object($v) && !is_array($v))) {
109                                        $oLoop = str_replace('[{' . $k . '}]', $v, $oLoop);
110                                    }
111                                }
112                                if (str_contains($oLoop, '[{i}]')) {
113                                    $oLoop = str_replace('[{i}]', ($i + 1), $oLoop);
114                                }
115                                $outputLoop .= $oLoop;
116                            } else {
117                                $s = '[{' . $ky . '}]';
118                                $e = '[{/' . $ky . '}]';
119                                if ((str_contains($loop, $s)) && (str_contains($loop, $e))) {
120                                    $l     = $loop;
121                                    $lCode = substr($l, strpos($l, $s));
122                                    $lCode = substr($lCode, 0, (strpos($lCode, $e) + strlen($e)));
123
124                                    $l     = str_replace($s, '', $lCode);
125                                    $l     = str_replace($e, '', $l);
126                                    $oLoop = '';
127                                    $j     = 1;
128
129                                    foreach ($val as $k => $v) {
130                                        // Check is value is stringable
131                                        if ((is_object($v) && method_exists($v, '__toString')) ||
132                                            (!is_object($v) && !is_array($v))) {
133                                            if (str_contains($l, '[{i}]')) {
134                                                $oLoop .= str_replace(['[{key}]', '[{value}]', '[{i}]'], [$k, $v, $j], $l);
135                                            } else {
136                                                $oLoop .= str_replace(['[{key}]', '[{value}]'], [$k, $v], $l);
137                                            }
138                                            $j++;
139                                        }
140                                    }
141                                    $outputLoop = str_replace($lCode, $oLoop, $loop);
142                                }
143                            }
144                        // Handle scalar
145                        } else {
146                            // Check is value is stringable
147                            if ((is_object($val) && method_exists($val, '__toString')) ||
148                                (!is_object($val) && !is_array($val))) {
149                                if (str_contains($loop, '[{i}]')) {
150                                    $outputLoop .= str_replace(['[{key}]', '[{value}]', '[{i}]'], [$ky, $val, ($i + 1)], $loop);
151                                } else {
152                                    $outputLoop .= str_replace(['[{key}]', '[{value}]'], [$ky, $val], $loop);
153                                }
154                            }
155                        }
156                        $i++;
157                        if ($i < count($value)) {
158                            $outputLoop .= PHP_EOL;
159                        }
160                    }
161                    $output = str_replace($loopCode, $outputLoop, $output);
162                }
163            }
164        }
165
166        return $output;
167    }
168
169    /**
170     * Parse conditionals in the template string
171     *
172     * @param  string $template
173     * @param  array  $data
174     * @param  string $output
175     * @return string
176     */
177    public static function parseConditionals(string $template, array $data, string $output): string
178    {
179        $matches = [];
180        preg_match_all('/\[{if/mi', $template, $matches, PREG_OFFSET_CAPTURE);
181        if (isset($matches[0]) && isset($matches[0][0])) {
182            foreach ($matches[0] as $match) {
183                $cond = substr($template, $match[1]);
184                $cond = substr($cond, 0, strpos($cond, '[{/if}]') + 7);
185                $var  = substr($cond, strpos($cond, '(') + 1);
186                $var  = substr($var, 0, strpos($var, ')'));
187
188                // If var is an array
189                if (str_contains($var, '[')) {
190                    $index  = substr($var, (strpos($var, '[') + 1));
191                    $index  = substr($index, 0, strpos($index, ']'));
192                    $var    = substr($var, 0, strpos($var, '['));
193                    $varSet = (!empty($data[$var][$index]));
194                } else {
195                    $index  = null;
196                    $varSet = (!empty($data[$var]));
197                }
198                if (str_contains($cond, '[{else}]')) {
199                    if ($varSet) {
200                        $code = substr($cond, (strpos($cond, ')}]') + 3));
201                        $code = substr($code, 0, strpos($code, '[{else}]'));
202                        $code = ($index !== null) ?
203                            str_replace('[{' . $var . '[' . $index . ']}]', $data[$var][$index], $code) :
204                            str_replace('[{' . $var . '}]', $data[$var], $code);
205                        $output = str_replace($cond, $code, $output);
206                    } else {
207                        $code = substr($cond, (strpos($cond, '[{else}]') + 8));
208                        $code = substr($code, 0, strpos($code, '[{/if}]'));
209                        $output = str_replace($cond, $code, $output);
210                    }
211                } else {
212                    if ($varSet) {
213                        $code = substr($cond, (strpos($cond, ')}]') + 3));
214                        $code = substr($code, 0, strpos($code, '[{/if}]'));
215                        $code = ($index !== null) ?
216                            str_replace('[{' . $var . '[' . $index . ']}]', $data[$var][$index], $code) :
217                            str_replace('[{' . $var . '}]', $data[$var], $code);
218                        $output = str_replace($cond, $code, $output);
219                    } else {
220                        $output = str_replace($cond, '', $output);
221                    }
222                }
223            }
224        }
225
226        return $output;
227    }
228
229    /**
230     * Parse scalar values in the template string
231     *
232     * @param  array  $data
233     * @param  string $output
234     * @return string
235     */
236    public static function parseScalars(array $data, string $output): string
237    {
238        foreach ($data as $key => $value) {
239            if (is_array($value) && (str_contains($output, '[{' . $key . '['))) {
240                $matches = [];
241                preg_match_all('/\[{' . $key .'\[/mi', $output, $matches, PREG_OFFSET_CAPTURE);
242
243                if (isset($matches[0]) && isset($matches[0][0])) {
244                    $indices = [];
245                    foreach ($matches[0] as $match) {
246                        $i = substr($output, $match[1] + (strlen($key) + 3));
247                        $i = substr($i, 0, strpos($i, ']'));
248                        $indices[] = $i;
249                    }
250                    foreach ($indices as $i) {
251                        if (isset($value[$i])) {
252                            $output = str_replace('[{' . $key . '[' . $i . ']}]', $value[$i], $output);
253                        } else {
254                            $output = str_replace('[{' . $key . '[' . $i . ']}]', '', $output);
255                        }
256                    }
257                }
258            } else if (!is_array($value) && !($value instanceof \ArrayAccess)) {
259                // Check is value is stringable
260                if ((is_object($value) && method_exists($value, '__toString')) || (!is_object($value) && !is_array($value))) {
261                    $output = str_replace('[{' . $key . '}]', $value, $output);
262                }
263            }
264        }
265
266        return $output;
267    }
268}