1<?php
2
3
4namespace ComboStrap;
5
6
7/**
8 * Class Template
9 * @package ComboStrap
10 * https://stackoverflow.com/questions/17869964/replacing-string-within-php-file
11 */
12class Template
13{
14
15    public const DOLLAR_VARIABLE_PREFIX = "$";
16
17    public const DOLLAR_ESCAPE = '\\';
18
19    public const CAPTURE_PATTERN_SHORT = Template::DOLLAR_ESCAPE . Template::DOLLAR_VARIABLE_PREFIX . self::VARIABLE_NAME_EXPRESSION;
20    public const CAPTURE_PATTERN_LONG = Template::DOLLAR_ESCAPE . Template::LONG_PREFIX . "[^}\r\n]+" . self::LONG_EXIT;
21
22
23    const CANONICAL = "variable-template";
24    public const LONG_PREFIX = self::DOLLAR_VARIABLE_PREFIX . self::LONG_ENTER;
25    public const LONG_EXIT = '}';
26    const LONG_ENTER = '{';
27    const VARIABLE_NAME_EXPRESSION = "[A-Za-z0-9_]+";
28    const LONG_VARIABLE_NAME_CAPTURE_EXPRESSION = self::DOLLAR_ESCAPE . self::LONG_PREFIX . '\s*(' . self::VARIABLE_NAME_EXPRESSION . ')[^\r\n]*' . self::LONG_EXIT;
29
30
31    protected string $_string;
32    protected array $_data = array();
33
34    public function __construct($string = null)
35    {
36        $this->_string = $string;
37    }
38
39    /**
40     * @param $string
41     * @return Template
42     */
43    public static function create($string): Template
44    {
45        return new Template($string);
46    }
47
48    public static function toValidVariableName(string $name)
49    {
50        return str_replace("-", "", $name);
51    }
52
53    public function setProperty($key, $value): Template
54    {
55        $this->_data[$key] = $value;
56        return $this;
57    }
58
59    public function render(): string
60    {
61
62        $pattern = '/' .
63            '(' . self::DOLLAR_ESCAPE . self::DOLLAR_VARIABLE_PREFIX . self::VARIABLE_NAME_EXPRESSION . ')' .
64            '|' .
65            '(' . self::DOLLAR_ESCAPE . self::LONG_PREFIX . '\s*' . self::VARIABLE_NAME_EXPRESSION . '[^\r\n]*' . self::LONG_EXIT . ')' .
66            '/im';
67        $splits = preg_split($pattern, $this->_string, -1, PREG_SPLIT_DELIM_CAPTURE);
68        $rendered = "";
69        foreach ($splits as $part) {
70            if (substr($part, 0, 1) === self::DOLLAR_VARIABLE_PREFIX) {
71                if (substr($part, 1, 1) === self::LONG_ENTER) {
72                    $matches = [];
73                    preg_match('/' . self::LONG_VARIABLE_NAME_CAPTURE_EXPRESSION . '/im', $part, $matches);
74                    $variable = $matches[1];
75                } else {
76                    $variable = trim(substr($part, 1));
77                }
78                if (array_key_exists($variable, $this->_data)) {
79                    $value = $this->_data[$variable];
80                } else {
81                    LogUtility::warning("The variable ($variable) was not found in the data and has not been replaced", self::CANONICAL);
82                    $value = $variable;
83                }
84            } else {
85                $value = $part;
86            }
87            $rendered .= $value;
88        }
89        return $rendered;
90
91    }
92
93    /**
94     *
95     * @return false|string
96     * @deprecated Just for demo, don't use because the input is not validated
97     *
98     */
99    public function renderViaEval()
100    {
101        extract($this->_data);
102        ob_start();
103        eval("echo $this->_string ;");
104        return ob_get_clean();
105    }
106
107    /**
108     * @return array - an array of variable name
109     */
110    public function getVariablesDetected(): array
111    {
112        /** @noinspection RegExpUnnecessaryNonCapturingGroup */
113        $pattern = '/' .
114            '(?:' . self::DOLLAR_ESCAPE . self::DOLLAR_VARIABLE_PREFIX . '(' . self::VARIABLE_NAME_EXPRESSION . '))' .
115            '|' .
116            '(?:' . self::LONG_VARIABLE_NAME_CAPTURE_EXPRESSION . ')' .
117            '/im';
118        $result = preg_match_all($pattern, $this->_string, $matches);
119        if ($result >= 1) {
120            $returnedMatch = [];
121            $firstExpressionMatches = $matches[1];
122            $secondExpressionMatches = $matches[2];
123            foreach ($firstExpressionMatches as $key => $match) {
124                if (empty($match)) {
125                    $returnedMatch[] = $secondExpressionMatches[$key];
126                    continue;
127                }
128                $returnedMatch[] = $match;
129            }
130            return $returnedMatch;
131        } else {
132            return [];
133        }
134
135    }
136
137    public function setProperties(array $properties): Template
138    {
139        foreach ($properties as $key => $val) {
140            $this->setProperty($key, $val);
141        }
142        return $this;
143
144    }
145
146}
147