Issues (2756)

includes/functions-plugins.php (2 issues)

1
<?php
2
3
/**
4
 * The filter/plugin API is located in this file, which allows for creating filters
5
 * and hooking functions, and methods. The functions or methods will be run when
6
 * the filter is called.
7
 *
8
 * Any of the syntaxes explained in the PHP documentation for the
9
 * {@link http://us2.php.net/manual/en/language.pseudo-types.php#language.types.callback 'callback'}
10
 * type are valid.
11
 *
12
 * This API is heavily inspired by the one I implemented in Zenphoto 1.3, which was heavily inspired by the one used in WordPress.
13
 *
14
 * @author Ozh
15
 * @since 1.5
16
 */
17
18
if ( !isset( $yourls_filters ) ) {
19
    $yourls_filters = [];
20
}
21
/*
22
 * This global var will collect filters with the following structure:
23
 * $yourls_filters['hook']['array of priorities']['serialized function names']['array of ['array (functions, accepted_args, filter or action)]']
24
 */
25
26
if ( !isset( $yourls_actions ) ) {
27
    $yourls_actions = [];
28
}
29
/*
30
 * This global var will collect 'done' actions with the following structure:
31
 * $yourls_actions['hook'] => number of time this action was done
32
 */
33
34
/**
35
 * Registers a filtering function
36
 *
37
 * Typical use:
38
 *        yourls_add_filter('some_hook', 'function_handler_for_hook');
39
 *
40
 * @param string   $hook           the name of the YOURLS element to be filtered or YOURLS action to be triggered
41
 * @param callback $function_name  the name of the function that is to be called.
42
 * @param int      $priority       optional. Used to specify the order in which the functions associated with a
43
 *                                 particular action are executed (default=10, lower=earlier execution, and functions
44
 *                                 with the same priority are executed in the order in which they were added to the
45
 *                                 filter)
46
 * @param int      $accepted_args  optional. The number of arguments the function accept (default is the number
47
 *                                 provided).
48
 * @param string   $type
49
 * @global array   $yourls_filters Storage for all of the filters
50
 */
51
function yourls_add_filter( $hook, $function_name, $priority = 10, $accepted_args = NULL, $type = 'filter' ) {
52 109
    global $yourls_filters;
53
    // At this point, we cannot check if the function exists, as it may well be defined later (which is OK)
54 109
    $id = yourls_filter_unique_id( $hook, $function_name, $priority );
55
56 109
    $yourls_filters[ $hook ][ $priority ][ $id ] = [
57 109
        'function'      => $function_name,
58 109
        'accepted_args' => $accepted_args,
59 109
        'type'          => $type,
60
    ];
61 109
}
62
63
/**
64
 * Hooks a function on to a specific action.
65
 *
66
 * Actions are the hooks that YOURLS launches at specific points
67
 * during execution, or when specific events occur. Plugins can specify that
68
 * one or more of its PHP functions are executed at these points, using the
69
 * Action API.
70
 *
71
 * @param string $hook The name of the action to which the $function_to_add is hooked.
72
 * @param callback $function_name The name of the function you wish to be called.
73
 * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
74
 * @param int $accepted_args optional. The number of arguments the function accept (default 1).
75
 */
76
function yourls_add_action( $hook, $function_name, $priority = 10, $accepted_args = 1 ) {
77 29
	yourls_add_filter( $hook, $function_name, $priority, $accepted_args, 'action' );
78 29
}
79
80
/**
81
 * Build Unique ID for storage and retrieval.
82
 *
83
 * Simply using a function name is not enough, as several functions can have the same name when they are enclosed in classes.
84
 *
85
 * @global array $yourls_filters storage for all of the filters
86
 * @param string $hook hook to which the function is attached
87
 * @param string|array $function used for creating unique id
88
 * @param int|bool $priority used in counting how many hooks were applied.  If === false and $function is an object reference, we return the unique id only if it already has one, false otherwise.
89
 * @return string unique ID for usage as array key
90
 */
