The task
It’s not as easy, as it looks at a first glance. The main problem is, that you can define your own shortcode and simply override the default. Actually that’s what some themes are doing (blame you ThemeForest authors!). In this case, simply hooking into
post_gallery
won’t work in every case.
Drawbacks for post_gallery
The problem is that both the shortcode, as well as the core callback can be overridden and therefore every callback will simply be knocked out.
Drawbacks for the_content
If you’re switching to another filter and take the raw output, then you’ll have to deal (another time) with the Regex. This is slow and mostly won’t be simple.
So what to do now? Easy. Interact with the global $shortcode_tags
. It holds the callback as second argument, so it’s actually not too hard to determine in which case we are. Then we can simply switch on demand. This way we have a fine balance between reliability and performance.
Build a base plugin
Here’s a plugin that defines an abstract
class (one that must get extend
ed in order to work). There’re three things that need definition in the child class:
$part
– the part you want to retrieve$type
– the type of match you need. Valid aredigit/alphanumeric/alpha
process_atts()
– the method that processes your output – whatever you want to do with the result
Just upload this via (S)FTP into your plugins folder and activate.
<?php
/** Plugin Name: (#70451) »kaiser« Get Gallery attributes (Base) */
if ( ! class_exists( 'wpse70451_extract_gallery_atts' ) )
{
abstract class wpse70451_extract_gallery_atts
{
public $atts = array();
public $error;
// Must get defined in extending class
public $part;
public $type;
public static function init()
{
is_null( self :: $instance ) AND self :: $instance = new self;
return self :: $instance;
}
public function __construct()
{
! isset( $this->part ) AND new WP_Error();
add_action( 'loop_start', array( $this, 'error_handler' ) );
// The value of the array is the callback fn name
// If it's the default, then we're on the safe side,
// as the core fn is no pluggable and can't get overridden.
if ( 'gallery_shortcode' === $GLOBALS['shortcode_tags'] )
{
add_filter( 'post_gallery', array( $this, 'get_gallery_atts' ), 0, 2 );
}
// Else we have to go with a slower regex
else
{
add_filter( 'the_content', array( $this, 'get_gallery_ids' ), 0 );
}
}
public function error_handler()
{
if (
! in_array(
$this->type
,array(
'digit'
,'alphanumeric'
,'alpha'
)
)
OR ! empty( $this->type )
)
return new WP_Error(
'invalid_type'
,__( 'Invalid type set.', 'wpse70451_textdomain' )
,__FILE__
);
}
public function __toString()
{
$is_error = $this->error;
if ( ! is_wp_error( $is_error ) )
return;
// No error message for Guests or Subscribers
// Assuming that no one has activated caching plugins when debugging
// and not set WP_DEBUG to TRUE on a live site
if (
! is_user_logged_in()
AND ! current_user_can( 'edit_posts' )
AND ( ! defined( 'WP_DEBUG' ) OR ! WP_DEBUG )
)
return '';
// Error output for development
return "{$is_error->get_error_message( 'invalid_type' )}: {$is_error->get_error_data()}";
}
public function get_gallery_ids( $content )
{
$pattern = get_shortcode_regex( $content );
preg_match_all( "/{$pattern}/s", $content, $matches );
$atts = explode( " ", array_shift( $matches[3] ) );
foreach ( $atts as $att )
{
if ( strstr( $att, $this->part ) )
break;
}
preg_match_all( $this->get_regex( $this->type ), trim( $att ), $atts );
$this->atts = array_filter( $atts );
return $content;
}
// Build pattern
public function get_regex( $type )
{
switch ( $type )
{
case 'digit' :
$pattern_atts="/(\d*)/";
break;
case 'alphanumeric' :
$pattern_atts="/([A-Za-z0-9]*)/";
break;
case 'alpha' :
$pattern_atts="/([A-Za-z]*)/";
break;
default :
// Add a method name `get_pattern()` to the extending class
// to work with a custom regex pattern.
if ( method_exists( $this, 'get_pattern' ) )
{
$pattern_atts = $this->get_pattern();
break;
}
$pattern_atts = $this->get_regex( 'alphanumeric' );
break;
}
return $pattern_atts;
}
public function get_gallery_atts( $content, $atts )
{
$this->atts = $atts[ $this->part ];
// Allow overrides to trigger
// at this point we already got what we need
return $content;
}
// Must get defined in extending class
public abstract function process_atts() {}
} // END Class
} // endif;
Handle a task, get a child
Here you see the actual processing plugin. First it hooks itself statically to the init
hook and then runs the parents __construct()
method to retrieve the attributes.
Then you need to define what attributes you want to retrieve (see the classes properties $part
and $type
, which already default to what you asked for).
The last two decisions you need to make are
- What do I want to do with my result? See
process_atts()
- When do you want to output the result. See
__construct()
and whereprocess_atts()
is hooked in.
It’s as easy as that.
If you need a custom regex, just add a method named get_regex()
to your extending class and return
your custom pattern. Then set the $type
to and empty string ''
and you’re ready to go.
<?php
/** Plugin Name: (#70451) »kaiser« Get Gallery excludes */
if ( ! class_exists( 'wpse70451_extract_gallery_excludes' ) )
{
add_action( 'init', array( 'wpse70451_extract_gallery_excludes', 'init' ) );
final class wpse70451_extract_gallery_excludes extends wpse70451_extract_gallery_atts
{
public static $instance;
public $part="exclude";
public $type="digit";
public static function init()
{
is_null( self :: $instance ) AND self :: $instance = new self;
return self :: $instance;
}
public function __construct()
{
parent :: __construct();
// Example hook: `loop_end`
add_action( 'loop_end', array( $this, 'process_atts' ) );
}
public function process_atts()
{
$markup = '';
// Do something with $this->atts;
return print $markup;
}
} // END Class
} // endif;
Alter what you need and then again: Just upload this via (S)FTP into your plugins folder and activate.