Actions, filters and...?

What is the good way to share an event with other plugins?

Example scenario: some function in my plugin saves an order. I want to allow people to know there have been a new order, but they can change nothing in my process.
Way to do this:

  • do_action( 'new-order' , $my_order_details )
  • apply_filters( 'new-order' , $my_order_details )

Is there a preferred way? In both I don’t care and don’t want that another plugin output something or change something.

Or are we missing a third hook?
Something like signal_event( 'my-event-handle', $data_about_it ) that cleans any output from the function hooked at this.

Simone.

If you don’t want them to change anything, then you definitely do NOT want to use apply_filters. That is precisely for changing something.

The do_action hook is precisely what you want. Then another plugin can hook into that and fire off whatever functions it wants in response.

The real issue here is what variables you decide to expose with the do_action hook. In principle, the more you expose, the more flexible it becomes.

2 Likes

Exactly what Tim said. :slight_smile:

Also, you didn’t specify if you wanted the people to be notified that there was an order, or if you wanted them to be able to learn there is an order. This would affect the approach taken.

If you don’t want to use an action or a filter, maybe you could store “just enough” data in an option or transient and let other code poll for new orders from there.

3 Likes

Not sure if I get the idea right, as it seems that signal_event() would work absolutely the same as do_action(). It doesn’t apply any changes to a passed variable anyway. But when you set an event trigger this means that you allow other plugins to run their custom code during your execution process. This code doesn’t change your process, but it certainly can generate some output and I don’t see the way to prevent it from your hook. Is there a real need to trigger that event? Maybe it’s possible to save your result in an option and then provide a hook that fires after your output is finished?

2 Likes

Thank you @timkaye and @anon71687268!

This is exactly my concern!

@anon71687268 the real scenario is the hook for statistics in Update-Manager we are discussing.

Example e-commerce plugin:

register_order( $customer, $item, $credit_card_id );
greet_customer( $customer );
do_action( 'new-order', $customer, $item );

Example companion plugin:

add_action( 'new-order', 'alert_me' );
function alert_me( $customer, $item){
   send_sms("Hey man, prepare $item for $customer"); //right
   echo "Hey $customer, not good shopping"; // not right
}

So as I can understand do_action() is the right approach hoping that nobody “echoes” something.

1 Like

Maybe it could be solved by creating a predefined function structure instead of trying to disallow particular things like output. In this case your alert function will remain a part of your code and you can just allow to filter some separate options by apply_filters(). Kinda this way:

// Your plugin
...
function alert_me( $customer, $item ) {
	// What contact methods are available (in general)
	$allowed_methods = array(
		'send_sms' => array(
			// This property can be overriden
			'text' => apply_filters( 'sms_text', "Hey man, prepare $item for $customer", $item, $customer ),
			// This property can be overriden
			'some_property' =>  apply_filters('some_property', 'some_value' ),
		),
		'one_more_method' => array(),
	);

	// What contact methods are used (can be redefined by a companion plugin, too)
	$methods = apply_filters( 'methods', array( 'send_sms' ) );
	
	// Predefined process: sending SMS (if needed)
	if ( array_key_exists( 'send_sms', $methods ) ) {
		$props = $allowed_methods[ 'send_sms' ];
		send_sms( $props['text'], $props[ 'some_property'] );
	}

	// Predefined process: using another method (if needed)
	if ( array_key_exists( 'one_more_method', $methods ) ) {
		one_more_method();
	}

	// Do something else...

}

register_order( $customer, $item, $credit_card_id );
greet_customer( $customer );
alert_me( $customer, $item );

// Companion plugin:

// Choosing contact methods
function my_methods() {
	return array( 'send_sms', 'one_more_method' );
};

// Overriding SMS text
function my_sms_text( $text, $item, $customer ) {
	return  "$item for $customer! NOW!";
};

add_filter( 'methods', 'my_methods' );
add_filter( 'sms_text', 'my_sms_text' );
2 Likes

A little off-topic of the question asked, but, I wanted to add a note about best practices when creating new hooks (whether they’re actions or filters.) If you give them a prefix, it creates a nice safeguard to isolate your hooks against collisions. For example, the following hook:

add_action( 'new-order', 'alert_me' );

…is somewhat generic and might be found in any number of other plugins. Adding a prefix ensures that your plugin won’t be the culprit if there’s a collision.

add_action( 'your-prefix-new-order', 'alert_me' );

1 Like

You can also prefix actions like namespaces
‘someplugin/someaction’

3 Likes

I usually use underscored prefixes do avoid collisions. so ie. all of my UI-based plugins use “ui(plugin_acronym)_” as a prefix. Double underscore might help, too.

1 Like