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


Generated: Tue Jan 27 05:10:15 2026 Cross-referenced by PHPXref 0.7.1