1 <?php
2
3 /**
4 * @package redaxo\core
5 */
6 class rex_fragment
7 {
8 /**
9 * filename of the actual fragmentfile.
10 *
11 * @var string
12 */
13 private $filename;
14
15 /**
16 * key-value pair which represents all variables defined inside the fragment.
17 *
18 * @var array
19 */
20 private $vars;
21
22 /**
23 * another fragment which can optionaly be used to decorate the current fragment.
24 *
25 * @var rex_fragment
26 */
27 private $decorator;
28
29 /**
30 * Creates a fragment with the given variables.
31 *
32 * @param array $vars A array of key-value pairs to pass as local parameters
33 */
34 public function __construct(array $vars = [])
35 {
36 $this->vars = $vars;
37 }
38
39 /**
40 * Returns the value of the given variable $name.
41 *
42 * @param string $name Variable name
43 * @param string $default Default value
44 *
45 * @return mixed
46 */
47 public function getVar($name, $default = null)
48 {
49 if (isset($this->vars[$name])) {
50 return $this->vars[$name];
51 }
52
53 return $default;
54 }
55
56 /**
57 * Set the variable $name to the given value.
58 *
59 * @param string $name The name of the variable
60 * @param mixed $value The value for the variable
61 * @param bool $escape Flag which indicates if the value should be escaped or not
62 *
63 * @throws InvalidArgumentException
64 *
65 * @return $this
66 */
67 public function setVar($name, $value, $escape = true)
68 {
69 if (null === $name) {
70 throw new InvalidArgumentException(sprintf('Expecting $name to be not null!'));
71 }
72
73 if ($escape) {
74 $this->vars[$name] = $this->escape($value);
75 } else {
76 $this->vars[$name] = $value;
77 }
78
79 return $this;
80 }
81
82 /**
83 * Parses the variables of the fragment into the file $filename.
84 *
85 * @param string $filename the filename of the fragment to parse
86 *
87 * @throws InvalidArgumentException
88 * @throws rex_exception
89 *
90 * @return string
91 */
92 public function parse($filename)
93 {
94 if (!is_string($filename)) {
95 throw new InvalidArgumentException(sprintf('Expecting $filename to be a string, %s given!', gettype($filename)));
96 }
97
98 $this->filename = $filename;
99
100 foreach (self::$fragmentDirs as $fragDir) {
101 $fragment = $fragDir . $filename;
102 if (is_readable($fragment)) {
103 ob_start();
104 require $fragment;
105 $content = ob_get_clean();
106
107 if ($this->decorator) {
108 $this->decorator->setVar('rexDecoratedContent', $content, false);
109 $content = $this->decorator->parse($this->decorator->filename);
110 }
111
112 return $content;
113 }
114 }
115
116 throw new rex_exception(sprintf('Fragmentfile "%s" not found!', $filename));
117 }
118
119 /**
120 * Decorate the current fragment, with another fragment.
121 * The decorated fragment receives the parameters which are passed to this method.
122 *
123 * @param string $filename The filename of the fragment used for decoration
124 * @param array $params A array of key-value pairs to pass as parameters
125 *
126 * @return $this
127 */
128 public function decorate($filename, array $params)
129 {
130 $this->decorator = new self($params);
131 $this->decorator->filename = $filename;
132
133 return $this;
134 }
135
136 // -------------------------- in-fragment helpers
137
138 /**
139 * Escapes the value $val for proper use in the gui.
140 *
141 * @param mixed $value The value to escape
142 * @param string $strategy One of "html", "html_attr", "css", "js", "url"
143 *
144 * @throws InvalidArgumentException
145 *
146 * @return mixed
147 */
148 protected function escape($value, $strategy = 'html')
149 {
150 return rex_escape($value, $strategy);
151 }
152
153 /**
154 * Include a Subfragment from within a fragment.
155 *
156 * The Subfragment gets all variables of the current fragment, plus optional overrides from $params
157 *
158 * @param string $filename The filename of the fragment to use
159 * @param array $params A array of key-value pairs to pass as local parameters
160 */
161 protected function subfragment($filename, array $params = [])
162 {
163 $fragment = new self(array_merge($this->vars, $params));
164 echo $fragment->parse($filename);
165 }
166
167 /**
168 * Translate the given key $key.
169 *
170 * @param string $key The key to translate
171 *
172 * @throws InvalidArgumentException
173 *
174 * @return string
175 */
176 protected function i18n($key)
177 {
178 if (!is_string($key)) {
179 throw new InvalidArgumentException(sprintf('Expecting $key to be a string, %s given!', gettype($key)));
180 }
181
182 // use the magic call only when more than one parameter is passed along,
183 // to get best performance
184 $argNum = func_num_args();
185 if ($argNum > 1) {
186 // pass along all given parameters
187 $args = func_get_args();
188 return call_user_func_array(['rex_i18n', 'msg'], $args);
189 }
190
191 return rex_i18n::msg($key);
192 }
193
194 /**
195 * Magic getter to reference variables from within the fragment.
196 *
197 * @param string $name The name of the variable to get
198 *
199 * @return mixed
200 */
201 public function __get($name)
202 {
203 if (isset($this->vars[$name]) || array_key_exists($name, $this->vars)) {
204 return $this->vars[$name];
205 }
206
207 trigger_error(sprintf('Undefined variable "%s" in rex_fragment "%s"', $name, $this->filename), E_USER_WARNING);
208
209 return null;
210 }
211
212 /**
213 * Magic method to check if a variable is set.
214 *
215 * @param string $name The name of the variable to check
216 *
217 * @return bool
218 */
219 public function __isset($name)
220 {
221 return isset($this->vars[$name]) || array_key_exists($name, $this->vars);
222 }
223
224 // /-------------------------- in-fragment helpers
225
226 /**
227 * array which contains all folders in which fragments will be searched for at runtime.
228 *
229 * @var array
230 */
231 private static $fragmentDirs = [];
232
233 /**
234 * Add a path to the fragment search path.
235 *
236 * @param string $dir A path to a directory where fragments can be found
237 */
238 public static function addDirectory($dir)
239 {
240 // add the new directory in front of the already know dirs,
241 // so a later caller can override core settings/fragments
242 $dir = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
243 array_unshift(self::$fragmentDirs, $dir);
244 }
245 }
246