This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Picos Twig extension to implement additional filters |
||
5 | * |
||
6 | * @author Daniel Rudolf |
||
7 | * @link http://picocms.org |
||
8 | * @license http://opensource.org/licenses/MIT |
||
9 | * @version 1.0 |
||
10 | */ |
||
11 | class PicoTwigExtension extends Twig_Extension |
||
12 | { |
||
13 | /** |
||
14 | * Current instance of Pico |
||
15 | * |
||
16 | * @see PicoTwigExtension::getPico() |
||
17 | * @var Pico |
||
18 | */ |
||
19 | private $pico; |
||
20 | |||
21 | /** |
||
22 | * Constructs a new instance of this Twig extension |
||
23 | * |
||
24 | * @param Pico $pico current instance of Pico |
||
25 | */ |
||
26 | public function __construct(Pico $pico) |
||
27 | { |
||
28 | $this->pico = $pico; |
||
29 | } |
||
30 | |||
31 | /** |
||
32 | * Returns the extensions instance of Pico |
||
33 | * |
||
34 | * @see Pico |
||
35 | * @return Pico the extensions instance of Pico |
||
36 | */ |
||
37 | public function getPico() |
||
38 | { |
||
39 | return $this->pico; |
||
40 | } |
||
41 | |||
42 | /** |
||
43 | * Returns the name of the extension |
||
44 | * |
||
45 | * @see Twig_ExtensionInterface::getName() |
||
46 | * @return string the extension name |
||
47 | */ |
||
48 | public function getName() |
||
49 | { |
||
50 | return 'PicoTwigExtension'; |
||
51 | } |
||
52 | |||
53 | /** |
||
54 | * Returns the Twig filters markdown, map and sort_by |
||
55 | * |
||
56 | * @see Twig_ExtensionInterface::getFilters() |
||
57 | * @return Twig_SimpleFilter[] array of Picos Twig filters |
||
58 | */ |
||
59 | public function getFilters() |
||
60 | { |
||
61 | return array( |
||
62 | 'markdown' => new Twig_SimpleFilter('markdown', array($this, 'markdownFilter')), |
||
63 | 'map' => new Twig_SimpleFilter('map', array($this, 'mapFilter')), |
||
64 | 'sort_by' => new Twig_SimpleFilter('sort_by', array($this, 'sortByFilter')), |
||
65 | ); |
||
66 | } |
||
67 | |||
68 | /** |
||
69 | * Parses a markdown string to HTML |
||
70 | * |
||
71 | * This method is registered as the Twig `markdown` filter. You can use it |
||
72 | * to e.g. parse a meta variable (`{{ meta.description|markdown }}`). |
||
73 | * Don't use it to parse the contents of a page, use the `content` filter |
||
74 | * instead, what ensures the proper preparation of the contents. |
||
75 | * |
||
76 | * @param string $markdown markdown to parse |
||
77 | * @return string parsed HTML |
||
78 | */ |
||
79 | public function markdownFilter($markdown) |
||
80 | { |
||
81 | if ($this->getPico()->getParsedown() === null) { |
||
82 | throw new LogicException( |
||
83 | 'Unable to apply Twig "markdown" filter: ' |
||
84 | . 'Parsedown instance wasn\'t registered yet' |
||
85 | ); |
||
86 | } |
||
87 | |||
88 | return $this->getPico()->getParsedown()->text($markdown); |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Returns a array with the values of the given key or key path |
||
93 | * |
||
94 | * This method is registered as the Twig `map` filter. You can use this |
||
95 | * filter to e.g. get all page titles (`{{ pages|map("title") }}`). |
||
96 | * |
||
97 | * @param array|Traversable $var variable to map |
||
98 | * @param mixed $mapKeyPath key to map; either a scalar or a |
||
99 | * array interpreted as key path (i.e. ['foo', 'bar'] will return all |
||
100 | * $item['foo']['bar'] values) |
||
101 | * @return array mapped values |
||
102 | */ |
||
103 | public function mapFilter($var, $mapKeyPath) |
||
104 | { |
||
105 | if (!is_array($var) && (!is_object($var) || !is_a($var, 'Traversable'))) { |
||
106 | throw new Twig_Error_Runtime(sprintf( |
||
107 | 'The map filter only works with arrays or "Traversable", got "%s"', |
||
108 | is_object($var) ? get_class($var) : gettype($var) |
||
109 | )); |
||
110 | } |
||
111 | |||
112 | $result = array(); |
||
113 | foreach ($var as $key => $value) { |
||
114 | $mapValue = $this->getKeyOfVar($value, $mapKeyPath); |
||
115 | $result[$key] = ($mapValue !== null) ? $mapValue : $value; |
||
116 | } |
||
117 | return $result; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * Sorts an array by one of its keys or a arbitrary deep sub-key |
||
122 | * |
||
123 | * This method is registered as the Twig `sort_by` filter. You can use this |
||
124 | * filter to e.g. sort the pages array by a arbitrary meta value. Calling |
||
125 | * `{{ pages|sort_by("meta:nav"|split(":")) }}` returns all pages sorted by |
||
126 | * the meta value `nav`. Please note the `"meta:nav"|split(":")` part of |
||
127 | * the example. The sorting algorithm will never assume equality of two |
||
128 | * values, it will then fall back to the original order. The result is |
||
129 | * always sorted in ascending order, apply Twigs `reverse` filter to |
||
130 | * achieve a descending order. |
||
131 | * |
||
132 | * @param array|Traversable $var variable to sort |
||
133 | * @param mixed $sortKeyPath key to use for sorting; either |
||
134 | * a scalar or a array interpreted as key path (i.e. ['foo', 'bar'] |
||
135 | * will sort $var by $item['foo']['bar']) |
||
136 | * @param string $fallback specify what to do with items |
||
137 | * which don't contain the specified sort key; use "bottom" (default) |
||
138 | * to move those items to the end of the sorted array, "top" to rank |
||
139 | * them first, or "keep" to keep the original order of those items |
||
140 | * @return array sorted array |
||
141 | */ |
||
142 | public function sortByFilter($var, $sortKeyPath, $fallback = 'bottom') |
||
143 | { |
||
144 | if (is_object($var) && is_a($var, 'Traversable')) { |
||
145 | $var = iterator_to_array($var, true); |
||
146 | } elseif (!is_array($var)) { |
||
147 | throw new Twig_Error_Runtime(sprintf( |
||
148 | 'The sort_by filter only works with arrays or "Traversable", got "%s"', |
||
149 | is_object($var) ? get_class($var) : gettype($var) |
||
150 | )); |
||
151 | } |
||
152 | if (($fallback !== 'top') && ($fallback !== 'bottom') && ($fallback !== 'keep')) { |
||
153 | throw new Twig_Error_Runtime('The sort_by filter only supports the "top", "bottom" and "keep" fallbacks'); |
||
154 | } |
||
155 | |||
156 | $twigExtension = $this; |
||
157 | $varKeys = array_keys($var); |
||
158 | uksort($var, function ($a, $b) use ($twigExtension, $var, $varKeys, $sortKeyPath, $fallback, &$removeItems) { |
||
159 | $aSortValue = $twigExtension->getKeyOfVar($var[$a], $sortKeyPath); |
||
160 | $aSortValueNull = ($aSortValue === null); |
||
161 | |||
162 | $bSortValue = $twigExtension->getKeyOfVar($var[$b], $sortKeyPath); |
||
163 | $bSortValueNull = ($bSortValue === null); |
||
164 | |||
165 | if ($aSortValueNull xor $bSortValueNull) { |
||
166 | if ($fallback === 'top') { |
||
167 | return ($aSortValueNull - $bSortValueNull) * -1; |
||
168 | } elseif ($fallback === 'bottom') { |
||
169 | return ($aSortValueNull - $bSortValueNull); |
||
170 | } |
||
171 | } elseif (!$aSortValueNull && !$bSortValueNull) { |
||
172 | if ($aSortValue != $bSortValue) { |
||
173 | return ($aSortValue > $bSortValue) ? 1 : -1; |
||
174 | } |
||
175 | } |
||
176 | |||
177 | // never assume equality; fallback to original order |
||
178 | $aIndex = array_search($a, $varKeys); |
||
179 | $bIndex = array_search($b, $varKeys); |
||
180 | return ($aIndex > $bIndex) ? 1 : -1; |
||
181 | }); |
||
182 | |||
183 | return $var; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Returns the value of a variable item specified by a scalar key or a |
||
188 | * arbitrary deep sub-key using a key path |
||
189 | * |
||
190 | * @param array|Traversable|ArrayAccess|object $var base variable |
||
191 | * @param mixed $keyPath scalar key or a |
||
192 | * array interpreted as key path (when passing e.g. ['foo', 'bar'], |
||
193 | * the method will return $var['foo']['bar']) specifying the value |
||
194 | * @return mixed the requested |
||
195 | * value or NULL when the given key or key path didn't match |
||
196 | */ |
||
197 | public static function getKeyOfVar($var, $keyPath) |
||
198 | { |
||
199 | if (empty($keyPath)) { |
||
200 | return null; |
||
201 | } elseif (!is_array($keyPath)) { |
||
202 | $keyPath = array($keyPath); |
||
203 | } |
||
204 | |||
205 | foreach ($keyPath as $key) { |
||
206 | if (is_object($var)) { |
||
207 | if (is_a($var, 'ArrayAccess')) { |
||
0 ignored issues
–
show
|
|||
208 | // use ArrayAccess, see below |
||
209 | } elseif (is_a($var, 'Traversable')) { |
||
210 | $var = iterator_to_array($var); |
||
211 | } elseif (isset($var->{$key})) { |
||
212 | $var = $var->{$key}; |
||
213 | continue; |
||
214 | } elseif (is_callable(array($var, 'get' . ucfirst($key)))) { |
||
215 | try { |
||
216 | $var = call_user_func(array($var, 'get' . ucfirst($key))); |
||
217 | continue; |
||
218 | } catch (BadMethodCallException $e) { |
||
219 | return null; |
||
220 | } |
||
221 | } else { |
||
222 | return null; |
||
223 | } |
||
224 | } elseif (!is_array($var)) { |
||
225 | return null; |
||
226 | } |
||
227 | |||
228 | if (isset($var[$key])) { |
||
229 | $var = $var[$key]; |
||
230 | continue; |
||
231 | } |
||
232 | |||
233 | return null; |
||
234 | } |
||
235 | |||
236 | return $var; |
||
237 | } |
||
238 | } |
||
239 |
This check looks for the bodies of
if
statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.These
if
bodies can be removed. If you have an empty if but statements in theelse
branch, consider inverting the condition.could be turned into
This is much more concise to read.