Skip to content

Instantly share code, notes, and snippets.

@robertuniqid
Created June 1, 2021 23:17
Show Gist options
  • Save robertuniqid/a55df8c887ad2a3792f58cf2d27b195c to your computer and use it in GitHub Desktop.
Save robertuniqid/a55df8c887ad2a3792f58cf2d27b195c to your computer and use it in GitHub Desktop.
backup, reddit post.
Some context :
I need to do instant push notifications with one-signal when someone posts a comment, and I must ensure there's no compromise on the speed, it seems to add to the load time from 1 device to 5 devices receiving notifications, and gets slower and slower as I'm going to 100 devices, because of the way these tokens work, a user could artifically get 1000s of device tokens in onesignal ( assumption ), cleaning them up trough a cron job would be another thing I'll tackle, but later ( if someone gets here with similar requirements, a heads up )
I'm looking for suggestions, or weak points in my logic, if anyone ever encountered this, or simply want to talk about it, maybe there's a better way of approaching this, or WP Core has something better available.
Why I want to use an Async Request :
I can't work with cron jobs, because I want this to happen instantly, I'm essentially turning 1 POST request into another GET request, my other option would be to have an infinite loop somewhere always happening, and I don't know yet if that will work better than this, there might be a sweet spot somewhere, where that might run fast, and more effective.
Architecture Details :
- There will always be an action_alias ( something to identify what needs to run ) in there.
- There will always be atleast an ID in every request, if there isn't one, it's better off as a cron job most likely.
- I want this to be secure from abusing, I've went with a simple solution, md5( NONCE_SALT . $action . $id ) is my security token, and artificially creating them is impossible if someone does not know the NONCE_SALT, which in itself would be an entirelly different problem
---
I've decided to do a non-blocking HTTP Request, eventually I've reached this code :
wp_remote_get( $url, [
'timeout' => 0.01,
'blocking' => false,
'sslverify' => false
] );
The class that handles everything, is this one :
class IUA {
/**
* @var IUA
*/
protected static $instance;
/**
* @return IUA
*/
public static function instance() {
if (!isset(self::$instance))
self::$instance = new self();
return self::$instance;
}
private $_callbacks = [];
public function setup() {
if( !defined( 'MY_PREFIX_IGNORE_USER_ABORT_REQUEST' ) || !MY_PREFIX_IGNORE_USER_ABORT_REQUEST )
return;
if( md5( NONCE_SALT . $_GET[ 'action' ] . $_GET[ 'id' ] ) !== $_GET[ 'secure' ] )
exit;
add_action( "init", [ $this, '_init' ], 50 );
}
public function _init() {
if( !isset( $this->_callbacks[ $_GET[ 'action' ] ] ) )
exit;
$request_data = $_GET;
unset( $request_data[ 'action' ] );
unset( $request_data[ 'secure' ] );
call_user_func( $this->_callbacks[ $_GET[ 'action' ] ], $request_data );
exit;
}
public function register( $alias, $callback ) {
$this->_callbacks[ $alias ] = $callback;
}
public function request( $action, $id, $request_data = [] ) {
$url = MY_PLUGIN_BASE_URL_PATH . '/ignore-user-abort.php';
$url = add_query_arg( 'action', $action, $url );
$url = add_query_arg( 'id', $id, $url );
$url = add_query_arg( 'secure', md5( NONCE_SALT . $action . $id ), $url );
if( is_array( $request_data ) && !empty( $request_data ) )
foreach( $request_data as $key => $value )
$url = add_query_arg( $key, $value, $url );
wp_remote_get( $url, [
'timeout' => 0.01,
'blocking' => false,
'sslverify' => false
] );
}
}
The URL is pointing to a file called ignore-user-abort.php which is located in the plugin file.
Now, because I don't know how to safely get to the central index.php file, I've went ahead and came up with a hack, sort-off, this is the part I'm actually most concerned about, seems to work.
File : ignore-user-abort.php
// The 3 core parameters of this functionality, it is intended to be used async in PHP, and do extensive requests without stopping the user request.
// An ID is central for any action, and we'll use it to add a security layer to the request.
ignore_user_abort( true );
if( !isset( $_GET[ 'action' ] )
|| !isset( $_GET[ 'id' ] )
|| !isset( $_GET[ 'secure' ] ) )
exit;
$dirname = dirname( __FILE__ );
while( $dirname !== '/' ) {
// We aim to find the wp-login.php, as the wp-config could be hidden on certain hosts, and we're aiming for the index.php either way.
if( file_exists( $dirname . '/wp-login.php' ) )
break;
$dirname = dirname( $dirname );
}
if( $dirname === '/' && !file_exists( $dirname . 'wp-login.php' ) )
exit;
if( !file_exists( rtrim( $dirname, '/' ) . '/index.php' ) )
exit;
define( "MY_PREFIX_IGNORE_USER_ABORT_REQUEST", true );
require_once( rtrim( $dirname, '/' ) . '/index.php' );
exit;
To register an async request :
IUA::instance()->register( 'push_one_signal', [ $this, '_iua_push_notifications' ] );
To Trigger an async request :
IUA::instance()->request( 'push_one_signal', $my_id );
Discarded Ideas :
- I've initially wanted to hook directly into the wp-cron.php, but seems risky, as it would fail if a cron job is running, or can potentially mess up with the cron jobs.
- Initially wanted to rely on add_action & do_action & has_action, but I've decided that I might need more than an alias later, and went for the function.
Thank you
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment