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( |
|||||||||||
0 ignored issues
–
show
|
||||||||||||
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) { |
|||||||||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Using logical operators such as
xor instead of ^ is generally not recommended.
PHP has two types of connecting operators (logical operators, and boolean operators):
The difference between these is the order in which they are executed. In most cases,
you would want to use a boolean operator like Let’s take a look at a few examples: // Logical operators have lower precedence:
$f = false or true;
// is executed like this:
($f = false) or true;
// Boolean operators have higher precedence:
$f = false || true;
// is executed like this:
$f = (false || true);
Logical Operators are used for Control-FlowOne case where you explicitly want to use logical operators is for control-flow such as this: $x === 5
or die('$x must be 5.');
// Instead of
if ($x !== 5) {
die('$x must be 5.');
}
Since // The following is currently a parse error.
$x === 5
or throw new RuntimeException('$x must be 5.');
These limitations lead to logical operators rarely being of use in current PHP code. ![]() |
||||||||||||
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
This
if statement is empty and can be removed.
This check looks for the bodies of These if (rand(1, 6) > 3) {
//print "Check failed";
} else {
print "Check succeeded";
}
could be turned into if (rand(1, 6) <= 3) {
print "Check succeeded";
}
This is much more concise to read. ![]() |
||||||||||||
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 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.