Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
40.87% covered (danger)
40.87%
85 / 208
41.38% covered (danger)
41.38%
12 / 29
CRAP
0.00% covered (danger)
0.00%
0 / 1
TwigExtension
40.87% covered (danger)
40.87%
85 / 208
41.38% covered (danger)
41.38%
12 / 29
1143.99
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getName
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFunctions
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
1
 isImageAllowed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getLanguageDescriptor
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 isNumeric
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNow
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getSystemStatus
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toTextFormat
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 formatDateTime
87.50% covered (warning)
87.50%
14 / 16
0.00% covered (danger)
0.00%
0 / 1
3.02
 currentRoute
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
 currentLang
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 currentLocale
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 currentVersion
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 urlGet
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 csvProperty
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 azPrefixList
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
30
 azPrefixListCollator
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 isValueInArray
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 remoteInclude
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 includeUrl
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 baseUrl
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getEsiFromPath
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 getClientHost
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 toSortableString
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 sortByName
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 sortFirstChar
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 dumpAppProfiler
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 kindOfPayment
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
1<?php
2
3/**
4 * @package   BO Slim
5 * @copyright BerlinOnline Stadtportal GmbH & Co. KG
6 **/
7
8namespace BO\Slim;
9
10use Twig\Extension\AbstractExtension;
11
12/**
13  * Extension for Twig and Slim
14  *
15  *  @SuppressWarnings(PublicMethod)
16  *  @SuppressWarnings(TooManyMethods)
17  *  @SuppressWarnings(Coupling)
18  *  @SuppressWarnings(Complexity)
19  */
20class TwigExtension extends AbstractExtension
21{
22    /**
23     * @var \BO\Slim\Container
24     */
25    private $container;
26
27    public function __construct($container)
28    {
29        $this->container = $container;
30    }
31
32    public function getName()
33    {
34        return 'boslimExtension';
35    }
36
37    public function getFunctions()
38    {
39        $safe = array('is_safe' => array('html'));
40        return array(
41            new \Twig\TwigFunction('urlGet', array($this, 'urlGet')),
42            new \Twig\TwigFunction('csvProperty', array($this, 'csvProperty')),
43            new \Twig\TwigFunction('azPrefixList', array($this, 'azPrefixList')),
44            new \Twig\TwigFunction('azPrefixListCollator', array($this, 'azPrefixListCollator')),
45            new \Twig\TwigFunction('isValueInArray', array($this, 'isValueInArray')),
46            new \Twig\TwigFunction('remoteInclude', array($this, 'remoteInclude'), $safe),
47            new \Twig\TwigFunction('includeUrl', array($this, 'includeUrl')),
48            new \Twig\TwigFunction('getEsiFromPath', array($this, 'getEsiFromPath')),
49            new \Twig\TwigFunction('baseUrl', array($this, 'baseUrl')),
50            new \Twig\TwigFunction('getLanguageDescriptor', array($this, 'getLanguageDescriptor')),
51            new \Twig\TwigFunction('currentLang', array($this, 'currentLang')),
52            new \Twig\TwigFunction('currentRoute', array($this, 'currentRoute')),
53            new \Twig\TwigFunction('currentLocale', array($this, 'currentLocale')),
54            new \Twig\TwigFunction('currentVersion', array($this, 'currentVersion')),
55            new \Twig\TwigFunction('formatDateTime', array($this, 'formatDateTime')),
56            new \Twig\TwigFunction('toTextFormat', array($this, 'toTextFormat')),
57            new \Twig\TwigFunction('getNow', array($this, 'getNow')),
58            new \Twig\TwigFunction('isNumeric', array($this, 'isNumeric')),
59            new \Twig\TwigFunction('dumpAppProfiler', array($this, 'dumpAppProfiler'), $safe),
60            new \Twig\TwigFunction('getSystemStatus', array($this, 'getSystemStatus'), $safe),
61            new \Twig\TwigFunction('getClientHost', array($this, 'getClientHost')),
62            new \Twig\TwigFunction('kindOfPayment', array($this, 'kindOfPayment')),
63            new \Twig\TwigFunction('isImageAllowed', array($this, 'isImageAllowed')),
64        );
65    }
66
67
68    public static function isImageAllowed()
69    {
70        return (isset(\App::$isImageAllowed)) ? \App::$isImageAllowed : true;
71    }
72
73    public function getLanguageDescriptor($locale = 'de')
74    {
75        $language = \App::$supportedLanguages[$locale] ?? [];
76        return $language['name'] ?? null;
77    }
78
79    public static function isNumeric($var)
80    {
81        return is_numeric($var);
82    }
83
84    public static function getNow()
85    {
86        if (\App::$now instanceof \DateTimeInterface) {
87            return \App::$now;
88        }
89        return new \DateTimeImmutable();
90    }
91
92    public static function getSystemStatus($env)
93    {
94        return getenv($env);
95    }
96
97    public function toTextFormat($string)
98    {
99        $string = \strip_tags($string, '<br />');
100        $temp = str_replace(array("<br />"), "\n", $string);
101        $lines = explode("\n", $temp);
102        $new_lines = array();
103        foreach ($lines as $line) {
104            if (!empty($line)) {
105                $new_lines[] = trim($line);
106            }
107        }
108        $result = implode("\n", $new_lines);
109        return addSlashes($result);
110    }
111
112    public function formatDateTime($dateString)
113    {
114        $dateTime = new \DateTimeImmutable(
115            $dateString->year . '-' . $dateString->month . '-' . $dateString->day,
116            new \DateTimezone('Europe/Berlin')
117        );
118        $formatDate['date']     = Helper::getFormatedDates($dateTime, "EE, dd. MMMM yyyy");
119        $formatDate['fulldate'] = Helper::getFormatedDates($dateTime, "EEEE, 'den' dd. MMMM yyyy");
120        $formatDate['weekday']  = (date('w', $dateTime->getTimestamp()) == 0) ?
121            date('w', $dateTime->getTimestamp()) + 6 :
122            date('w', $dateTime->getTimestamp()) - 1;
123        $formatDate['ym']       = $dateTime->format('Y-m');
124        $formatDate['ymd']       = $dateTime->format('Y-m-d');
125        $formatDate['ts']       = $dateTime->getTimestamp();
126        $formatDate['time']     = ($dateTime->format('H:i') != '00:00') ?
127            Helper::getFormatedDates($dateTime, 'HH:mm Uhr') :
128            false;
129        return $formatDate;
130    }
131
132    public function currentRoute($lang = null)
133    {
134        $route = array(
135            'name' => 'noroute',
136            'params' => []
137        );
138        if ($this->container->has('currentRoute')) {
139            $routeParams = $this->container->get('currentRouteParams');
140            if (null !== $lang && 'de' == $lang) {
141                unset($routeParams['lang']);
142            } elseif (\App::MULTILANGUAGE) {
143                $routeParams['lang'] = ($lang !== null) ? $lang : \App::$language->getCurrentLanguage();
144            }
145
146            $routeName = $this->container->get('currentRoute');
147            $route = array(
148                'name' => $routeName,
149                'params' => $routeParams
150            );
151        }
152        return $route;
153    }
154
155    public function currentLang()
156    {
157        return (\App::MULTILANGUAGE) ? \App::$language->getCurrentLanguage() : 'de';
158    }
159
160    public function currentLocale()
161    {
162        $locale = 'de_DE';
163        if (\App::MULTILANGUAGE) {
164            $locale = explode('.', \App::$language->getCurrentLocale());
165            $locale = reset($locale);
166        }
167        return $locale;
168    }
169
170    public function currentVersion()
171    {
172        $version = Version::getString();
173        return ($version != Version::UNKNOWN) ? $version : Git::readCurrentVersion();
174    }
175
176    public function urlGet($routeName, $params = array(), $getparams = array())
177    {
178        $url = \App::$slim->urlFor($routeName, $params);
179        $url = preg_replace('#^.*?(https?://)#', '\1', $url); // allow http:// routes
180        if ($getparams) {
181            $url .= '?' . http_build_query($getparams);
182        }
183        return Helper::proxySanitizeUri($url);
184    }
185
186    public function csvProperty($list, $property)
187    {
188        $propertylist = array();
189        foreach ($list as $item) {
190            if (!is_scalar($item) && array_key_exists($property, $item)) {
191                $propertylist[] = $item[$property];
192            }
193        }
194        return implode(',', array_unique($propertylist));
195    }
196
197    public function azPrefixList($list, $property)
198    {
199        $azList = array();
200        foreach ($list as $item) {
201            if (!is_scalar($item) && array_key_exists($property, $item)) {
202                $currentPrefix = self::sortFirstChar($item[$property]);
203                if (!array_key_exists($currentPrefix, $azList)) {
204                    $azList[$currentPrefix] = array(
205                        'prefix' => $currentPrefix,
206                        'sublist' => array(),
207                    );
208                }
209                $azList[$currentPrefix]['sublist'][] = $item;
210                uasort($azList[$currentPrefix]['sublist'], array($this,'sortByName'));
211                ksort($azList);
212            }
213        }
214        return $azList;
215    }
216
217    public function azPrefixListCollator($list, $property, $locale)
218    {
219        $collator = collator_create($locale);
220        $collator->setAttribute(\Collator::QUATERNARY, \Collator::ON);
221        $collator->setAttribute(\Collator::CASE_FIRST, \Collator::ON);
222        $collator->setAttribute(\Collator::NUMERIC_COLLATION, \Collator::ON);
223
224        if (is_array($list)) {
225            uasort($list, function ($itemA, $itemB) use ($collator, $property) {
226                return collator_compare($collator, $itemA[$property], $itemB[$property]);
227            });
228        } else {
229            $list = $list->sortWithCollator($property, $locale);
230        }
231
232        $azList = array();
233
234        foreach ($list as $item) {
235            $currentPrefix = self::sortFirstChar($item[$property]);
236            if (!array_key_exists($currentPrefix, $azList)) {
237                $azList[$currentPrefix] = array(
238                    'prefix' => $currentPrefix,
239                    'sublist' => array(),
240                );
241            }
242            $azList[$currentPrefix]['sublist'][] = $item;
243        }
244        return $azList;
245    }
246
247    public function isValueInArray($value, $params)
248    {
249        $paramsArr = explode(',', $params);
250        if (in_array($value, $paramsArr)) {
251            return true;
252        }
253        return false;
254    }
255
256    public static function remoteInclude($uri)
257    {
258        $prepend = '';
259        $append = '';
260        if (\App::DEBUG) {
261            $prepend = "<!-- include($uri) -->\n";
262            $append = "\n<!-- /include($uri) -->";
263        }
264        if (\App::ESI_ENABLED) {
265            // Varnish does not support https
266            $uri = preg_replace('#^(https?:)?//#', 'http://', $uri);
267            if (\App::DEBUG) {
268                $prepend = "<!-- replaced uri=$uri --> " . $prepend;
269            }
270            return $prepend . '<esi:include src="' . $uri . '" />' . $append;
271        } else {
272            $useragent = 'Client-' . (defined("\App::IDENTIFIER") ? constant("\App::IDENTIFIER") : 'ZMS');
273            $options = array(
274                'http' => array(
275                  'method' => "GET",
276                  'header' => "Accept-language: de\r\n" .
277                            "Cookie: zms=development\r\n" .
278                            "user-agent: $useragent \r\n"
279                )
280              );
281            $context = stream_context_create($options);
282            return $prepend . file_get_contents($uri, false, $context) . $append;
283        }
284    }
285
286    public function includeUrl($withUri = true)
287    {
288        if (null === \App::$includeUrl) {
289            /** @var Request $request */
290            $request = $this->container['request'];
291            $uri = (string)$request->getBasePath();
292            if ($withUri) {
293                $uri = $request->getBaseUrl();
294                $uri = preg_replace('#^https?://[^/]+#', '', $uri); //Do not force protocoll or host
295            }
296            return Helper::proxySanitizeUri($uri);
297        } else {
298            return \App::$includeUrl;
299        }
300    }
301
302    public function baseUrl()
303    {
304        return $this->includeUrl(false);
305    }
306
307    public function getEsiFromPath($path, $locale = false)
308    {
309        $localePath = ($locale && 'de' != $locale) ? '/' . $locale : '';
310        return \App::$esiBaseUrl . $localePath . \App::$$path;
311    }
312
313    public function getClientHost()
314    {
315        $request = $this->container['request'];
316        $headerList = ['host', 'x-forwarded-host'];
317        foreach ($headerList as $headername) {
318            if ($request->hasHeader($headername)) {
319                $hostname = $request->getHeaderLine($headername);
320            }
321        }
322        return $hostname;
323    }
324
325    protected static function toSortableString($string)
326    {
327        $string = strtr($string, array(
328            'Ä' => 'Ae',
329            'Ö' => 'Oe',
330            'Ü' => 'Ue',
331            'ä' => 'ae',
332            'ö' => 'oe',
333            'ü' => 'ue',
334            'ß' => 'ss',
335            '€' => 'E',
336        ));
337        return $string;
338    }
339
340    protected static function sortByName($left, $right)
341    {
342        return strcmp(
343            self::toSortableString(strtolower($left['name'])),
344            strtolower(self::toSortableString($right['name']))
345        );
346    }
347
348    protected static function sortFirstChar($string)
349    {
350        $firstChar = mb_substr($string, 0, 1);
351        $firstChar = mb_strtoupper($firstChar);
352        $firstChar = strtr($firstChar, array('Ä' => 'A', 'Ö' => 'O', 'Ü' => 'U'));
353        return $firstChar;
354    }
355
356    public function dumpAppProfiler()
357    {
358        $output = '<h2>App Profiles</h2>'
359            . ' <p>For debugging: This log contains runtime information.
360            <strong>DISABLE FOR PRODUCTION!</strong></p><ul>';
361        foreach (Profiler::$profileList as $entry) {
362            if ($entry instanceof Profiler) {
363                $output .= "<li>$entry</li>";
364            } else {
365                $output .= \Tracy\Debugger::dump($entry, true);
366            }
367        }
368        return $output . '</ul>';
369    }
370
371    public function kindOfPayment($code)
372    {
373        $result = '';
374        if ($code == 0) {
375            $result = 'eccash';
376        } elseif ($code == 1) {
377            $result = 'nocash';
378        } elseif ($code == 2) {
379            $result = 'ec';
380        } elseif ($code == 3) {
381            $result = 'cash';
382        } elseif ($code == 4) {
383            $result = 'subscribecash';
384        }
385        return $result;
386    }
387}