1 | <?php |
---|
2 | |
---|
3 | // misc help functions and utilities |
---|
4 | |
---|
5 | function rand_str($len=32) { |
---|
6 | return substr(md5(uniqid(rand())), 0, $len); |
---|
7 | } |
---|
8 | |
---|
9 | function rand_long_str( $length ) { |
---|
10 | $chars = 'abcdefghijklmnopqrstuvwxyz'; |
---|
11 | $string = ''; |
---|
12 | |
---|
13 | for ( $i = 0; $i < $length; $i++ ) { |
---|
14 | $rand = rand( 0, strlen( $chars ) - 1 ); |
---|
15 | $string .= substr( $chars, $rand, 1 ); |
---|
16 | } |
---|
17 | |
---|
18 | return $string; |
---|
19 | } |
---|
20 | |
---|
21 | // strip leading and trailing whitespace from each line in the string |
---|
22 | function strip_ws($txt) { |
---|
23 | $lines = explode("\n", $txt); |
---|
24 | $result = array(); |
---|
25 | foreach ($lines as $line) |
---|
26 | if (trim($line)) |
---|
27 | $result[] = trim($line); |
---|
28 | |
---|
29 | return trim(join("\n", $result)); |
---|
30 | } |
---|
31 | |
---|
32 | // helper class for testing code that involves actions and filters |
---|
33 | // typical use: |
---|
34 | // $ma = new MockAction(); |
---|
35 | // add_action('foo', array(&$ma, 'action')); |
---|
36 | class MockAction { |
---|
37 | var $events; |
---|
38 | var $debug; |
---|
39 | |
---|
40 | /** |
---|
41 | * PHP5 constructor. |
---|
42 | */ |
---|
43 | function __construct( $debug = 0 ) { |
---|
44 | $this->reset(); |
---|
45 | $this->debug = $debug; |
---|
46 | } |
---|
47 | |
---|
48 | function reset() { |
---|
49 | $this->events = array(); |
---|
50 | } |
---|
51 | |
---|
52 | function current_filter() { |
---|
53 | if (is_callable('current_filter')) |
---|
54 | return current_filter(); |
---|
55 | global $wp_actions; |
---|
56 | return end($wp_actions); |
---|
57 | } |
---|
58 | |
---|
59 | function action($arg) { |
---|
60 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
61 | $args = func_get_args(); |
---|
62 | $this->events[] = array('action' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args); |
---|
63 | return $arg; |
---|
64 | } |
---|
65 | |
---|
66 | function action2($arg) { |
---|
67 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
68 | |
---|
69 | $args = func_get_args(); |
---|
70 | $this->events[] = array('action' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args); |
---|
71 | return $arg; |
---|
72 | } |
---|
73 | |
---|
74 | function filter($arg) { |
---|
75 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
76 | |
---|
77 | $args = func_get_args(); |
---|
78 | $this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args); |
---|
79 | return $arg; |
---|
80 | } |
---|
81 | |
---|
82 | function filter2($arg) { |
---|
83 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
84 | |
---|
85 | $args = func_get_args(); |
---|
86 | $this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args); |
---|
87 | return $arg; |
---|
88 | } |
---|
89 | |
---|
90 | function filter_append($arg) { |
---|
91 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
92 | |
---|
93 | $args = func_get_args(); |
---|
94 | $this->events[] = array('filter' => __FUNCTION__, 'tag'=>$this->current_filter(), 'args'=>$args); |
---|
95 | return $arg . '_append'; |
---|
96 | } |
---|
97 | |
---|
98 | function filterall($tag, $arg=NULL) { |
---|
99 | // this one doesn't return the result, so it's safe to use with the new 'all' filter |
---|
100 | if ($this->debug) dmp(__FUNCTION__, $this->current_filter()); |
---|
101 | |
---|
102 | $args = func_get_args(); |
---|
103 | $this->events[] = array('filter' => __FUNCTION__, 'tag'=>$tag, 'args'=>array_slice($args, 1)); |
---|
104 | } |
---|
105 | |
---|
106 | // return a list of all the actions, tags and args |
---|
107 | function get_events() { |
---|
108 | return $this->events; |
---|
109 | } |
---|
110 | |
---|
111 | // return a count of the number of times the action was called since the last reset |
---|
112 | function get_call_count($tag='') { |
---|
113 | if ($tag) { |
---|
114 | $count = 0; |
---|
115 | foreach ($this->events as $e) |
---|
116 | if ($e['action'] == $tag) |
---|
117 | ++$count; |
---|
118 | return $count; |
---|
119 | } |
---|
120 | return count($this->events); |
---|
121 | } |
---|
122 | |
---|
123 | // return an array of the tags that triggered calls to this action |
---|
124 | function get_tags() { |
---|
125 | $out = array(); |
---|
126 | foreach ($this->events as $e) { |
---|
127 | $out[] = $e['tag']; |
---|
128 | } |
---|
129 | return $out; |
---|
130 | } |
---|
131 | |
---|
132 | // return an array of args passed in calls to this action |
---|
133 | function get_args() { |
---|
134 | $out = array(); |
---|
135 | foreach ($this->events as $e) |
---|
136 | $out[] = $e['args']; |
---|
137 | return $out; |
---|
138 | } |
---|
139 | } |
---|
140 | |
---|
141 | // convert valid xml to an array tree structure |
---|
142 | // kinda lame but it works with a default php 4 install |
---|
143 | class testXMLParser { |
---|
144 | var $xml; |
---|
145 | var $data = array(); |
---|
146 | |
---|
147 | /** |
---|
148 | * PHP5 constructor. |
---|
149 | */ |
---|
150 | function __construct( $in ) { |
---|
151 | $this->xml = xml_parser_create(); |
---|
152 | xml_set_object($this->xml, $this); |
---|
153 | xml_parser_set_option($this->xml,XML_OPTION_CASE_FOLDING, 0); |
---|
154 | xml_set_element_handler($this->xml, array($this, 'startHandler'), array($this, 'endHandler')); |
---|
155 | xml_set_character_data_handler($this->xml, array($this, 'dataHandler')); |
---|
156 | $this->parse($in); |
---|
157 | } |
---|
158 | |
---|
159 | function parse($in) { |
---|
160 | $parse = xml_parse($this->xml, $in, sizeof($in)); |
---|
161 | if (!$parse) { |
---|
162 | trigger_error(sprintf("XML error: %s at line %d", |
---|
163 | xml_error_string(xml_get_error_code($this->xml)), |
---|
164 | xml_get_current_line_number($this->xml)), E_USER_ERROR); |
---|
165 | xml_parser_free($this->xml); |
---|
166 | } |
---|
167 | return true; |
---|
168 | } |
---|
169 | |
---|
170 | function startHandler($parser, $name, $attributes) { |
---|
171 | $data['name'] = $name; |
---|
172 | if ($attributes) { $data['attributes'] = $attributes; } |
---|
173 | $this->data[] = $data; |
---|
174 | } |
---|
175 | |
---|
176 | function dataHandler($parser, $data) { |
---|
177 | $index = count($this->data) - 1; |
---|
178 | @$this->data[$index]['content'] .= $data; |
---|
179 | } |
---|
180 | |
---|
181 | function endHandler($parser, $name) { |
---|
182 | if (count($this->data) > 1) { |
---|
183 | $data = array_pop($this->data); |
---|
184 | $index = count($this->data) - 1; |
---|
185 | $this->data[$index]['child'][] = $data; |
---|
186 | } |
---|
187 | } |
---|
188 | } |
---|
189 | |
---|
190 | function xml_to_array($in) { |
---|
191 | $p = new testXMLParser($in); |
---|
192 | return $p->data; |
---|
193 | } |
---|
194 | |
---|
195 | function xml_find($tree /*, $el1, $el2, $el3, .. */) { |
---|
196 | $a = func_get_args(); |
---|
197 | $a = array_slice($a, 1); |
---|
198 | $n = count($a); |
---|
199 | $out = array(); |
---|
200 | |
---|
201 | if ($n < 1) |
---|
202 | return $out; |
---|
203 | |
---|
204 | for ($i=0; $i<count($tree); $i++) { |
---|
205 | # echo "checking '{$tree[$i][name]}' == '{$a[0]}'\n"; |
---|
206 | # var_dump($tree[$i]['name'], $a[0]); |
---|
207 | if ($tree[$i]['name'] == $a[0]) { |
---|
208 | # echo "n == {$n}\n"; |
---|
209 | if ($n == 1) |
---|
210 | $out[] = $tree[$i]; |
---|
211 | else { |
---|
212 | $subtree =& $tree[$i]['child']; |
---|
213 | $call_args = array($subtree); |
---|
214 | $call_args = array_merge($call_args, array_slice($a, 1)); |
---|
215 | $out = array_merge($out, call_user_func_array('xml_find', $call_args)); |
---|
216 | } |
---|
217 | } |
---|
218 | } |
---|
219 | |
---|
220 | return $out; |
---|
221 | } |
---|
222 | |
---|
223 | function xml_join_atts($atts) { |
---|
224 | $a = array(); |
---|
225 | foreach ($atts as $k=>$v) |
---|
226 | $a[] = $k.'="'.$v.'"'; |
---|
227 | return join(' ', $a); |
---|
228 | } |
---|
229 | |
---|
230 | function xml_array_dumbdown(&$data) { |
---|
231 | $out = array(); |
---|
232 | |
---|
233 | foreach (array_keys($data) as $i) { |
---|
234 | $name = $data[$i]['name']; |
---|
235 | if (!empty($data[$i]['attributes'])) |
---|
236 | $name .= ' '.xml_join_atts($data[$i]['attributes']); |
---|
237 | |
---|
238 | if (!empty($data[$i]['child'])) { |
---|
239 | $out[$name][] = xml_array_dumbdown($data[$i]['child']); |
---|
240 | } |
---|
241 | else |
---|
242 | $out[$name] = $data[$i]['content']; |
---|
243 | } |
---|
244 | |
---|
245 | return $out; |
---|
246 | } |
---|
247 | |
---|
248 | function dmp() { |
---|
249 | $args = func_get_args(); |
---|
250 | |
---|
251 | foreach ($args as $thing) |
---|
252 | echo (is_scalar($thing) ? strval($thing) : var_export($thing, true)), "\n"; |
---|
253 | } |
---|
254 | |
---|
255 | function dmp_filter($a) { |
---|
256 | dmp($a); |
---|
257 | return $a; |
---|
258 | } |
---|
259 | |
---|
260 | function get_echo($callable, $args = array()) { |
---|
261 | ob_start(); |
---|
262 | call_user_func_array($callable, $args); |
---|
263 | return ob_get_clean(); |
---|
264 | } |
---|
265 | |
---|
266 | // recursively generate some quick assertEquals tests based on an array |
---|
267 | function gen_tests_array($name, $array) { |
---|
268 | $out = array(); |
---|
269 | foreach ($array as $k=>$v) { |
---|
270 | if (is_numeric($k)) |
---|
271 | $index = strval($k); |
---|
272 | else |
---|
273 | $index = "'".addcslashes($k, "\n\r\t'\\")."'"; |
---|
274 | |
---|
275 | if (is_string($v)) { |
---|
276 | $out[] = '$this->assertEquals( \'' . addcslashes($v, "\n\r\t'\\") . '\', $'.$name.'['.$index.'] );'; |
---|
277 | } |
---|
278 | elseif (is_numeric($v)) { |
---|
279 | $out[] = '$this->assertEquals( ' . $v . ', $'.$name.'['.$index.'] );'; |
---|
280 | } |
---|
281 | elseif (is_array($v)) { |
---|
282 | $out[] = gen_tests_array("{$name}[{$index}]", $v); |
---|
283 | } |
---|
284 | } |
---|
285 | return join("\n", $out)."\n"; |
---|
286 | } |
---|
287 | |
---|
288 | /** |
---|
289 | * Use to create objects by yourself |
---|
290 | */ |
---|
291 | class MockClass {}; |
---|
292 | |
---|
293 | /** |
---|
294 | * Drops all tables from the WordPress database |
---|
295 | */ |
---|
296 | function drop_tables() { |
---|
297 | global $wpdb; |
---|
298 | $tables = $wpdb->get_col('SHOW TABLES;'); |
---|
299 | foreach ($tables as $table) |
---|
300 | $wpdb->query("DROP TABLE IF EXISTS {$table}"); |
---|
301 | } |
---|
302 | |
---|
303 | function print_backtrace() { |
---|
304 | $bt = debug_backtrace(); |
---|
305 | echo "Backtrace:\n"; |
---|
306 | $i = 0; |
---|
307 | foreach ($bt as $stack) { |
---|
308 | echo ++$i, ": "; |
---|
309 | if ( isset($stack['class']) ) |
---|
310 | echo $stack['class'].'::'; |
---|
311 | if ( isset($stack['function']) ) |
---|
312 | echo $stack['function'].'() '; |
---|
313 | echo "line {$stack[line]} in {$stack[file]}\n"; |
---|
314 | } |
---|
315 | echo "\n"; |
---|
316 | } |
---|
317 | |
---|
318 | // mask out any input fields matching the given name |
---|
319 | function mask_input_value($in, $name='_wpnonce') { |
---|
320 | return preg_replace('@<input([^>]*) name="'.preg_quote($name).'"([^>]*) value="[^>]*" />@', '<input$1 name="'.preg_quote($name).'"$2 value="***" />', $in); |
---|
321 | } |
---|
322 | |
---|
323 | if ( !function_exists( 'str_getcsv' ) ) { |
---|
324 | function str_getcsv( $input, $delimiter = ',', $enclosure = '"', $escape = "\\" ) { |
---|
325 | $fp = fopen( 'php://temp/', 'r+' ); |
---|
326 | fputs( $fp, $input ); |
---|
327 | rewind( $fp ); |
---|
328 | $data = fgetcsv( $fp, strlen( $input ), $delimiter, $enclosure ); |
---|
329 | fclose( $fp ); |
---|
330 | return $data; |
---|
331 | } |
---|
332 | } |
---|
333 | |
---|
334 | /** |
---|
335 | * Removes the post type and its taxonomy associations. |
---|
336 | */ |
---|
337 | function _unregister_post_type( $cpt_name ) { |
---|
338 | unregister_post_type( $cpt_name ); |
---|
339 | } |
---|
340 | |
---|
341 | function _unregister_taxonomy( $taxonomy_name ) { |
---|
342 | unregister_taxonomy( $taxonomy_name ); |
---|
343 | } |
---|
344 | |
---|
345 | /** |
---|
346 | * Unregister a post status. |
---|
347 | * |
---|
348 | * @since 4.2.0 |
---|
349 | * |
---|
350 | * @param string $status |
---|
351 | */ |
---|
352 | function _unregister_post_status( $status ) { |
---|
353 | unset( $GLOBALS['wp_post_statuses'][ $status ] ); |
---|
354 | } |
---|
355 | |
---|
356 | function _cleanup_query_vars() { |
---|
357 | // clean out globals to stop them polluting wp and wp_query |
---|
358 | foreach ( $GLOBALS['wp']->public_query_vars as $v ) |
---|
359 | unset( $GLOBALS[$v] ); |
---|
360 | |
---|
361 | foreach ( $GLOBALS['wp']->private_query_vars as $v ) |
---|
362 | unset( $GLOBALS[$v] ); |
---|
363 | |
---|
364 | foreach ( get_taxonomies( array() , 'objects' ) as $t ) { |
---|
365 | if ( $t->publicly_queryable && ! empty( $t->query_var ) ) |
---|
366 | $GLOBALS['wp']->add_query_var( $t->query_var ); |
---|
367 | } |
---|
368 | |
---|
369 | foreach ( get_post_types( array() , 'objects' ) as $t ) { |
---|
370 | if ( is_post_type_viewable( $t ) && ! empty( $t->query_var ) ) |
---|
371 | $GLOBALS['wp']->add_query_var( $t->query_var ); |
---|
372 | } |
---|
373 | } |
---|
374 | |
---|
375 | function _clean_term_filters() { |
---|
376 | remove_filter( 'get_terms', array( 'Featured_Content', 'hide_featured_term' ), 10, 2 ); |
---|
377 | remove_filter( 'get_the_terms', array( 'Featured_Content', 'hide_the_featured_term' ), 10, 3 ); |
---|
378 | } |
---|
379 | |
---|
380 | /** |
---|
381 | * Special class for exposing protected wpdb methods we need to access |
---|
382 | */ |
---|
383 | class wpdb_exposed_methods_for_testing extends wpdb { |
---|
384 | public function __construct() { |
---|
385 | global $wpdb; |
---|
386 | $this->dbh = $wpdb->dbh; |
---|
387 | $this->use_mysqli = $wpdb->use_mysqli; |
---|
388 | $this->is_mysql = $wpdb->is_mysql; |
---|
389 | $this->ready = true; |
---|
390 | $this->field_types = $wpdb->field_types; |
---|
391 | $this->charset = $wpdb->charset; |
---|
392 | } |
---|
393 | |
---|
394 | public function __call( $name, $arguments ) { |
---|
395 | return call_user_func_array( array( $this, $name ), $arguments ); |
---|
396 | } |
---|
397 | } |
---|
398 | |
---|
399 | /** |
---|
400 | * Determine approximate backtrack count when running PCRE. |
---|
401 | * |
---|
402 | * @return int The backtrack count. |
---|
403 | */ |
---|
404 | function benchmark_pcre_backtracking( $pattern, $subject, $strategy ) { |
---|
405 | $saved_config = ini_get( 'pcre.backtrack_limit' ); |
---|
406 | |
---|
407 | // Attempt to prevent PHP crashes. Adjust these lower when needed. |
---|
408 | if ( version_compare( phpversion(), '5.4.8', '>' ) ) { |
---|
409 | $limit = 1000000; |
---|
410 | } else { |
---|
411 | $limit = 20000; // 20,000 is a reasonable upper limit, but see also https://core.trac.wordpress.org/ticket/29557#comment:10 |
---|
412 | } |
---|
413 | |
---|
414 | // Start with small numbers, so if a crash is encountered at higher numbers we can still debug the problem. |
---|
415 | for( $i = 4; $i <= $limit; $i *= 2 ) { |
---|
416 | |
---|
417 | ini_set( 'pcre.backtrack_limit', $i ); |
---|
418 | |
---|
419 | switch( $strategy ) { |
---|
420 | case 'split': |
---|
421 | preg_split( $pattern, $subject ); |
---|
422 | break; |
---|
423 | case 'match': |
---|
424 | preg_match( $pattern, $subject ); |
---|
425 | break; |
---|
426 | case 'match_all': |
---|
427 | $matches = array(); |
---|
428 | preg_match_all( $pattern, $subject, $matches ); |
---|
429 | break; |
---|
430 | } |
---|
431 | |
---|
432 | ini_set( 'pcre.backtrack_limit', $saved_config ); |
---|
433 | |
---|
434 | switch( preg_last_error() ) { |
---|
435 | case PREG_NO_ERROR: |
---|
436 | return $i; |
---|
437 | case PREG_BACKTRACK_LIMIT_ERROR: |
---|
438 | continue; |
---|
439 | case PREG_RECURSION_LIMIT_ERROR: |
---|
440 | trigger_error('PCRE recursion limit encountered before backtrack limit.'); |
---|
441 | return; |
---|
442 | case PREG_BAD_UTF8_ERROR: |
---|
443 | trigger_error('UTF-8 error during PCRE benchmark.'); |
---|
444 | return; |
---|
445 | case PREG_INTERNAL_ERROR: |
---|
446 | trigger_error('Internal error during PCRE benchmark.'); |
---|
447 | return; |
---|
448 | default: |
---|
449 | trigger_error('Unexpected error during PCRE benchmark.'); |
---|
450 | return; |
---|
451 | } |
---|
452 | } |
---|
453 | |
---|
454 | return $i; |
---|
455 | } |
---|