GithubHelp home page GithubHelp logo

wp-async-task's Introduction

TechCrunch WP Asynchronous Tasks

TechCrunch WP Asynchronous Tasks plugin for TechCrunch.com

Quick Start

WP Async Task can be installed as a plugin or bundled in other plugins or a theme. The class definition is wrapped in a class_exists check, so it will never run the risk of being accidentally defined twice. Just make sure that the plugin file is being included somehow.

Next, you need to extend the class with your own implementation. Implementations of the class act on an arbitrary action (e.g., 'save_post', etc). There are three parts that must be present in any class extending WP_Async_Task:

  1. A protected $action property
  2. A protected prepare_data() method
  3. A protected run_action() method
<?php

class JPB_Async_Task extends WP_Async_Task {

	protected $action = 'save_post';

	/**
	 * Prepare data for the asynchronous request
	 *
	 * @throws Exception If for any reason the request should not happen
	 *
	 * @param array $data An array of data sent to the hook
	 *
	 * @return array
	 */
	protected function prepare_data( $data ) {}

	/**
	 * Run the async task action
	 */
	protected function run_action() {}

}

$action

The protected $action property should be set to the action to which you wish to attach the asynchronous task. For example, if you want to spin off an asynchronous task whenever a post gets saved, you would set this to save_post.

prepare_data( $data )

Use this method to prepare the action's data for use in the asynchronous process. Data will be given to prepare_data() as an indexed array, just as it would if you used func_get_args() to get a function's arguments. This method needs to return an array containing the data in a more useful format. Since these values will be sent in a POST request, it's advisable to stick to scalar values for the most part. For example, on 'save_post', the action provides $post_id and the $post object, so we might do this:

protected function prepare_data($data){
	$post_id = $data[0];
	return array( 'post_id' => $post_id );
}

If for any reason the asynchronous task needs to be canceled, you will need to throw an exception:

protected function prepare_data($data){
	$post_id = $data[0];
	$post = $data[1];
	if( 'post' !== $post->post_type ) {
		throw new Exception( 'We only want async tasks for posts' );
	}
	return array( 'post_id' => $post_id );
}

The library will handle catching the exception and will prevent the request from running if it catches an Exception.

run_action()

This method is responsible for running whatever action should trigger the functions that need to run inside the asynchronous request. The convention is to use "wp_async_$this->action", but that is up to the implementation.

protected function run_action() {
	$post_id = $_POST['post_id'];
	$post = get_post( $post_id );
	if ( $post ) {
		// Assuming $this->action is 'save_post'
		do_action( "wp_async_$this->action", $post->ID, $post );
	}
}

Make sure that you instantiate your asynchronous task once. Do this no earlier than the 'plugins_loaded' action.

Finally, update the action of any tasks that you wish to move to the asynchronous task.

For example, you might change this:

add_action( 'save_post', 'really_slow_process', 10, 2 );

to this:

add_action( 'wp_async_save_post', 'really_slow_process', 10, 2 );

Contributing

To contribute, please fork the github repository and submit a pull request.

When submitting pull requests, please make sure your changes do not cause any unit tests to fail. To run the unit test suite, make sure you've installed composer and install the test tools by running

composer install

After you've installed the dev tools, run the unit tests by running

vendor/bin/phpunit

Copyright

ยฉ TechCrunch 2014

License

This library is licensed under the MIT license. See LICENSE.md for more details.

wp-async-task's People

Contributors

ericmann avatar jdgrimes avatar johnpbloch avatar nicolqs avatar r-a-y avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wp-async-task's Issues

Use a custom Exception child to signal a return from prepare_data() and/or logger

In the comments on the announcement post, Shaun Harding brought up the point that it's a bit counter intuitive to use an exception to tell the runner not to create an async task:

No offense... but this is really annoying...

try {
  $data = $this->prepare_data( $data );
} catch ( Exception $e ) {
  return;
}

If you're going to throw an exception, and go through the trouble of catching it, don't ignore it...

He followed up with another good point:

The intent may not be to catch errors, but should an exception get thrown due to an error, it will be caught and promptly ignored. If you're only looking to catch a very specific type of exception, then a specific type of exception should be defined. If you really must catch all exceptions, then logging the exception would be nice. There's no xdebug.scream to undo that suppression.

Let's use this issue thread to discuss the merits of the approaches he's suggesting:

  1. Use a custom Exception child class, or
  2. Add some sort of logging to the process that will log the exceptions that occur

We could even use both together to allow the logger to differentiate between different types of exceptions but still catch all types.

Can you submit the plugin to Packagist.org?

Hi,

I see that the composer.json file is already exists for the project.
Can I suggest adding the plugin to Packagist.org so we can use it with composer.json file in our projects?

Is wp async a good fit

Wondering if wp async is right choice. I have a plugin that fetches data initiated by user, from external source, I take array of records and create custom post ty

Readme should note that the extending class should be initialized

In the Quick Start section of the README.md, there should be a note saying that the extended class should be initialized.

For example:

new JPB_Async_Task;

It's a small thing, but people can over look that ๐Ÿ˜„

Update:

I see that it's noted on this line:

Make sure that you instantiate your asynchronous task once. Do this no earlier than the 'plugins_loaded' action.

Maybe add a code example?

function jpb_async_task_init() {
    new JPB_Async_Task;
}
add_action( 'plugins_loaded', 'jpb_async_task_init' );

I would normally write this as an anonymous function, but WP supports a minimum of PHP 5.2.4.

Questions about environment dependencies

Hi, I'm just getting started playing with this library. One thing I've noticed is that in my local development environment (a more or less stock VIP Quickstart Vagrant box on a Macbook Pro), the post to admin-post.php always fails with the 0.01s timeout in the plugin.

Is there a reason why this needs to be set so low? The request is non-blocking anyways, Is there any reason a default timeout can't work?

In addition, the only way I could make the admin-post callback complete was by adding @ignore_user_abort( true ); to my local admin-post.php (or the equivalent value in php.ini, though that's even sketchier...). Is this a requirement for the plugin to work, and if so, should it be documented somewhere?

thumbs are coming with blank id

Hello,
@nicolqs
we are using this class and its working just fine for main image when we upload through media but not working for thumbs generated by media.

we are hooking like this wp_async for
add_action( 'wp_async_wp_generate_attachment_metadata', array( &$this, 'media_uploader_callback' ) );
add_filter( 'wp_generate_attachment_metadata', array( &$this, 'thumbnails' ) );

I tried adding wp_async to add_filter but then nothing is coming to remote server. so is there any way? as main image works fine with first hook

i can see with var_dump that full data is available including thumbs but only main image are going to remote server and checked with file put contents and i can see that thumbs are not coming as thumbs id were empty

wp_sync...action hook not being executed as async

Hi there, this plugin seems useful, but doesn't work for me in WP v4.7.5.

I'm trying to get a action hook from another plugin running asynchronously. This is the action hook I'm trying to run asynchronously https://www.gravityhelp.com/documentation/article/gform_after_submission/

I tried to incorporate that hook using similar steps described in the plugin's Github readme.

To just test the gform_after_submission_19 (where form id=19) action this is what I have added in my theme's function.php after activating the TechCrunch's plugin

if (class_exists('WP_Async_Task')) {

    class GF_Async_Task extends WP_Async_Task {
        
        protected $action = 'gform_after_submission_19';

        public function __construct() {
            parent::__construct(parent::BOTH);
        }
        protected function prepare_data($data) {
            return array(
                'entry' => $data[0],
                'form' => $data[1]
            );
        }
        protected function run_action() {
            if (isset($_POST['entry'])) {
                do_action("wp_async_$this->action", $_POST['entry'], $_POST['form']);
            }
        }
    }
}
function send_custom_notification($entry, $form) {
    error_log("test", 3, $_SERVER['DOCUMENT_ROOT'] . "/GFTEST.log");
}

add_action('wp_async_gform_after_submission_19', 'send_custom_notification', 10, 2);

function my_init_gf_task() {
    new GF_Async_Task();
}
add_action('plugins_loaded', 'my_init_gf_task');

But when I post the gravity form the expected action send_custom_notification action is just not doing the log
What could be wrong?

I also tried to set the sslverify option to false in the the launch_on_shutdown method of WP_Async_Task class by the editing original plugin file wp-async-task.php. I may needed to do this as i'm working on localhost for now.

But still no luck, any help/tips will be much appreciated, thanks!

not working the code

Thanks for the solution , i am using this code to run clear cache feature when saving a post in wordpress. But my run_action () not executing .Can you please help:
This is my code in functions.php
include 'wp-async-task.php';

class ApiStatusTask extends WP_Async_Task {

protected $action = 'save_post';

/**

  • Prepare data for the asynchronous request
  • @throws Exception If for any reason the request should not happen
  • @param array $data An array of data sent to the hook
  • @return array
    */
    protected function prepare_data( $data ) {
    return array(
    'api_url' => $data[0]
    );
    }

/**

  • Run the async task action
    */
    protected function run_action() {
    if(isset($POST['api_url'])){
    do_action("wp_async
    $this->action", $_POST['api_url']);
    }
    // cache clear feature
    }

}

add_action( 'wp_async_save_post', 'josh_send_to_api' );
function josh_send_to_api( $id ) {
wp_mail("[email protected]","subject","Content")
}

run_action() doesn't seem to be invoked.

Hi there,

I implemented my own async task and couldn't see it's working, so I tried to echo stuff in run_action method but didn't see the output, not in ajax request response either. Here's my implementation. prepare_data seems fine though.

<?php

class My_Async_Task extends WP_Async_Task
{
    protected $action = 'acf/save_post';

    protected function prepare_data($data)
    {
        $postId = $data[0];
        $post = get_post($postId);

        if ('post' == $post->post_type) {
            return array('post_id' => $postId);
        }

        return;
    }

    protected function run_action()
    {
        echo 'test output'; exit;

        $postId = $_POST['post_id'];
        do_action("wp_async_$this->action", $postId);
    }
}

README example mistake

Just a little mistake in README.md

/**
 * Run the async task action
 */
protected function run_action( $data ) {}

should be changed to

/**
 * Run the async task action
 */
protected function run_action() {}

Because run_action should match the abstract class definition _ (with no parameter)_

abstract protected function run_action();

Task is not executed as async.

Hello I'm using your package to implement async tasks.The basic idea is that I'm giving the option to the admin from the dashboard to create a post with some related videos. The videos are stored on the server but when he creates the post I want to move the videos that he selected to another platform and after the upload finish to save the response on a table with a foreign key of the post ID.
My problem is that instead of saving the post and then create an async task to upload the video on the platform it keeps wait for the video to be uploaded and then to refresh the page.

Here is my code

function init_async_task() {
    if (!class_exists('Upload_Videos_Async', false)) {
        new Upload_Videos_Async(WP_Async_Task::LOGGED_IN);
    }
}

add_action('plugins_loaded', 'init_async_task');
add_action('wp_async_save_post', 'move_videos_to_platform', 10, 2);
function move_videos_to_platform($id) {
//upload here the videos
}



class Upload_Videos_Async extends WP_Async_Task {

    protected $action = 'save_post';

    protected $argument_count = 1;

    public function __construct($auth_level = 0) {
        parent::__construct(parent::LOGGED_IN);
    }

    /**
     * Prepare data for the asynchronous request
     *
     * @throws Exception If for any reason the request should not happen
     *
     * @param array $data An array of data sent to the hook
     *
     * @return array
     */
    protected function prepare_data($data) {
        $returnData = ['post_id' => $data[0]];
        $files = [];
        $http = herbert('http');
        if ($http->has('file_name')) {

            foreach ($http->get('file_name') as $key => $value) {
                $tempKey = str_replace('.mp4', '', $value);
                $files[$tempKey] = $value;
            }
        }

        if (count($files) > 0) {
            $returnData['file_name'] = http_build_query($files);
        }
        return $returnData;
    }

    /**
     * Run the async task action
     */
    protected function run_action() {
        $http = herbert('http');
        $post = get_post($http->get('post_id'));
        if ($post) {
            if ($post->post_type == 'futures-video')
            // Assuming $this->action is 'save_post'
            {
                if ($http->has('file_name')) {
                    $files = [];
                    foreach (glob($_SERVER['DOCUMENT_ROOT'] . '/*.mp4') as $file) {
                        $tempKey = str_replace('.mp4', '', basename($file));
                        $tempKey = str_replace('.', '_', $tempKey);
                        $files[$tempKey] = $file;
                    }

                    $urlFiles = [];
                    parse_str($http->get('file_name'), $urlFiles);
                    $count = 0;
                    foreach ($urlFiles as $key => $filepath) {
                        $key = array_search(basename($filepath), $urlFiles);
                        if ($key !== false) {
                            $videos = new Videos;
                            $videos->name = $key;
                            $videos->post_id = $post->ID;
                            $videos->status = 'pending';
                            $videos->save();
                            $count++;
                        }
                    }
                    if ($count > 0) {
                        do_action("wp_async_$this->action", $post->ID, $post);
                    }
                }
            }
        }
    }

}

At the end if the videos didn't uploaded for any reason I want to execute the same tasks to re-upload them. How I can call the async task?
any help?

How to use this, exactly?

Hey, great code, thanks!

I'm having some trouble using it, though. Here's a quick test I'm doing:

add_action( 'wp_async_shutdown', [$this, 'testing_async'], 10, 1 );

I expect this to run public function testing_async() asynchronously when WordPress be shutting down, right?

public function testing_async() {
    error_log('test', 1, '[email protected]');
}

But this is never called. OK, this didn't work. Let's try the "extend class" approach.

public function testing_async() {
    $test = new WP_Async_Test();
}

WP_Async_Test.php:

<?php

class WP_Async_Test extends WP_Async_Task {

    //protected $action = 'shutdown';
    protected $action = 'testing_async';

    /**
     * Prepare data for the asynchronous request
     *
     * @throws Exception If for any reason the request should not happen
     *
     * @param array $data An array of data sent to the hook
     *
     * @return array
     */
    protected function prepare_data( $data ) {

    }

    /**
     * Run the async task action
     */
    protected function run_action() {
        error_log('hi', 1, '[email protected]');
    }

}

This didn't work either... What am I doing wrong?

$_COOKIE serialization

I am using your class to implement a queue which will run one job per process and then loop on itself by making a remote non blocking call.

I was facing an issue that was very painful to debug because Apache was simply dropping the request with the following message:
POST /wp-admin/admin-post.php HTTP/1.1" 400 306 "-" "-" "Size of a request header field exceeds server limit.<br />\n<pre>\ncookie\n</pre>/n

I our case we forward cookies from one async call to another. The fact that you serialize the $_COOKIE value that are arrays, https://github.com/techcrunch/wp-async-task/blob/master/wp-async-task.php#L132, results in a header size that keeps increasing:
%5C%5C%5C%5C%5C%5C%5C%5C%5C%5C%5C%5C...

Any reason you added this serialization? Does not seem to be handled natively by WP, right?

Do you think of a serialization that would scale in our case? For now we just ignore non string values, which is sufficient.

Great lib, keep it up!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.