开发者

How to determine if Wordpress plugin is called from Wordpress widget

开发者 https://www.devze.com 2022-12-15 22:24 出处:网络
I am writing a Wordpress plugin that does string processing whenever \'the_author\' filter event is fired. However, some widgets also contain \'the_author\' event and subsequently my plugin is fired w

I am writing a Wordpress plugin that does string processing whenever 'the_author' filter event is fired. However, some widgets also contain 'the_author' event and subsequently my plugin is fired which should not happen. So I am trying to detect if my plugi开发者_开发问答n is called from certain widgets but so far to no avail. One widget that I would like to ignore is called 'Recent Comments'. I have tried:

function wrap_author($the_author) {
    if(!is_active_widget('recent_comments')) {
        $the_author = '<span class="CA_author">' . $the_author . '</span>';
        return $the_author;
    }
}

It could be that I am not using the right name for the widget, I have Googled a lot to find the proper internal name for the Recent Comments Widget but can't find it so far. Or maybe I should not be using is_active_widget function.


If you just want yours to fire on the "main" content areas then use the in_the_loop() function to check and see if you're in a content loop. This will probably get you 99% of the way there but you'll almost certainly find some edge case that'll cause frustration ;)

if (in_the_loop()) {
// do stuff
}

This will keep your code from executing at all in a sidebar.


You can do this using debug_backtrace(), however I would not recommend doing it frequently during a given page load because debug_backtrace() is not performant and it uses a significant memory each time it is called.

Here is the code for an in_widget() function which depends on being called within the display_callable() of WP_Widget or a class that extends WP_Widget

/**
 * Returns true if it is being called inside of a WordPress widget
 * 
 * Or at least that is what we hope.
 * 
 * @return bool
 */
function in_widget() {
  $in_widget = false;
  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
  foreach( $trace as $call ) {
    if ( !isset( $call['type'] ) ) {
      continue;
    }
    if ( !isset( $call['class'] ) ) {
      continue;
    }
    if ( !isset( $call['function'] ) ) {
      continue;
    }
    if ( '->' !== $call['type'] ) {
      continue;
    }
    if ( 'display_callback' !== $call['function'] ) {
      continue;
    }
    if ( of_lineage( $call['class'], WP_Widget::class ) ) {
      continue;
    }
    $in_widget = true;
    break;
  }
  return $in_widget;
}
/**
 * Returns true if $class is "of the lineage" of $lineage_class
 *
 * More technically speaking returns true when $class is  
 * either equal to $lineage_class or $class is a child 
 * class of $lineage_class.
 * 
 * @return bool
 */
function of_lineage( $class, $lineage_class ) {
  if ( is_object($class)) {
    $class = get_class($class);
  }
  return is_subclass_of( $class, $lineage_class ) 
      || $class === $lineage_class;
}

We use DEBUG_BACKTRACE_IGNORE_ARGS when calling debug_backtrace() to omit the argument values passed to functions in the call statck because as we do not need them for our use-case so we should not use the memory they would require.

In our foreach loop we then use short-circuit logic to look for the display_callable() method of WP_Widget or a child or WP_Widget. In each if{} statement we look for indicators the call we are inspecting is not the one we want starting wtih the one ones quickest to inspect. If any of our indicators tell us the call is not the one we want we simply continue to the next call in the foreach.

When (if?) we find the call we are looking for we set $in_widget = true; and immediately break out of the loop as there is no need to continue processing.

To allow you to visualize and better understand the logic in the in_widget() function the following is an example array returned by a call to debug_backtrace() when DEBUG_BACKTRACE_IGNORE_ARGS is passed. Note that each element representing a function or method call has different elements so you need to be careful when attempting to access any named element as that named element might not be set for a given function call:

array(
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-content/plugins/my-plugin/example.php',
    'line'     => 123,
    'function' => 'my_func',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/shortcodes.php',
    'line'     => 356,
    'function' => 'call_user_func',
  ),
  array(
    'function' => 'do_shortcode_tag',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/shortcodes.php',
    'line'     => 228,
    'function' => 'preg_replace_callback',
  ),
  array(
    'function' => 'do_shortcode',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/class-wp-hook.php',
    'line'     => 305,
    'function' => 'call_user_func_array',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/plugin.php',
    'line'     => 189,
    'function' => 'apply_filters',
    'class'    => 'WP_Hook',
    'type'     => '->',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/widgets/class-wp-widget-block.php',
    'line'     => 82,
    'function' => 'apply_filters',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/class-wp-widget.php',
    'line'     => 393,
    'function' => 'widget',
    'class'    => 'WP_Widget_Block',
    'type'     => '->',
  ),
  array(
    'function' => 'display_callback',
    'class'    => 'WP_Widget',
    'type'     => '->',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/widgets.php',
    'line'     => 831,
    'function' => 'call_user_func_array',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-content/themes/twentytwenty/template-parts/footer-menus-widgets.php',
    'line'     => 96,
    'function' => 'dynamic_sidebar',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/template.php',
    'line'     => 772,
    'args'     =>
      array(
        0 => '/Users/mikeschinkel/Sites/example.local/wp-content/themes/twentytwenty/template-parts/footer-menus-widgets.php',
      ),
    'function' => 'require',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/template.php',
    'line'     => 716,
    'function' => 'load_template',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/general-template.php',
    'line'     => 204,
    'function' => 'locate_template',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-content/themes/twentytwenty/index.php',
    'line'     => 116,
    'function' => 'get_template_part',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-includes/template-loader.php',
    'line'     => 106,
    'args'     =>
      array(
        0 => '/Users/mikeschinkel/Sites/example.local/wp-content/themes/twentytwenty/index.php',
      ),
    'function' => 'include',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/wp-blog-header.php',
    'line'     => 19,
    'args'     =>
      array(
        0 => '/Users/mikeschinkel/Sites/example.local/wp-includes/template-loader.php',
      ),
    'function' => 'require_once',
  ),
  array(
    'file'     => '/Users/mikeschinkel/Sites/example.local/index.php',
    'line'     => 17,
    'args'     =>
      array(
        0 => '/Users/mikeschinkel/Sites/example.local/wp-blog-header.php',
      ),
    'function' => 'require',
  ),
)

If for some reason this in_widget() function is not returning true when you think it should then maybe you can inspect the return value of debug_backtrace() to see why the logic is failing? If you figure out a use-case where this function does not work but you are able to modify it to work in your use-case and the more general use-case I wrote it for, please upate this answer or write your own answer to let me and other readers know of your improved version of in_widget().

Note: I was using this for deciding if I should display a shortened error message depending when the error occurs within a widget so for my use-case the performance (and generally) the memory usage will not be a problem. But YMMV.

BTW, I know this question is over a decade old, but I was looking for this answer today in hopes I wouldn't have to write it myself, but since nobody else seems to have posted how to do it I thought I would go ahead and do so for the next person who is looking.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号