91
function yourls_filter_unique_id( $hook, $function, $priority ) {
92
    // If function then just skip all of the tests and not overwrite the following.
93 202
    if ( is_string( $function ) ) {
94 150
        return $function;
95
    }
96
97 64
    if ( is_object( $function ) ) {
98
        // Closures are currently implemented as objects
99 15
        $function = [ $function, '' ];
100
    }
101
    else {
102 49
        $function = (array)$function;
103
    }
104
105
    // Object Class Calling
106 64
    if ( is_object( $function[ 0 ] ) ) {
107 59
        return spl_object_hash( $function[ 0 ] ).$function[ 1 ];
108
    }
109
110
    // Static Calling
111 5
    if ( is_string( $function[ 0 ] ) ) {
112 5
        return $function[ 0 ].'::'.$function[ 1 ];
113
    }
114
    // TODO: missing final return;
115
}
116
117
/**
118
 * Performs a filtering operation on a YOURLS element or event.
119
 *
120
 * Typical use:
121
 *
122
 * 		1) Modify a variable if a function is attached to hook 'yourls_hook'
123
 *		$yourls_var = "default value";
124
 *		$yourls_var = yourls_apply_filter( 'yourls_hook', $yourls_var );
125
 *
126
 *		2) Trigger functions is attached to event 'yourls_event'
127
 *		yourls_apply_filter( 'yourls_event' );
128
 *      (see yourls_do_action() )
129
 *
130
 * Returns an element which may have been filtered by a filter.
131
 *
132
 * @global array $yourls_filters storage for all of the filters
133
 * @param string $hook the name of the YOURLS element or action
134
 * @param mixed $value the value of the element before filtering
135
 * @return mixed
136
 */
137
function yourls_apply_filter( $hook, $value = '' ) {
138 456
    global $yourls_filters;
139 456
    if ( !isset( $yourls_filters[ $hook ] ) ) {
140 430
        return $value;
141
    }
142
143 105
    $args = func_get_args();
144
145
    // Sort filters by priority
146 105
    ksort( $yourls_filters[ $hook ] );
147
148
    // Loops through each filter
149 105
    reset( $yourls_filters[ $hook ] );
150
    do {
151 105
        foreach ( (array)current( $yourls_filters[ $hook ] ) as $the_ ) {
152 105
            if ( !is_null($the_[ 'function' ]) ) {
153 105
                $args[ 1 ] = $value;
154 105
                $count = $the_[ 'accepted_args' ];
155 105
                if ( is_null( $count ) ) {
156 82
                    $_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1 ) );
157
                }
158
                else {
159 23
                    $_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1, (int)$count ) );
160
                }
161
            }
162 100
            if ( $the_[ 'type' ] == 'filter' ) {
163 82
                $value = $_value;
164
            }
165
        }
166 100
    } while ( next( $yourls_filters[ $hook ] ) !== false );
167
168
    // TODO: Missing final return - Why not just return all the time? It's up receiving code to use or not.
169 100
    if ( $the_[ 'type' ] == 'filter' ) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $the_ does not seem to be defined for all execution paths leading up to this point.
Loading history...
170 82
        return $value;
171
    }
172 18
}
173
174
/**
175
 * Performs an action triggered by a YOURLS event.
176
*
177
 * @param string $hook the name of the YOURLS action
178
 * @param mixed $arg action arguments
179
 */
180
function yourls_do_action( $hook, $arg = '' ) {
181 136
    global $yourls_actions;
182
183
    // Keep track of actions that are "done"
184 136
    if ( !isset( $yourls_actions ) ) {
185
        $yourls_actions = [];
186
    }
187 136
    if ( !isset( $yourls_actions[ $hook ] ) ) {
188 33
        $yourls_actions[ $hook ] = 1;
189
    }
190
    else {
191 119
        ++$yourls_actions[ $hook ];
192
    }
193
194 136
    $args = [];
195 136
    if ( is_array( $arg ) && 1 == count( $arg ) && isset( $arg[ 0 ] ) && is_object( $arg[ 0 ] ) ) { // array(&$this)
196
        $args[] =& $arg[ 0 ];
197
    }
198
    else {
199 136
        $args[] = $arg;
200
    }
201
202 136
    for ( $a = 2 ; $a < func_num_args() ; $a++ ) {
203 104
        $args[] = func_get_arg( $a );
204
    }
205
206 136
    yourls_apply_filter( $hook, $args );
207 132
}
208
209
/**
210
 * Retrieve the number times an action is fired.
211
 *
212
 * @param string $hook Name of the action hook.
213
 * @return int The number of times action hook <tt>$hook</tt> is fired
214
 */
