[ Index ]

PHP Cross Reference of YOURLS

title

Body

[close]

/includes/ -> functions-plugins.php (source)

   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 https://www.php.net/manual/en/language.types.callable.php '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  /**
  19   * This global var will collect filters with the following structure:
  20   * $yourls_filters['hook']['array of priorities']['serialized function names']['array of ['array (functions, accepted_args, filter or action)]']
  21   *
  22   * Real life example :
  23   * print_r($yourls_filters) :
  24   * Array
  25   *  (
  26   *      [plugins_loaded] => Array
  27   *          (
  28   *              [10] => Array
  29   *                  (
  30   *                      [yourls_kses_init] => Array
  31   *                          (
  32   *                              [function] => yourls_kses_init
  33   *                              [accepted_args] => 1
  34   *                              [type] => action
  35   *                          )
  36   *                      [yourls_tzp_config] => Array
  37   *                          (
  38   *                              [function] => yourls_tzp_config
  39   *                              [accepted_args] => 1
  40   *                              [type] => action
  41   *                          )
  42   *                  )
  43   *          )
  44   *      [admin_menu] => Array
  45   *          (
  46   *              [10] => Array
  47   *                  (
  48   *                      [ozh_show_db] => Array
  49   *                          (
  50   *                              [function] => ozh_show_db
  51   *                              [accepted_args] =>
  52   *                              [type] => filter
  53   *                          )
  54   *                  )
  55   *          )
  56   *  )
  57   *
  58   * @var array $yourls_filters
  59   */
  60  if ( !isset( $yourls_filters ) ) {
  61      $yourls_filters = [];
  62  }
  63  
  64  /**
  65   * This global var will collect 'done' actions with the following structure:
  66   * $yourls_actions['hook'] => number of time this action was done
  67   *
  68   * @var array $yourls_actions
  69   */
  70  if ( !isset( $yourls_actions ) ) {
  71      $yourls_actions = [];
  72  }
  73  
  74  /**
  75   * Registers a filtering function
  76   *
  77   * Typical use:
  78   *        yourls_add_filter('some_hook', 'function_handler_for_hook');
  79   *
  80   * @link  https://docs.yourls.org/development/plugins.html
  81   * @param string   $hook           the name of the YOURLS element to be filtered or YOURLS action to be triggered
  82   * @param callable $function_name  the name of the function that is to be called.
  83   * @param int      $priority       optional. Used to specify the order in which the functions associated with a
  84   *                                 particular action are executed (default=10, lower=earlier execution, and functions
  85   *                                 with the same priority are executed in the order in which they were added to the
  86   *                                 filter)
  87   * @param int      $accepted_args  optional. The number of arguments the function accept (default is the number
  88   *                                 provided).
  89   * @param string   $type
  90   * @global array   $yourls_filters Storage for all of the filters
  91   * @return void
  92   */
  93  function yourls_add_filter( $hook, $function_name, $priority = 10, $accepted_args = NULL, $type = 'filter' ) {
  94      global $yourls_filters;
  95      // At this point, we cannot check if the function exists, as it may well be defined later (which is OK)
  96      $id = yourls_filter_unique_id($function_name);
  97  
  98      $yourls_filters[ $hook ][ $priority ][ $id ] = [
  99          'function'      => $function_name,
 100          'accepted_args' => $accepted_args,
 101          'type'          => $type,
 102      ];
 103  }
 104  
 105  /**
 106   * Hooks a function on to a specific action.
 107   *
 108   * Actions are the hooks that YOURLS launches at specific points
 109   * during execution, or when specific events occur. Plugins can specify that
 110   * one or more of its PHP functions are executed at these points, using the
 111   * Action API.
 112   *
 113   * Typical use:
 114   *        yourls_add_action('some_hook', 'function_handler_for_hook');
 115   *
 116   * @link  https://docs.yourls.org/development/plugins.html
 117   * @param string   $hook           The name of the action to which the $function_to_add is hooked.
 118   * @param callable $function_name  The name of the function you wish to be called.
 119   * @param int      $priority       Optional. Used to specify the order in which the functions associated with a particular action
 120   *                                 are executed (default: 10). Lower numbers correspond with earlier execution, and functions
 121   *                                 with the same priority are executed in the order in which they were added to the action.
 122   * @param int      $accepted_args  Optional. The number of arguments the function accept (default 1).
 123   * @return void
 124   */
 125  function yourls_add_action( $hook, $function_name, $priority = 10, $accepted_args = 1 ) {
 126      yourls_add_filter( $hook, $function_name, $priority, $accepted_args, 'action' );
 127  }
 128  
 129  /**
 130   * Build Unique ID for storage and retrieval.
 131   *
 132   * Simply using a function name is not enough, as several functions can have the same name when they are enclosed in classes.
 133   * Possible ways to attach a function to a hook (filter or action):
 134   *   - strings:
 135   *     yourls_add_filter('my_hook_test', 'my_callback_function');
 136   *     yourls_add_filter('my_hook_test', 'My_Class::my_callback_function');
 137   *
 138   *   - arrays:
 139   *     yourls_add_filter('my_hook_test', array('My_Class','my_callback_function'));
 140   *     yourls_add_filter('my_hook_test', array($class_instance, 'my_callback_function'));
 141   *
 142   *   - objects:
 143   *     yourls_add_filter('my_hook_test', $class_instance_with_invoke_method);
 144   *     yourls_add_filter('my_hook_test', $my_callback_function);
 145   *
 146   * @link https://docs.yourls.org/development/hooks.html
 147   * @param  string|array|object $function  The callable used in a filter or action.
 148   * @return string  unique ID for usage as array key
 149   */
 150  function yourls_filter_unique_id($function) {
 151      // If given a string (function name)
 152      if ( is_string( $function ) ) {
 153          return $function;
 154      }
 155  
 156      if ( is_object( $function ) ) {
 157          // Closures are implemented as objects
 158          $function = [ $function, '' ];
 159      }
 160      else {
 161          $function = (array)$function;
 162      }
 163  
 164      // Object Class Calling
 165      if ( is_object( $function[0] ) ) {
 166          return spl_object_hash( $function[0] ).$function[1];
 167      }
 168  
 169      // Last case, static Calling : $function[0] is a string (Class Name) and $function[1] is a string (Method Name)
 170      return $function[0].'::'.$function[1];
 171  }
 172  
 173  /**
 174   * Performs a filtering operation on a value or an event.
 175   *
 176   * Typical use:
 177   *
 178   *         1) Modify a variable if a function is attached to hook 'yourls_hook'
 179   *        $yourls_var = "default value";
 180   *        $yourls_var = yourls_apply_filter( 'yourls_hook', $yourls_var );
 181   *
 182   *        2) Trigger functions is attached to event 'yourls_event'
 183   *        yourls_apply_filter( 'yourls_event' );
 184   *      (see yourls_do_action() )
 185   *
 186   * Returns a value which may have been modified by a filter.
 187   *
 188   * @global array $yourls_filters storage for all of the filters
 189   * @param string $hook the name of the YOURLS element or action
 190   * @param mixed $value the value of the element before filtering
 191   * @param true|mixed $is_action true if the function is called by yourls_do_action() - otherwise may be the second parameter of an arbitrary number of parameters
 192   * @return mixed
 193   */
 194  function yourls_apply_filter( $hook, $value = '', $is_action = false ) {
 195      global $yourls_filters;
 196  
 197      $args = func_get_args();
 198  
 199      // Do 'all' filters first. We check if $is_action to avoid calling `yourls_call_all_hooks()` twice
 200      if ( !$is_action && isset($yourls_filters['all']) ) {
 201          yourls_call_all_hooks('filter', $hook, $args);
 202      }
 203  
 204      // If we have no hook attached to that filter, just return unmodified $value
 205      if ( !isset( $yourls_filters[ $hook ] ) ) {
 206          return $value;
 207      }
 208  
 209      // Sort filters by priority
 210      ksort( $yourls_filters[ $hook ] );
 211  
 212      // Loops through each filter
 213      reset( $yourls_filters[ $hook ] );
 214      do {
 215          foreach ( (array)current( $yourls_filters[ $hook ] ) as $the_ ) {
 216              $_value = '';
 217              if ( !is_null($the_[ 'function' ]) ) {
 218                  $args[ 1 ] = $value;
 219                  $count = $the_[ 'accepted_args' ];
 220                  if ( is_null( $count ) ) {
 221                      $_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1 ) );
 222                  }
 223                  else {
 224                      $_value = call_user_func_array( $the_[ 'function' ], array_slice( $args, 1, (int)$count ) );
 225                  }
 226              }
 227              if ( $the_[ 'type' ] == 'filter' ) {
 228                  $value = $_value;
 229              }
 230          }
 231      } while ( next( $yourls_filters[ $hook ] ) !== false );
 232  
 233      // Return the value - this will be actually used only for filters, not for actions (see `yourls_do_action()`)
 234      return $value;
 235  }
 236  
 237  /**
 238   * Performs an action triggered by a YOURLS event.
 239   *
 240   * @param string $hook the name of the YOURLS action
 241   * @param mixed $arg action arguments
 242   * @return void
 243   */
 244  function yourls_do_action( $hook, $arg = '' ) {
 245      global $yourls_actions, $yourls_filters;
 246  
 247      // Keep track of actions that are "done"
 248      if ( !isset( $yourls_actions ) ) {
 249          $yourls_actions = [];
 250      }
 251      if ( !isset( $yourls_actions[ $hook ] ) ) {
 252          $yourls_actions[ $hook ] = 1;
 253      }
 254      else {
 255          ++$yourls_actions[ $hook ];
 256      }
 257  
 258      $args = [];
 259      if ( is_array( $arg ) && 1 == count( $arg ) && isset( $arg[ 0 ] ) && is_object( $arg[ 0 ] ) ) { // array(&$this)
 260          $args[] =& $arg[ 0 ];
 261      }
 262      else {
 263          $args[] = $arg;
 264      }
 265  
 266      for ( $a = 2 ; $a < func_num_args() ; $a++ ) {
 267          $args[] = func_get_arg( $a );
 268      }
 269  
 270      // Do 'all' actions first
 271      if ( isset($yourls_filters['all']) ) {
 272          yourls_call_all_hooks('action', $hook, $args);
 273      }
 274  
 275      yourls_apply_filter( $hook, $args, true );
 276  }
 277  
 278  /**
 279   * Retrieve the number times an action is fired.
 280   *
 281   * @param string $hook Name of the action hook.
 282   * @return int The number of times action hook <tt>$hook</tt> is fired
 283   */
 284  function yourls_did_action( $hook ) {
 285      global $yourls_actions;
 286      return empty( $yourls_actions[ $hook ] ) ? 0 : $yourls_actions[ $hook ];
 287  }
 288  
 289  /**
 290   * Execute the 'all' hook, with all of the arguments or parameters that were used for the hook
 291   *
 292   * Internal function used by yourls_do_action() and yourls_apply_filter() - not meant to be used from
 293   * outside these functions.
 294   * This is mostly a debugging function to understand the flow of events.
 295   * See https://docs.yourls.org/development/debugging.html to learn how to use the 'all' hook
 296   *
 297   * @link   https://docs.yourls.org/development/debugging.html
 298   * @since  1.8.1
 299   * @param  string $type Either 'action' or 'filter'
 300   * @param  string $hook The hook name, eg 'plugins_loaded'
 301   * @param  mixed  $args Variable-length argument lists that were passed to the action or filter
 302   * @return void
 303   */
 304  function yourls_call_all_hooks($type, $hook, ...$args) {
 305      global $yourls_filters;
 306  
 307      // Loops through each filter or action hooked with the 'all' hook
 308      reset( $yourls_filters['all'] );
 309      do {
 310          foreach ( (array) current($yourls_filters['all']) as $the_ )
 311              // Call the hooked function only if it's hooked to the current type of hook (eg 'filter' or 'action')
 312              if ( $the_['type'] == $type && !is_null($the_['function']) ) {
 313                  call_user_func_array( $the_['function'], array($type, $hook, $args) );
 314                  /**
 315                   * Note that we don't return a value here, regardless of $type being an action (obviously) but also
 316                   * a filter. Indeed it would not make sense to actually "filter" and return values when we're
 317                   * feeding the same function every single hook in YOURLS, no matter their parameters.
 318                   */
 319              }
 320  
 321      } while ( next($yourls_filters['all']) !== false );
 322  
 323  }
 324  
 325  /**
 326   * Removes a function from a specified filter hook.
 327   *
 328   * This function removes a function attached to a specified filter hook. This
 329   * method can be used to remove default functions attached to a specific filter
 330   * hook and possibly replace them with a substitute.
 331   *
 332   * To remove a hook, the $function_to_remove and $priority arguments must match
 333   * when the hook was added.
 334   *
 335   * @global array $yourls_filters storage for all of the filters
 336   * @param string $hook The filter hook to which the function to be removed is hooked.
 337   * @param callable $function_to_remove The name of the function which should be removed.
 338   * @param int $priority optional. The priority of the function (default: 10).
 339   * @return bool Whether the function was registered as a filter before it was removed.
 340   */
 341  function yourls_remove_filter( $hook, $function_to_remove, $priority = 10 ) {
 342      global $yourls_filters;
 343  
 344      $function_to_remove = yourls_filter_unique_id($function_to_remove);
 345  
 346      $remove = isset( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
 347  
 348      if ( $remove === true ) {
 349          unset ( $yourls_filters[ $hook ][ $priority ][ $function_to_remove ] );
 350          if ( empty( $yourls_filters[ $hook ][ $priority ] ) ) {
 351              unset( $yourls_filters[ $hook ] );
 352          }
 353      }
 354      return $remove;
 355  }
 356  
 357  /**
 358   * Removes a function from a specified action hook.
 359   *
 360   * @see yourls_remove_filter()
 361   * @since 1.7.1
 362   * @param string   $hook               The action hook to which the function to be removed is hooked.
 363   * @param callable $function_to_remove The name of the function which should be removed.
 364   * @param int      $priority           optional. The priority of the function (default: 10).
 365   * @return bool                        Whether the function was registered as an action before it was removed.
 366   */
 367  function yourls_remove_action( $hook, $function_to_remove, $priority = 10 ) {
 368      return yourls_remove_filter( $hook, $function_to_remove, $priority );
 369  }
 370  
 371  /**
 372   * Removes all functions from a specified action hook.
 373   *
 374   * @see   yourls_remove_all_filters()
 375   * @since 1.7.1
 376   * @param string    $hook     The action to remove hooks from
 377   * @param int|false $priority optional. The priority of the functions to remove
 378   * @return bool true when it's finished
 379   */
 380  function yourls_remove_all_actions( $hook, $priority = false ) {
 381      return yourls_remove_all_filters( $hook, $priority );
 382  }
 383  
 384  /**
 385   * Removes all functions from a specified filter hook.
 386   *
 387   * @since 1.7.1
 388   * @param string    $hook     The filter to remove hooks from
 389   * @param int|false $priority optional. The priority of the functions to remove
 390   * @return bool true when it's finished
 391   */
 392  function yourls_remove_all_filters( $hook, $priority = false ) {
 393      global $yourls_filters;
 394  
 395      if ( isset( $yourls_filters[ $hook ] ) ) {
 396          if ( $priority === false ) {
 397              unset( $yourls_filters[ $hook ] );
 398          }
 399          elseif ( isset( $yourls_filters[ $hook ][ $priority ] ) ) {
 400              unset( $yourls_filters[ $hook ][ $priority ] );
 401          }
 402      }
 403  
 404      return true;
 405  }
 406  
 407  /**
 408   * Return filters for a specific hook.
 409   *
 410   * If hook has filters (or actions, see yourls_has_action()), this will return an array priorities => callbacks.
 411   * See the structure of yourls_filters on top of this file for details.
 412   *
 413   * @since 1.8.3
 414   * @param string $hook The hook to retrieve filters for
 415   * @return array
 416   */
 417  function yourls_get_filters($hook) {
 418      global $yourls_filters;
 419      return $yourls_filters[$hook] ?? array();
 420  }
 421  
 422  /**
 423   * Return actions for a specific hook.
 424   *
 425   * @since 1.8.3
 426   * @param string $hook The hook to retrieve actions for
 427   * @return array
 428   */
 429  function yourls_get_actions($hook) {
 430      return yourls_get_filters($hook);
 431  }
 432  /**
 433   * Check if any filter has been registered for a hook.
 434   *
 435   * @since 1.5
 436   * @global array         $yourls_filters    storage for all of the filters
 437   * @param string         $hook              The name of the filter hook.
 438   * @param callable|false $function_to_check optional. If specified, return the priority of that function on this hook or false if not attached.
 439   * @return int|bool Optionally returns the priority on that hook for the specified function.
 440   */
 441  function yourls_has_filter( $hook, $function_to_check = false ) {
 442      global $yourls_filters;
 443  
 444      $has = !empty( $yourls_filters[ $hook ] );
 445      if ( false === $function_to_check || false === $has ) {
 446          return $has;
 447      }
 448  
 449      if ( !$idx = yourls_filter_unique_id($function_to_check) ) {
 450          return false;
 451      }
 452  
 453      foreach ( array_keys( $yourls_filters[ $hook ] ) as $priority ) {
 454          if ( isset( $yourls_filters[ $hook ][ $priority ][ $idx ] ) ) {
 455              return $priority;
 456          }
 457      }
 458      return false;
 459  }
 460  
 461  
 462  /**
 463   * Check if any action has been registered for a hook.
 464   *
 465   * @since 1.5
 466   * @param string         $hook
 467   * @param callable|false $function_to_check
 468   * @return bool|int
 469   */
 470  function yourls_has_action( $hook, $function_to_check = false ) {
 471      return yourls_has_filter( $hook, $function_to_check );
 472  }
 473  
 474  /**
 475   * Return number of active plugins
 476   *
 477   * @return int Number of activated plugins
 478   */
 479  function yourls_has_active_plugins() {
 480      return count( yourls_get_db()->get_plugins() );
 481  }
 482  
 483  /**
 484   * List plugins in /user/plugins
 485   *
 486   * @return array Array of [/plugindir/plugin.php]=>array('Name'=>'Ozh', 'Title'=>'Hello', )
 487   */
 488  function yourls_get_plugins() {
 489      $plugins = (array)glob( YOURLS_PLUGINDIR.'/*/plugin.php' );
 490  
 491      if ( is_array( $plugins ) ) {
 492          foreach ( $plugins as $key => $plugin ) {
 493              $plugins[ yourls_plugin_basename( $plugin ) ] = yourls_get_plugin_data( $plugin );
 494              unset( $plugins[ $key ] );
 495          }
 496      }
 497  
 498      return empty( $plugins ) ? [] : $plugins;
 499  }
 500  
 501  /**
 502   * Check if a plugin is active
 503   *
 504   * @param string $plugin Physical path to plugin file
 505   * @return bool
 506   */
 507  function yourls_is_active_plugin( $plugin ) {
 508      return yourls_has_active_plugins() > 0 ?
 509          in_array( yourls_plugin_basename( $plugin ), yourls_get_db()->get_plugins() )
 510          : false;
 511  }
 512  
 513  /**
 514   * Parse a plugin header
 515   *
 516   * The plugin header has the following form:
 517   *      /*
 518   *      Plugin Name: <plugin name>
 519   *      Plugin URI: <plugin home page>
 520   *      Description: <plugin description>
 521   *      Version: <plugin version number>
 522   *      Author: <author name>
 523   *      Author URI: <author home page>
 524   *      * /
 525   *
 526   * Or in the form of a phpdoc block
 527   *      /**
 528   *       * Plugin Name: <plugin name>
 529   *       * Plugin URI: <plugin home page>
 530   *       * Description: <plugin description>
 531   *       * Version: <plugin version number>
 532   *       * Author: <author name>
 533   *       * Author URI: <author home page>
 534   *       * /
 535   *
 536   * @since 1.5
 537   * @param string $file Physical path to plugin file
 538   * @return array Array of 'Field'=>'Value' from plugin comment header lines of the form "Field: Value"
 539   */
 540  function yourls_get_plugin_data( $file ) {
 541      $fp = fopen( $file, 'r' ); // assuming $file is readable, since yourls_load_plugins() filters this
 542      $data = fread( $fp, 8192 ); // get first 8kb
 543      fclose( $fp );
 544  
 545      // Capture all the header within first comment block
 546      if ( !preg_match( '!.*?/\*(.*?)\*/!ms', $data, $matches ) ) {
 547          return [];
 548      }
 549  
 550      // Capture each line with "Something: some text"
 551      unset( $data );
 552      $lines = preg_split( "[\n|\r]", $matches[ 1 ] );
 553      unset( $matches );
 554  
 555      $plugin_data = [];
 556      foreach ( $lines as $line ) {
 557          if ( !preg_match( '!(\s*)?\*?(\s*)?(.*?):\s+(.*)!', $line, $matches ) ) {
 558              continue;
 559          }
 560  
 561          $plugin_data[ trim($matches[3]) ] = yourls_esc_html(trim($matches[4]));
 562      }
 563  
 564      return $plugin_data;
 565  }
 566  
 567  /**
 568   * Include active plugins
 569   *
 570   * This function includes every 'YOURLS_PLUGINDIR/plugin_name/plugin.php' found in option 'active_plugins'
 571   * It will return a diagnosis array with the following keys:
 572   *    (bool)'loaded' : true if plugin(s) loaded, false otherwise
 573   *    (string)'info' : extra information
 574   *
 575   * @since 1.5
 576   * @return array    Array('loaded' => bool, 'info' => string)
 577   */
 578  function yourls_load_plugins() {
 579      // Don't load plugins when installing or updating
 580      if ( yourls_is_installing() OR yourls_is_upgrading() OR !yourls_is_installed() ) {
 581          return [
 582              'loaded' => false,
 583              'info'   => 'install/upgrade'
 584          ];
 585      }
 586  
 587      $active_plugins = (array)yourls_get_option( 'active_plugins' );
 588      if ( empty( $active_plugins ) ) {
 589          return [
 590              'loaded' => false,
 591              'info'   => 'no active plugin'
 592          ];
 593      }
 594  
 595      $plugins = [];
 596      foreach ( $active_plugins as $key => $plugin ) {
 597          $file = YOURLS_PLUGINDIR . '/' . $plugin;
 598          if ( yourls_is_a_plugin_file($file) && yourls_activate_plugin_sandbox( $file ) === true ) {
 599              $plugins[] = $plugin;
 600              unset( $active_plugins[ $key ] );
 601          }
 602      }
 603  
 604      // Replace active plugin list with list of plugins we just activated
 605      yourls_get_db()->set_plugins( $plugins );
 606      $info = count( $plugins ).' activated';
 607  
 608      // $active_plugins should be empty now, if not, a plugin could not be found, or is erroneous : remove it
 609      $missing_count = count( $active_plugins );
 610      if ( $missing_count > 0 ) {
 611          yourls_update_option( 'active_plugins', $plugins );
 612          $message = yourls_n( 'Could not find and deactivate plugin :', 'Could not find and deactivate plugins :', $missing_count );
 613          $missing = '<strong>'.implode( '</strong>, <strong>', $active_plugins ).'</strong>';
 614          yourls_add_notice( $message.' '.$missing );
 615          $info .= ', '.$missing_count.' removed';
 616      }
 617  
 618      return [
 619          'loaded' => true,
 620          'info'   => $info
 621      ];
 622  }
 623  
 624  /**
 625   * Check if a file is a plugin file
 626   *
 627   * This doesn't check if the file is a valid PHP file, only that it's correctly named.
 628   *
 629   * @since 1.5
 630   * @param string $file Full pathname to a file
 631   * @return bool
 632   */
 633  function yourls_is_a_plugin_file($file) {
 634      return false === strpos( $file, '..' )
 635             && false === strpos( $file, './' )
 636             && 'plugin.php' === substr( $file, -10 )
 637             && is_readable( $file );
 638  }
 639  
 640  /**
 641   * Activate a plugin
 642   *
 643   * @since 1.5
 644   * @param string $plugin Plugin filename (full or relative to plugins directory)
 645   * @return string|true  string if error or true if success
 646   */
 647  function yourls_activate_plugin( $plugin ) {
 648      // validate file
 649      $plugin = yourls_plugin_basename( $plugin );
 650      $plugindir = yourls_sanitize_filename( YOURLS_PLUGINDIR );
 651      if ( !yourls_is_a_plugin_file($plugindir . '/' . $plugin ) ) {
 652          return yourls__( 'Not a valid plugin file' );
 653      }
 654  
 655      // check not activated already
 656      $ydb = yourls_get_db();
 657      if ( yourls_is_active_plugin( $plugin ) ) {
 658          return yourls__( 'Plugin already activated' );
 659      }
 660  
 661      // attempt activation.
 662      $attempt = yourls_activate_plugin_sandbox( $plugindir.'/'.$plugin );
 663      if( $attempt !== true ) {
 664          return yourls_s( 'Plugin generated unexpected output. Error was: <br/><pre>%s</pre>', $attempt );
 665      }
 666  
 667      // so far, so good: update active plugin list
 668      $ydb->add_plugin( $plugin );
 669      yourls_update_option( 'active_plugins', $ydb->get_plugins() );
 670      yourls_do_action( 'activated_plugin', $plugin );
 671      yourls_do_action( 'activated_'.$plugin );
 672  
 673      return true;
 674  }
 675  
 676  /**
 677   * Plugin activation sandbox
 678   *
 679   * @since 1.8.3
 680   * @param string $pluginfile Plugin filename (full path)
 681   * @return string|true  string if error or true if success
 682   */
 683  function yourls_activate_plugin_sandbox( $pluginfile ) {
 684      try {
 685          include_once $pluginfile;
 686          return true;
 687      } catch ( \Throwable $e ) {
 688          return $e->getMessage();
 689      }
 690  }
 691  
 692  /**
 693   * Deactivate a plugin
 694   *
 695   * @since 1.5
 696   * @param string $plugin Plugin filename (full relative to plugins directory)
 697   * @return string|true  string if error or true if success
 698   */
 699  function yourls_deactivate_plugin( $plugin ) {
 700      $plugin = yourls_plugin_basename( $plugin );
 701  
 702      // Check plugin is active
 703      if ( !yourls_is_active_plugin( $plugin ) ) {
 704          return yourls__( 'Plugin not active' );
 705      }
 706  
 707      // Check if we have an uninstall file - load if so
 708      $uninst_file = YOURLS_PLUGINDIR . '/' . dirname($plugin) . '/uninstall.php';
 709      if ( file_exists($uninst_file) ) {
 710          define('YOURLS_UNINSTALL_PLUGIN', true);
 711          $attempt = yourls_activate_plugin_sandbox( $uninst_file );
 712          if( $attempt !== true ) {
 713              return yourls_s( 'Plugin generated unexpected output. Error was: <br/><pre>%s</pre>', $attempt );
 714          }
 715      }
 716  
 717      // Deactivate the plugin
 718      $ydb = yourls_get_db();
 719      $plugins = $ydb->get_plugins();
 720      $key = array_search( $plugin, $plugins );
 721      if ( $key !== false ) {
 722          array_splice( $plugins, $key, 1 );
 723      }
 724  
 725      $ydb->set_plugins( $plugins );
 726      yourls_update_option( 'active_plugins', $plugins );
 727      yourls_do_action( 'deactivated_plugin', $plugin );
 728      yourls_do_action( 'deactivated_'.$plugin );
 729  
 730      return true;
 731  }
 732  
 733  /**
 734   * Return the path of a plugin file, relative to the plugins directory
 735   *
 736   * @since 1.5
 737   * @param string $file
 738   * @return string
 739   */
 740  function yourls_plugin_basename( $file ) {
 741      return trim( str_replace( yourls_sanitize_filename( YOURLS_PLUGINDIR ), '', yourls_sanitize_filename( $file ) ), '/' );
 742  }
 743  
 744  /**
 745   * Return the URL of the directory a plugin
 746   *
 747   * @since 1.5
 748   * @param string $file
 749   * @return string
 750   */
 751  function yourls_plugin_url( $file ) {
 752      $url = YOURLS_PLUGINURL.'/'.yourls_plugin_basename( $file );
 753      if ( yourls_is_ssl() or yourls_needs_ssl() ) {
 754          $url = str_replace( 'http://', 'https://', $url );
 755      }
 756      return (string)yourls_apply_filter( 'plugin_url', $url, $file );
 757  }
 758  
 759  /**
 760   * Build list of links to plugin admin pages, if any
 761   *
 762   * @since 1.5
 763   * @return array  Array of arrays of URL and anchor of plugin admin pages, or empty array if no plugin page
 764   */
 765  function yourls_list_plugin_admin_pages() {
 766      $plugin_links = [];
 767      foreach ( yourls_get_db()->get_plugin_pages() as $plugin => $page ) {
 768          $plugin_links[ $plugin ] = [
 769              'url'    => yourls_admin_url( 'plugins.php?page='.$page[ 'slug' ] ),
 770              'anchor' => $page[ 'title' ],
 771          ];
 772      }
 773      return $plugin_links;
 774  }
 775  
 776  /**
 777   * Register a plugin administration page
 778   *
 779   * @since 1.5
 780   * @param string   $slug
 781   * @param string   $title
 782   * @param callable $function
 783   * @return void
 784   */
 785  function yourls_register_plugin_page( $slug, $title, $function ) {
 786      yourls_get_db()->add_plugin_page( $slug, $title, $function );
 787  }
 788  
 789  /**
 790   * Handle plugin administration page
 791   *
 792   * @since 1.5
 793   * @param string $plugin_page
 794   * @return void
 795   */
 796  function yourls_plugin_admin_page( $plugin_page ) {
 797      // Check the plugin page is actually registered
 798      $pages = yourls_get_db()->get_plugin_pages();
 799      if ( !isset( $pages[ $plugin_page ] ) ) {
 800          yourls_die( yourls__( 'This page does not exist. Maybe a plugin you thought was activated is inactive?' ), yourls__( 'Invalid link' ) );
 801      }
 802  
 803      // Check the plugin page function is actually callable
 804      $page_function = $pages[ $plugin_page ][ 'function' ];
 805      if (!is_callable($page_function)) {
 806          yourls_die( yourls__( 'This page cannot be displayed because the displaying function is not callable.' ), yourls__( 'Invalid code' ) );
 807      }
 808  
 809      // Draw the page itself
 810      yourls_do_action( 'load-'.$plugin_page );
 811      yourls_html_head( 'plugin_page_'.$plugin_page, $pages[ $plugin_page ][ 'title' ] );
 812      yourls_html_logo();
 813      yourls_html_menu();
 814  
 815      $page_function( );
 816  
 817      yourls_html_footer();
 818  }
 819  
 820  /**
 821   * Callback function: Sort plugins
 822   *
 823   * @link http://php.net/uasort
 824   * @codeCoverageIgnore
 825   *
 826   * @since 1.5
 827   * @param array $plugin_a
 828   * @param array $plugin_b
 829   * @return int 0, 1 or -1, see uasort()
 830   */
 831  function yourls_plugins_sort_callback( $plugin_a, $plugin_b ) {
 832      $orderby = yourls_apply_filter( 'plugins_sort_callback', 'Plugin Name' );
 833      $order = yourls_apply_filter( 'plugins_sort_callback', 'ASC' );
 834  
 835      $a = isset( $plugin_a[ $orderby ] ) ? $plugin_a[ $orderby ] : '';
 836      $b = isset( $plugin_b[ $orderby ] ) ? $plugin_b[ $orderby ] : '';
 837  
 838      if ( $a == $b ) {
 839          return 0;
 840      }
 841  
 842      if ( 'DESC' == $order ) {
 843          return ( $a < $b ) ? 1 : -1;
 844      }
 845      else {
 846          return ( $a < $b ) ? -1 : 1;
 847      }
 848  }
 849  
 850  /**
 851   * Shutdown function, runs just before PHP shuts down execution. Stolen from WP
 852   *
 853   * This function is automatically tied to the script execution end at startup time, see
 854   * var $actions->register_shutdown in includes/Config/Init.php
 855   *
 856   * You can use this function to fire one or several actions when the PHP execution ends.
 857   * Example of use:
 858   *   yourls_add_action('shutdown', 'my_plugin_action_this');
 859   *   yourls_add_action('shutdown', 'my_plugin_action_that');
 860   *   // functions my_plugin_action_this() and my_plugin_action_that() will be triggered
 861   *   // after YOURLS is completely executed
 862   *
 863   * @codeCoverageIgnore
 864   * @since 1.5.1
 865   * @return void
 866   */
 867  function yourls_shutdown() {
 868      yourls_do_action( 'shutdown' );
 869  }
 870  
 871  /**
 872   * Returns true.
 873   *
 874   * Useful for returning true to filters easily.
 875   *
 876   * @since 1.7.1
 877   * @return bool True.
 878   */
 879  function yourls_return_true() {
 880      return true;
 881  }
 882  
 883  /**
 884   * Returns false.
 885   *
 886   * Useful for returning false to filters easily.
 887   *
 888   * @since 1.7.1
 889   * @return bool False.
 890   */
 891  function yourls_return_false() {
 892      return false;
 893  }
 894  
 895  /**
 896   * Returns 0.
 897   *
 898   * Useful for returning 0 to filters easily.
 899   *
 900   * @since 1.7.1
 901   * @return int 0.
 902   */
 903  function yourls_return_zero() {
 904      return 0;
 905  }
 906  
 907  /**
 908   * Returns an empty array.
 909   *
 910   * Useful for returning an empty array to filters easily.
 911   *
 912   * @since 1.7.1
 913   * @return array Empty array.
 914   */
 915  function yourls_return_empty_array() {
 916      return [];
 917  }
 918  
 919  /**
 920   * Returns null.
 921   *
 922   * Useful for returning null to filters easily.
 923   *
 924   * @since 1.7.1
 925   * @return null Null value.
 926   */
 927  function yourls_return_null() {
 928      return null;
 929  }
 930  
 931  /**
 932   * Returns an empty string.
 933   *
 934   * Useful for returning an empty string to filters easily.
 935   *
 936   * @since 1.7.1
 937   * @return string Empty string.
 938   */
 939  function yourls_return_empty_string() {
 940      return '';
 941  }


Generated: Wed Sep 28 05:10:02 2022 Cross-referenced by PHPXref 0.7.1