215
function yourls_did_action( $hook ) {
216 25
    global $yourls_actions;
217 25
    return empty( $yourls_actions[ $hook ] ) ? 0 : $yourls_actions[ $hook ];
218
}
219
220
/**
221
 * Removes a function from a specified filter hook.
222
 *
223
 * This function removes a function attached to a specified filter hook. This
224
 * method can be used to remove default functions attached to a specific filter
225
 * hook and possibly replace them with a substitute.
226
 *
227
 * To remove a hook, the $function_to_remove and $priority arguments must match
228
 * when the hook was added.
229
 *
230
 * @global array $yourls_filters storage for all of the filters
231
 * @param string $hook The filter hook to which the function to be removed is hooked.
232
 * @param callback $function_to_remove The name of the function which should be removed.
233
 * @param int $priority optional. The priority of the function (default: 10).
234
 * @return bool Whether the function was registered as a filter before it was removed.
235
 */
236
function yourls_remove_filter( $hook, $function_to_remove, $priority = 10 ) {
237 122
    global $yourls_filters;
238
239 122
    $function_to_remove = yourls_filter_unique_id( $hook, $function_to_remove, $priority );
240
241 122
    $remove = isset( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
242
243 122
    if ( $remove === true ) {
244 32
        unset ( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
245 32
        if ( empty( $yourls_filters[ $hook ][ $priority ] ) ) {
246 32
            unset( $yourls_filters[ $hook ] );
247
        }
248
    }
249 122
    return $remove;
250
}
251
252
/**
253
 * Removes a function from a specified action hook.
254
 *
255
 * @see yourls_remove_filter()
256
 * @param string   $hook               The action hook to which the function to be removed is hooked.
257
 * @param callable $function_to_remove The name of the function which should be removed.
258
 * @param int      $priority           optional. The priority of the function (default: 10).
259
 * @return bool                        Whether the function was registered as an action before it was removed.
260
 */
261
function yourls_remove_action( $hook, $function_to_remove, $priority = 10 ) {
262 1
    return yourls_remove_filter( $hook, $function_to_remove, $priority );
263
}
264
265
/**
266
 * Removes all functions from a specified action hook.
267
 *
268
 * @see   yourls_remove_all_filters()
269
 * @since 1.7.1
270
 * @param string    $hook     The action to remove hooks from
271
 * @param int|false $priority optional. The priority of the functions to remove
272
 * @return bool true when it's finished
273
 */
274
function yourls_remove_all_actions( $hook, $priority = false ) {
275 28
    return yourls_remove_all_filters( $hook, $priority );
276
}
277
278
/**
279
 * Removes all functions from a specified filter hook.
280
 *
281
 * @since 1.7.1
282
 * @param string    $hook     The filter to remove hooks from
283
 * @param int|false $priority optional. The priority of the functions to remove
284
 * @return bool true when it's finished
285
 */
286
function yourls_remove_all_filters( $hook, $priority = false ) {
287 84
    global $yourls_filters;
288
289 84
    if ( isset( $yourls_filters[ $hook ] ) ) {
290 51
        if ( $priority === false ) {
291 50
            unset( $yourls_filters[ $hook ] );
292
        }
293 1
        elseif ( isset( $yourls_filters[ $hook ][ $priority ] ) ) {
294 1
            unset( $yourls_filters[ $hook ][ $priority ] );
295
        }
296
    }
297
298 84
    return true;
299
}
300
301
/**
302
 * Check if any filter has been registered for a hook.
303
 *
304
 * @param string         $hook              The name of the filter hook.
305
 * @param callable|false $function_to_check optional. If specified, return the priority of that function on this hook or false if not attached.
306
 * @return int|bool Optionally returns the priority on that hook for the specified function.
307
 * @global array         $yourls_filters    storage for all of the filters
308
 */
309
function yourls_has_filter( $hook, $function_to_check = false ) {
310 26
    global $yourls_filters;
311
312 26
    $has = !empty( $yourls_filters[ $hook ] );
313 26
    if ( false === $function_to_check || false === $has ) {
314 26
        return $has;
315
    }
316
317 4
    if ( !$idx = yourls_filter_unique_id( $hook, $function_to_check, false ) ) {
318
        return false;
319
    }
320
321 4
    foreach ( array_keys( $yourls_filters[ $hook ] ) as $priority ) {
322 4
        if ( isset( $yourls_filters[ $hook ][ $priority ][ $idx ] ) ) {
323 4
            return $priority;
324
        }
325
    }
326 1
    return false;
327
}
328
329
/**
330
 * @param string         $hook
331
 * @param callable|false $function_to_check
332
 * @return bool|int
333
 */
334
function yourls_has_action( $hook, $function_to_check = false ) {
335 13
    return yourls_has_filter( $hook, $function_to_check );
336
}
337
338
/**
339
 * Return number of active plugins
340
 *
341
 * @return int Number of activated plugins
342
 */
343
function yourls_has_active_plugins() {
344 15
    return count( yourls_get_db()->get_plugins() );
345
}
346
347
/**
348
 * List plugins in /user/plugins
349
 *
350
 * @return array Array of [/plugindir/plugin.php]=>array('Name'=>'Ozh', 'Title'=>'Hello', )
351
 */
352
function yourls_get_plugins() {
353 3
    $plugins = (array)glob( YOURLS_PLUGINDIR.'/*/plugin.php' );
354
355 3
    if ( is_array( $plugins ) ) {
0 ignored issues
show
The condition is_array($plugins) is always true.
Loading history...
356 3
        foreach ( $plugins as $key => $plugin ) {
357 3
            $plugins[ yourls_plugin_basename( $plugin ) ] = yourls_get_plugin_data( $plugin );
358 3
            unset( $plugins[ $key ] );
359
        }
360
    }
361
362 3
    return empty( $plugins ) ? [] : $plugins;
363
}
364
365
/**
366
 * Check if a plugin is active
367
 *
368
 * @param string $plugin Physical path to plugin file
369
 * @return bool
370
 */
371
function yourls_is_active_plugin( $plugin ) {
372 5
    return yourls_has_active_plugins() > 0 ?
373 3
        in_array( yourls_plugin_basename( $plugin ), yourls_get_db()->get_plugins() )
374 5
        : false;
375
}
376
377
/**
378
 * Parse a plugin header
379
 *
380
 * The plugin header has the following form:
381
 *      /*
382
 *      Plugin Name: <plugin name>
383
 *      Plugin URI: <plugin home page>
384
 *      Description: <plugin description>
385
 *      Version: <plugin version number>
386
 *      Author: <author name>
387
 *      Author URI: <author home page>
388
 *      * /
389
 *
390
 * Or in the form of a phpdoc block
391
 *      /**
392
 *       * Plugin Name: <plugin name>
393
 *       * Plugin URI: <plugin home page>
394
 *       * Description: <plugin description>
395
 *       * Version: <plugin version number>
396
 *       * Author: <author name>
397
 *       * Author URI: <author home page>
398
 *       * /
399
 *
400
 * @since 1.5
401
 * @param string $file Physical path to plugin file
402
 * @return array Array of 'Field'=>'Value' from plugin comment header lines of the form "Field: Value"
403
 */
404
function yourls_get_plugin_data( $file ) {
405 8
    $fp = fopen( $file, 'r' ); // assuming $file is readable, since yourls_load_plugins() filters this
406 8
    $data = fread( $fp, 8192 ); // get first 8kb
407 8
    fclose( $fp );
408
409
    // Capture all the header within first comment block
410 8
    if ( !preg_match( '!.*?/\*(.*?)\*/!ms', $data, $matches ) )
411
        return [];
412
413
    // Capture each line with "Something: some text"
414 8
    unset( $data );
415 8
    $lines = preg_split( "[\n|\r]", $matches[ 1 ] );
416 8
    unset( $matches );
417
418 8
    $plugin_data = [];
419 8
    foreach ( $lines as $line ) {
420 8
        if ( !preg_match( '!(\s*)?\*?(\s*)?(.*?):\s+(.*)!', $line, $matches ) ) {
421 8
            continue;
422
        }
423
424 6
        $plugin_data[ trim($matches[3]) ] = yourls_esc_html(trim($matches[4]));
425
    }
426
427 8
    return $plugin_data;
428
}
429
430
/**
431
 * Include active plugins
432
 *
433
 * This function includes every 'YOURLS_PLUGINDIR/plugin_name/plugin.php' found in option 'active_plugins'
434
 * It will return a diagnosis array with the following keys:
435
 *    (bool)'loaded' : true if plugin(s) loaded, false otherwise
436
 *    (string)'info' : extra information
437
 *
438
 * @since 1.5
439
 * @return array    Array('loaded' => bool, 'info' => string)
440
 */
441
function yourls_load_plugins() {
442
    // Don't load plugins when installing or updating
443 1
    if ( yourls_is_installing() OR yourls_is_upgrading() OR !yourls_is_installed() ) {
444
        return [
445
            'loaded' => false,
446
            'info'   => 'install/upgrade'
447
        ];
448
    }
449
450 1
    $active_plugins = (array)yourls_get_option( 'active_plugins' );
451 1
    if ( empty( $active_plugins ) ) {
452
        return [
453
            'loaded' => false,
454
            'info'   => 'no active plugin'
455
        ];
456
    }
457
458 1
    $plugins = [];
459 1
    foreach ( $active_plugins as $key => $plugin ) {
460 1
        if ( yourls_validate_plugin_file( YOURLS_PLUGINDIR.'/'.$plugin ) ) {
461 1
            include_once( YOURLS_PLUGINDIR.'/'.$plugin );
462 1
            $plugins[] = $plugin;
463 1
            unset( $active_plugins[ $key ] );
464
        }
465
    }
466
467
    // Replace active plugin list with list of plugins we just activated
468 1
    yourls_get_db()->set_plugins( $plugins );
469 1
    $info = count( $plugins ).' activated';
470
471
    // $active_plugins should be empty now, if not, a plugin could not be find: remove it
472 1
    $missing_count = count( $active_plugins );
473 1
    if ( $missing_count > 0 ) {
474 1
        yourls_update_option( 'active_plugins', $plugins );
475 1
        $message = yourls_n( 'Could not find and deactivate plugin :', 'Could not find and deactivate plugins :', $missing_count );
476 1
        $missing = '<strong>'.implode( '</strong>, <strong>', $active_plugins ).'</strong>';
477 1
        yourls_add_notice( $message.' '.$missing );
478 1
        $info .= ', '.$missing_count.' removed';
479
    }
480
481
    return [
482 1
        'loaded' => true,
483 1
        'info'   => $info
484
    ];
485
}
486
487
/**
488
 * Check if a file is safe for inclusion (well, "safe", no guarantee)
489
 *
490
 * @param string $file Full pathname to a file
491
 * @return bool
492
 */
493
function yourls_validate_plugin_file( $file ) {
494 6
    return false === strpos( $file, '..' )
495 6
           && false === strpos( $file, './' )
496 6
           && 'plugin.php' === substr( $file, -10 )
497 6
           && is_readable( $file );
498
}
499
500
/**
501
 * Activate a plugin
502
 *
503
 * @param string $plugin Plugin filename (full or relative to plugins directory)
504
 * @return string|true  string if error or true if success
505
 */
506
function yourls_activate_plugin( $plugin ) {
507
    // validate file
508 3
    $plugin = yourls_plugin_basename( $plugin );
509 3
    $plugindir = yourls_sanitize_filename( YOURLS_PLUGINDIR );
510 3
    if ( !yourls_validate_plugin_file( $plugindir.'/'.$plugin ) ) {
511 1
        return yourls__( 'Not a valid plugin file' );
512
    }
513
514
    // check not activated already
515 2
    $ydb = yourls_get_db();
516 2
    if ( yourls_is_active_plugin( $plugin ) ) {
517 1
        return yourls__( 'Plugin already activated' );
518
    }
519
520
    // attempt activation. TODO: uber cool fail proof sandbox like in WP.
521 1
    ob_start();
522 1
    include_once( $plugindir.'/'.$plugin );
523 1
    if ( ob_get_length() > 0 ) {
524
        // there was some output: error
525
        // @codeCoverageIgnoreStart
526
        $output = ob_get_clean();
527
        return yourls_s( 'Plugin generated unexpected output. Error was: <br/><pre>%s</pre>', $output );
528
        // @codeCoverageIgnoreEnd
529
    }
530 1
    ob_end_clean();
531
532
    // so far, so good: update active plugin list
533 1
    $ydb->add_plugin( $plugin );
534 1
    yourls_update_option( 'active_plugins', $ydb->get_plugins() );
535 1
    yourls_do_action( 'activated_plugin', $plugin );
536 1
    yourls_do_action( 'activated_'.$plugin );
537
538 1
    return true;
539
}
540
541
/**
542
 * Deactivate a plugin
543
 *
544
 * @param string $plugin Plugin filename (full relative to plugins directory)
545
 * @return string|true  string if error or true if success
546
 */
547
function yourls_deactivate_plugin( $plugin ) {
548 2
    $plugin = yourls_plugin_basename( $plugin );
549
550
    // Check plugin is active
551 2
    if ( !yourls_is_active_plugin( $plugin ) ) {
552 1
        return yourls__( 'Plugin not active' );
553
    }
554
555
    // Deactivate the plugin
556 1
    $ydb = yourls_get_db();
557 1
    $plugins = $ydb->get_plugins();
558 1
    $key = array_search( $plugin, $plugins );
559 1
    if ( $key !== false ) {
560 1
        array_splice( $plugins, $key, 1 );
561
    }
562
563 1
    $ydb->set_plugins( $plugins );
564 1
    yourls_update_option( 'active_plugins', $plugins );
565 1
    yourls_do_action( 'deactivated_plugin', $plugin );
566 1
    yourls_do_action( 'deactivated_'.$plugin );
567
568 1
    return true;
569
}
570
571
/**
572
 * Return the path of a plugin file, relative to the plugins directory
573
 * @param string $file
574
 * @return string
575
 */
576
function yourls_plugin_basename( $file ) {
577 11
	return trim( str_replace( yourls_sanitize_filename( YOURLS_PLUGINDIR ), '', yourls_sanitize_filename( $file ) ), '/' );
578
}
579
580
/**
581
 * Return the URL of the directory a plugin
582
 * @param string $file
583
 * @return string
584
 */
585
function yourls_plugin_url( $file ) {
586 4
    $url = YOURLS_PLUGINURL.'/'.yourls_plugin_basename( $file );
587 4
    if ( yourls_is_ssl() or yourls_needs_ssl() ) {
588 3
        $url = str_replace( 'http://', 'https://', $url );
589
    }
590 4
    return (string)yourls_apply_filter( 'plugin_url', $url, $file );
591
}
592
593
/**
594
 * Build list of links to plugin admin pages, if any
595
 *
596
 * @return array[]  Array of arrays of URL and anchor of plugin admin pages, or void if no plugin page
597
 */
598
function yourls_list_plugin_admin_pages() {
599 1
    $plugin_links = [];
600 1
    foreach ( yourls_get_db()->get_plugin_pages() as $plugin => $page ) {
601 1
        $plugin_links[ $plugin ] = [
602 1
            'url'    => yourls_admin_url( 'plugins.php?page='.$page[ 'slug' ] ),
603 1
            'anchor' => $page[ 'title' ],
604
        ];
605
    }
606 1
    return $plugin_links;
607
}
608
609
/**
610
 * Register a plugin administration page
611
 * @param string   $slug
612
 * @param string   $title
613
 * @param callable $function
614
 */
615
function yourls_register_plugin_page( $slug, $title, $function ) {
616 4
    yourls_get_db()->add_plugin_page( $slug, $title, $function );
617 4
}
618
619
/**
620
 * Handle plugin administration page
621
 *
622
 * @param string $plugin_page
623
 */
624
function yourls_plugin_admin_page( $plugin_page ) {
625
    // Check the plugin page is actually registered
626 3
    $pages = yourls_get_db()->get_plugin_pages();
627 3
    if ( !isset( $pages[ $plugin_page ] ) ) {
628 1
        yourls_die( yourls__( 'This page does not exist. Maybe a plugin you thought was activated is inactive?' ), yourls__( 'Invalid link' ) );
629
    }
630
631
    // Check the plugin page function is actually callable
632 2
    $page_function = $pages[ $plugin_page ][ 'function' ];
633 2
    if (!is_callable($page_function)) {
634 1
        yourls_die( yourls__( 'This page cannot be displayed because the displaying function is not callable.' ), yourls__( 'Invalid code' ) );
635
    }
636
637
    // Draw the page itself
638 1
    yourls_do_action( 'load-'.$plugin_page );
639 1
    yourls_html_head( 'plugin_page_'.$plugin_page, $pages[ $plugin_page ][ 'title' ] );
640 1
    yourls_html_logo();
641 1
    yourls_html_menu();
642
643 1
    $page_function( );
644
645 1
    yourls_html_footer();
646 1
}
647
648
/**
649
 * Callback function: Sort plugins
650
 *
651
 * @link http://php.net/uasort
652
 * @codeCoverageIgnore
653
 *
654
 * @param array $plugin_a
655
 * @param array $plugin_b
656
 * @return int 0, 1 or -1, see uasort()
657
 */
658
function yourls_plugins_sort_callback( $plugin_a, $plugin_b ) {
659
    $orderby = yourls_apply_filter( 'plugins_sort_callback', 'Plugin Name' );
660
    $order = yourls_apply_filter( 'plugins_sort_callback', 'ASC' );
661
662
    $a = isset( $plugin_a[ $orderby ] ) ? $plugin_a[ $orderby ] : '';
663
    $b = isset( $plugin_b[ $orderby ] ) ? $plugin_b[ $orderby ] : '';
664
665
    if ( $a == $b ) {
666
        return 0;
667
    }
668
669
    if ( 'DESC' == $order ) {
670
        return ( $a < $b ) ? 1 : -1;
671
    }
672
    else {
673
        return ( $a < $b ) ? -1 : 1;
674
    }
675
}
676
677
/**
678
 * Shutdown function, runs just before PHP shuts down execution. Stolen from WP
679
 *
680
 * This function is automatically tied to the script execution end at startup time, see
681
 * var $actions->register_shutdown in includes/Config/Init.php
682
 *
683
 * You can use this function to fire one or several actions when the PHP execution ends.
684
 * Example of use:
685
 *   yourls_add_action('shutdown', 'my_plugin_action_this');
686
 *   yourls_add_action('shutdown', 'my_plugin_action_that');
687
 *   // functions my_plugin_action_this() and my_plugin_action_that() will be triggered
688
 *   // after YOURLS is completely executed
689
 *
690
 * @since 1.5.1
691
 * @return void
692
 */
693
function yourls_shutdown() {
694
    yourls_do_action( 'shutdown' );
695
}
696
697
/**
698
 * Returns true.
699
 *
700
 * Useful for returning true to filters easily.
701
 *
702
 * @since 1.7.1
703
 * @return bool True.
704
 */
705
function yourls_return_true() {
706 35
    return true;
707
}
708
709
/**
710
 * Returns false.
711
 *
712
 * Useful for returning false to filters easily.
713
 *
714
 * @since 1.7.1
715
 * @return bool False.
716
 */
717
function yourls_return_false() {
718 16
    return false;
719
}
720
721
/**
722
 * Returns 0.
723
 *
724
 * Useful for returning 0 to filters easily.
725
 *
726
 * @since 1.7.1
727
 * @return int 0.
728
 */
729
function yourls_return_zero() {
730 1
    return 0;
731
}
732
733
/**
734
 * Returns an empty array.
735
 *
736
 * Useful for returning an empty array to filters easily.
737
 *
738
 * @since 1.7.1
739
 * @return array Empty array.
740
 */
741
function yourls_return_empty_array() {
742 4
    return [];
743
}
744
745
/**
746
 * Returns null.
747
 *
748
 * Useful for returning null to filters easily.
749
 *
750
 * @since 1.7.1
751
 * @return null Null value.
752
 */
753
function yourls_return_null() {
754
    return null;
755
}
756
757
/**
758
 * Returns an empty string.
759
 *
760
 * Useful for returning an empty string to filters easily.
761
 *
762
 * @since 1.7.1
763
 * @return string Empty string.
764
 */
765
function yourls_return_empty_string() {
766
    return '';
767
}
768