Файловый менеджер - Редактировать - /var/www/xthruster/html/wp-content/uploads/flags/techcrunch.tar
Назад
wp-async-task/README.md 0000755 00000007725 14721521766 0010555 0 ustar 00 # 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 <?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: ```php 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: ```php 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. ```php 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: ```php add_action( 'save_post', 'really_slow_process', 10, 2 ); ``` to this: ```php 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](https://getcomposer.org/doc/00-intro.md) and install the test tools by running ```sh composer install ``` After you've installed the dev tools, run the unit tests by running ```sh vendor/bin/phpunit ``` ## Copyright © TechCrunch 2014 ## License This library is licensed under the [MIT](http://opensource.org/licenses/MIT) license. See LICENSE.md for more details. wp-async-task/phpunit.xml.dist 0000755 00000000425 14721521766 0012437 0 ustar 00 <phpunit bootstrap="./bootstrap.php.dist" colors="true"> <testsuites> <testsuite> <directory suffix="Test.php">./tests/phpunit/</directory> </testsuite> </testsuites> <filter> <whitelist> <file>./wp-async-task.php</file> </whitelist> </filter> </phpunit> wp-async-task/bootstrap.php.dist 0000755 00000000160 14721521766 0012750 0 ustar 00 <?php require_once __DIR__ . '/vendor/autoload.php'; WP_Mock::setUsePatchwork( false ); WP_Mock::bootstrap(); wp-async-task/composer.json 0000755 00000001447 14721521766 0012013 0 ustar 00 { "name" : "techcrunch/wp-async-task", "description" : "Run asynchronous tasks for long-running operations in WordPress", "minimum-stability": "dev", "license" : "MIT", "type" : "wordpress-plugin", "authors" : [ { "name": "Alex Khadiwala", "role": "developer" }, { "name": "Nicolas Vincent", "role": "developer" }, { "name" : "Eric Mann", "email": "eric.mann@10up.com", "role" : "developer" }, { "name" : "John P. Bloch", "email": "john.bloch@10up.com", "role" : "developer" } ], "require-dev" : { "10up/wp_mock" : "dev-master", "phpunit/phpunit": "*@stable" }, "autoload" : { "classmap": ["wp-async-task.php"] }, "autoload-dev" : { "classmap": ["tests/phpunit/mocks/"] } } wp-async-task/tests/phpunit/mocks/Async.php 0000755 00000000233 14721521766 0015014 0 ustar 00 <?php class Async extends BaseAsync { public function __construct( $init = parent::BOTH ) { if ( $init ) { parent::__construct( $init ); } } } wp-async-task/tests/phpunit/mocks/EmptyAsync.php 0000755 00000000113 14721521766 0016030 0 ustar 00 <?php class EmptyAsync extends BaseAsync { protected $action = null; } wp-async-task/tests/phpunit/mocks/BaseAsync.php 0000755 00000000247 14721521766 0015614 0 ustar 00 <?php class BaseAsync extends WP_Async_Task { protected $action = 'async'; protected function prepare_data( $data ) { } protected function run_action() { } } wp-async-task/tests/phpunit/WP-Async-TaskTest.php 0000755 00000031666 14721521766 0016002 0 ustar 00 <?php use WP_Mock\Tools\TestCase; class WP_Async_Task_Tests extends TestCase { /** * Set up some test mocks */ public function setUp() { parent::setUp(); $_COOKIE = array(); $_POST = array(); } public function tearDown() { parent::tearDown(); $_COOKIE = array(); $_POST = array(); } /** * Test that the correct actions are registered based on the auth level */ public function test_auth_level_both() { $async = new Async( false ); WP_Mock::expectActionAdded( 'async', array( $async, 'launch' ), 10, 20 ); WP_Mock::expectActionAdded( 'admin_post_wp_async_async', array( $async, 'handle_postback' ) ); WP_Mock::expectActionAdded( 'admin_post_nopriv_wp_async_async', array( $async, 'handle_postback' ) ); $async->__construct( WP_Async_Task::BOTH ); $this->assertConditionsMet(); } /** * Test that the correct actions are registered based on the auth level */ public function test_auth_level_logged_in_only() { $async = new Async( false ); WP_Mock::expectActionAdded( 'async', array( $async, 'launch' ), 10, 20 ); WP_Mock::expectActionAdded( 'admin_post_wp_async_async', array( $async, 'handle_postback' ) ); $async->__construct( WP_Async_Task::LOGGED_IN ); $this->assertConditionsMet(); } /** * Test that the correct actions are registered based on the auth level */ public function test_auth_level_logged_out_only() { $async = new Async( false ); WP_Mock::expectActionAdded( 'async', array( $async, 'launch' ), 10, 20 ); WP_Mock::expectActionAdded( 'admin_post_nopriv_wp_async_async', array( $async, 'handle_postback' ) ); $async->__construct( WP_Async_Task::LOGGED_OUT ); $this->assertConditionsMet(); } /** * Test that the constructor throws an Exception if action is undefined * * @expectedException \Exception */ public function test_empty_action() { new EmptyAsync(); } /** * Test that throwing an Exception in prepare_data stops a postback from firing */ public function test_exception_stops_launch_sequence() { $async = $this->getMockAsync( 'Async', array( 'prepare_data', 'create_async_nonce' ) ); $arg1 = rand( 0, 9 ); $arg2 = rand( 10, 99 ); $async->shouldReceive( 'prepare_data' ) ->once() ->with( array( $arg1, $arg2 ) ) ->andThrow( 'Exception' ); $async->shouldReceive( 'create_async_nonce' )->never(); /** @var Async $async */ $async->launch( $arg1, $arg2 ); $this->assertConditionsMet(); } /** * Test that launch sets the correct action and _nonce values in the body */ public function test_launch() { $async = $this->getMockAsync( 'Async', array( 'prepare_data', 'create_async_nonce' ) ); $arg = 'arg' . rand( 0, 9 ); $async->shouldReceive( 'prepare_data' ) ->once() ->with( array( $arg ) ) ->andReturn( array( 'foo' => $arg ) ); $nonce = substr( md5( 'async' . rand( 0, 9 ) ), - 12, 10 ); $async->shouldReceive( 'create_async_nonce' ) ->once() ->with() ->andReturn( $nonce ); $body_data = new ReflectionProperty( 'Async', '_body_data' ); $body_data->setAccessible( true ); WP_Mock::wpFunction( 'has_action', array( 'times' => 1, 'args' => array( 'shutdown', array( $async, 'launch_on_shutdown' ) ), 'return' => false, ) ); WP_Mock::expectActionAdded( 'shutdown', array( $async, 'launch_on_shutdown' ) ); /** @var Async $async */ $async->launch( $arg ); $data = $body_data->getValue( $async ); $this->assertArrayHasKey( 'action', $data ); $this->assertEquals( 'wp_async_async', $data['action'] ); $this->assertArrayHasKey( '_nonce', $data ); $this->assertEquals( $nonce, $data['_nonce'] ); $this->assertConditionsMet(); } public function test_launch_on_shutdown() { $async = $this->getMockAsync( 'Async', array( 'prepare_data', 'create_async_nonce' ) ); $async->shouldReceive( 'prepare_data' )->andReturn( array() ); $async->shouldReceive( 'create_async_nonce' )->andReturn( 'asdf' ); WP_Mock::wpFunction( 'maybe_serialize', array( 'return' => function ( $thing ) { return is_scalar( $thing ) ? $thing : serialize( $thing ); } ) ); $_COOKIE = array( '_some_cookie' => 'Value', 'foo' => 'bar', 'random' => rand( 0, 999999 ), 'array' => array( 'not', 'scalar' ), ); $cookie_header = ''; array_walk( $_COOKIE, function ( $value, $key ) use ( &$cookie_header ) { if ( ! empty( $cookie_header ) ) { $cookie_header .= '; '; } if ( ! is_scalar( $value ) ) { $value = serialize( $value ); } $cookie_header .= "$key=" . urlencode( $value ); } ); $verify_ssl = (bool) rand( 0, 1 ); WP_Mock::onFilter( 'https_local_ssl_verify' )->with( true )->reply( $verify_ssl ); WP_Mock::wpFunction( 'admin_url', array( 'times' => 1, 'args' => array( 'admin-post.php' ), 'return' => $url = 'https://tctechcrunch2011.wordpress.com/wp-admin/admin-post.php' ) ); WP_Mock::wpFunction( 'wp_remote_post', array( 'times' => 1, 'args' => array( $url, array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => $verify_ssl, 'body' => array( 'action' => 'wp_async_async', '_nonce' => 'asdf', ), 'headers' => array( 'cookie' => $cookie_header, ), ) ), ) ); /** @var Async $async */ $async->launch(); // to set up body data, etc. $async->launch_on_shutdown(); $this->assertConditionsMet(); } public function test_launch_on_shutdown_empty_body() { WP_Mock::wpFunction( 'wp_remote_post', array( 'times' => 0, ) ); /** @var Async $async */ $async = $this->getMockAsync(); $async->launch_on_shutdown(); $this->assertConditionsMet(); } public function test_handle_postback_nonce_not_set() { $async = $this->getMockAsync( 'Async', array( 'verify_async_nonce', 'run_action' ) ); $async->shouldReceive( 'verify_async_nonce' )->never(); $async->shouldReceive( 'run_action' )->never(); WP_Mock::expectFilterAdded( 'wp_die_handler', function () { die(); } ); WP_Mock::wpFunction( 'wp_die', array( 'times' => 1 ) ); /** @var Async $async */ $async->handle_postback(); $this->assertConditionsMet(); } public function test_handle_postback_invalid_nonce() { $async = $this->getMockAsync( 'Async', array( 'verify_async_nonce', 'run_action' ) ); $nonce = 'asdfasdf'; $_POST['_nonce'] = $nonce; $async->shouldReceive( 'verify_async_nonce' ) ->once() ->with( $nonce ) ->andReturn( false ); $async->shouldReceive( 'run_action' )->never(); WP_Mock::expectFilterAdded( 'wp_die_handler', function () { die(); } ); WP_Mock::wpFunction( 'wp_die', array( 'times' => 1 ) ); /** @var Async $async */ $async->handle_postback(); $this->assertConditionsMet(); } public function test_handle_postback_anon() { $async = $this->getMockAsync( 'Async', array( 'verify_async_nonce', 'run_action' ) ); $nonce = 'asdfasdf'; $_POST['_nonce'] = $nonce; $async->shouldReceive( 'verify_async_nonce' ) ->once() ->with( $nonce ) ->andReturn( true ); WP_Mock::wpFunction( 'is_user_logged_in', array( 'times' => 1, 'return' => false, ) ); $async->shouldReceive( 'run_action' ) ->once() ->with(); WP_Mock::expectFilterAdded( 'wp_die_handler', function () { die(); } ); WP_Mock::wpFunction( 'wp_die', array( 'times' => 1 ) ); /** @var Async $async */ $async->handle_postback(); $action = new ReflectionProperty( 'Async', 'action' ); $action->setAccessible( true ); $this->assertEquals( 'nopriv_async', $action->getValue( $async ) ); $this->assertConditionsMet(); } public function test_handle_postback() { $async = $this->getMockAsync( 'Async', array( 'verify_async_nonce', 'run_action' ) ); $nonce = 'asdfasdf'; $_POST['_nonce'] = $nonce; $async->shouldReceive( 'verify_async_nonce' ) ->once() ->with( $nonce ) ->andReturn( true ); WP_Mock::wpFunction( 'is_user_logged_in', array( 'times' => 1, 'return' => true, ) ); $async->shouldReceive( 'run_action' ) ->once() ->with(); WP_Mock::expectFilterAdded( 'wp_die_handler', function () { die(); } ); WP_Mock::wpFunction( 'wp_die', array( 'times' => 1 ) ); /** @var Async $async */ $async->handle_postback(); $action = new ReflectionProperty( 'Async', 'action' ); $action->setAccessible( true ); $this->assertEquals( 'async', $action->getValue( $async ) ); $this->assertConditionsMet(); } public function test_create_async_nonce() { $async = $this->getMockAsync(); $nonce_tick = rand( 10, 99 ); WP_Mock::wpFunction( 'wp_nonce_tick', array( 'times' => 1, 'args' => array(), 'return' => $nonce_tick, ) ); $create_nonce = new ReflectionMethod( 'Async', 'create_async_nonce' ); $create_nonce->setAccessible( true ); $expected_hash = md5( $nonce_tick . 'wp_async_async' . get_class( $async ) ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 1, 'args' => array( $nonce_tick . 'wp_async_async' . get_class( $async ), 'nonce' ), 'return' => $expected_hash, ) ); $this->assertEquals( substr( $expected_hash, - 12, 10 ), $create_nonce->invoke( $async ) ); $this->assertConditionsMet(); } public function test_verify_async_nonce_invalid() { $async = $this->getMockAsync(); $nonce_tick = rand( 10, 99 ); WP_Mock::wpFunction( 'wp_nonce_tick', array( 'times' => 1, 'args' => array(), 'return' => $nonce_tick, ) ); $verify_nonce = new ReflectionMethod( 'Async', 'verify_async_nonce' ); $verify_nonce->setAccessible( true ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 2, 'return' => md5( rand( 100, 999 ) ), ) ); $this->assertFalse( $verify_nonce->invoke( $async, md5( $nonce_tick ) ) ); $this->assertConditionsMet(); } public function test_verify_async_nonce_recent() { $async = $this->getMockAsync(); $nonce_tick = rand( 10, 99 ); WP_Mock::wpFunction( 'wp_nonce_tick', array( 'times' => 1, 'args' => array(), 'return' => $nonce_tick, ) ); $verify_nonce = new ReflectionMethod( 'Async', 'verify_async_nonce' ); $verify_nonce->setAccessible( true ); $hash = md5( $nonce_tick . 'wp_async_async' . get_class( $async ) ); $nonce = substr( $hash, - 12, 10 ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 1, 'args' => array( $nonce_tick . 'wp_async_async' . get_class( $async ), 'nonce' ), 'return' => function ( $thing ) { return md5( $thing ); } ) ); $this->assertSame( 1, $verify_nonce->invoke( $async, $nonce ) ); $this->assertConditionsMet(); } public function test_verify_async_nonce_old_but_valid() { $async = $this->getMockAsync(); $nonce_tick = rand( 10, 99 ); $real_tick = $nonce_tick - 1; WP_Mock::wpFunction( 'wp_nonce_tick', array( 'times' => 1, 'args' => array(), 'return' => $nonce_tick, ) ); $verify_nonce = new ReflectionMethod( 'Async', 'verify_async_nonce' ); $verify_nonce->setAccessible( true ); $hash = md5( $real_tick . 'wp_async_async' . get_class( $async ) ); $nonce = substr( $hash, - 12, 10 ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 1, 'args' => array( $nonce_tick . 'wp_async_async' . get_class( $async ), 'nonce' ), 'return' => function ( $thing ) { return md5( $thing ); } ) ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 1, 'args' => array( $real_tick . 'wp_async_async' . get_class( $async ), 'nonce' ), 'return' => function ( $thing ) { return md5( $thing ); } ) ); $this->assertSame( 2, $verify_nonce->invoke( $async, $nonce ) ); $this->assertConditionsMet(); } public function test_verify_async_nonce_nopriv() { $async = $this->getMockAsync(); $nonce_tick = rand( 10, 99 ); WP_Mock::wpFunction( 'wp_nonce_tick', array( 'times' => 1, 'args' => array(), 'return' => $nonce_tick, ) ); $action = new ReflectionProperty( 'Async', 'action' ); $action->setAccessible( true ); $action->setValue( $async, 'nopriv_async' ); $verify_nonce = new ReflectionMethod( 'Async', 'verify_async_nonce' ); $verify_nonce->setAccessible( true ); $hash = md5( $nonce_tick . 'wp_async_async' . get_class( $async ) ); $nonce = substr( $hash, - 12, 10 ); WP_Mock::wpFunction( 'wp_hash', array( 'times' => 1, 'args' => array( $nonce_tick . 'wp_async_async' . get_class( $async ), 'nonce' ), 'return' => function ( $thing ) { return md5( $thing ); } ) ); $this->assertSame( 1, $verify_nonce->invoke( $async, $nonce ) ); $this->assertConditionsMet(); } /** * Get a mock object for the async task class * * @param string $class The name of the class to mock * @param array $methods Which methods to mock * @param mixed $auth The auth level to simulate * * @return \Mockery\Mock */ private function getMockAsync( $class = 'Async', array $methods = array(), $auth = false ) { $stub = ''; if ( ! empty( $methods ) ) { $stub = '[' . implode( ',', $methods ) . ']'; } $mockClass = "$class$stub"; /** @var \Mockery\Mock $mock */ $mock = Mockery::mock( $mockClass, array( $auth ) ); $mock->makePartial(); $mock->shouldAllowMockingProtectedMethods(); return $mock; } } wp-async-task/LICENSE.md 0000755 00000002064 14721521766 0010671 0 ustar 00 The MIT License (MIT) Copyright (c) 2014 TechCrunch Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wp-async-task/wp-async-task.php 0000755 00000016743 14721521766 0012510 0 ustar 00 <?php /** * Plugin Name: WP Asynchronous Tasks * Version: 1.0 * Description: Creates an abstract class to execute asynchronous tasks * Author: 10up, Eric Mann, Luke Gedeon, John P. Bloch * License: MIT */ if ( ! class_exists( 'WP_Async_Task' ) ) { abstract class WP_Async_Task { /** * Constant identifier for a task that should be available to logged-in users * * See constructor documentation for more details. */ const LOGGED_IN = 1; /** * Constant identifier for a task that should be available to logged-out users * * See constructor documentation for more details. */ const LOGGED_OUT = 2; /** * Constant identifier for a task that should be available to all users regardless of auth status * * See constructor documentation for more details. */ const BOTH = 3; /** * This is the argument count for the main action set in the constructor. It * is set to an arbitrarily high value of twenty, but can be overridden if * necessary * * @var int */ protected $argument_count = 20; /** * Priority to fire intermediate action. * * @var int */ protected $priority = 10; /** * @var string */ protected $action; /** * @var array */ protected $_body_data; /** * Constructor to wire up the necessary actions * * Which hooks the asynchronous postback happens on can be set by the * $auth_level parameter. There are essentially three options: logged in users * only, logged out users only, or both. Set this when you instantiate an * object by using one of the three class constants to do so: * - LOGGED_IN * - LOGGED_OUT * - BOTH * $auth_level defaults to BOTH * * @throws Exception If the class' $action value hasn't been set * * @param int $auth_level The authentication level to use (see above) */ public function __construct( $auth_level = self::BOTH ) { if ( empty( $this->action ) ) { throw new Exception( 'Action not defined for class ' . __CLASS__ ); } add_action( $this->action, array( $this, 'launch' ), (int) $this->priority, (int) $this->argument_count ); if ( $auth_level & self::LOGGED_IN ) { add_action( "admin_post_wp_async_$this->action", array( $this, 'handle_postback' ) ); } if ( $auth_level & self::LOGGED_OUT ) { add_action( "admin_post_nopriv_wp_async_$this->action", array( $this, 'handle_postback' ) ); } } /** * Add the shutdown action for launching the real postback if we don't * get an exception thrown by prepare_data(). * * @uses func_get_args() To grab any arguments passed by the action */ public function launch() { $data = func_get_args(); try { $data = $this->prepare_data( $data ); } catch ( Exception $e ) { return; } $data['action'] = "wp_async_$this->action"; $data['_nonce'] = $this->create_async_nonce(); $this->_body_data = $data; if ( ! has_action( 'shutdown', array( $this, 'launch_on_shutdown' ) ) ) { add_action( 'shutdown', array( $this, 'launch_on_shutdown' ) ); } } /** * Launch the request on the WordPress shutdown hook * * On VIP we got into data races due to the postback sometimes completing * faster than the data could propogate to the database server cluster. * This made WordPress get empty data sets from the database without * failing. On their advice, we're moving the actual firing of the async * postback to the shutdown hook. Supposedly that will ensure that the * data at least has time to get into the object cache. * * @uses $_COOKIE To send a cookie header for async postback * @uses apply_filters() * @uses admin_url() * @uses wp_remote_post() */ public function launch_on_shutdown() { if ( ! empty( $this->_body_data ) ) { $cookies = array(); foreach ( $_COOKIE as $name => $value ) { $cookies[] = "$name=" . urlencode( is_array( $value ) ? serialize( $value ) : $value ); } $request_args = array( 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters( 'https_local_ssl_verify', true ), 'body' => $this->_body_data, 'headers' => array( 'cookie' => implode( '; ', $cookies ), ), ); $url = admin_url( 'admin-post.php' ); wp_remote_post( $url, $request_args ); } } /** * Verify the postback is valid, then fire any scheduled events. * * @uses $_POST['_nonce'] * @uses is_user_logged_in() * @uses add_filter() * @uses wp_die() */ public function handle_postback() { if ( isset( $_POST['_nonce'] ) && $this->verify_async_nonce( $_POST['_nonce'] ) ) { if ( ! is_user_logged_in() ) { $this->action = "nopriv_$this->action"; } $this->run_action(); } add_filter( 'wp_die_handler', function() { die(); } ); wp_die(); } /** * Create a random, one time use token. * * Based entirely on wp_create_nonce() but does not tie the nonce to the * current logged-in user. * * @uses wp_nonce_tick() * @uses wp_hash() * * @return string The one-time use token */ protected function create_async_nonce() { $action = $this->get_nonce_action(); $i = wp_nonce_tick(); return substr( wp_hash( $i . $action . get_class( $this ), 'nonce' ), - 12, 10 ); } /** * Verify that the correct nonce was used within the time limit. * * @uses wp_nonce_tick() * @uses wp_hash() * * @param string $nonce Nonce to be verified * * @return bool Whether the nonce check passed or failed */ protected function verify_async_nonce( $nonce ) { $action = $this->get_nonce_action(); $i = wp_nonce_tick(); // Nonce generated 0-12 hours ago if ( substr( wp_hash( $i . $action . get_class( $this ), 'nonce' ), - 12, 10 ) == $nonce ) { return 1; } // Nonce generated 12-24 hours ago if ( substr( wp_hash( ( $i - 1 ) . $action . get_class( $this ), 'nonce' ), - 12, 10 ) == $nonce ) { return 2; } // Invalid nonce return false; } /** * Get a nonce action based on the $action property of the class * * @return string The nonce action for the current instance */ protected function get_nonce_action() { $action = $this->action; if ( substr( $action, 0, 7 ) === 'nopriv_' ) { $action = substr( $action, 7 ); } $action = "wp_async_$action"; return $action; } /** * Prepare any data to be passed to the asynchronous postback * * The array this function receives will be a numerically keyed array from * func_get_args(). It is expected that you will return an associative array * so that the $_POST values used in the asynchronous call will make sense. * * The array you send back may or may not have anything to do with the data * passed into this method. It all depends on the implementation details and * what data is needed in the asynchronous postback. * * Do not set values for 'action' or '_nonce', as those will get overwritten * later in launch(). * * @throws Exception If the postback should not occur for any reason * * @param array $data The raw data received by the launch method * * @return array The prepared data */ abstract protected function prepare_data( $data ); /** * Run the do_action function for the asynchronous postback. * * This method needs to fetch and sanitize any and all data from the $_POST * superglobal and provide them to the do_action call. * * The action should be constructed as "wp_async_task_$this->action" */ abstract protected function run_action(); } } wp-async-task/.gitignore 0000755 00000000042 14721521766 0011247 0 ustar 00 vendor/ composer.lock phpunit.xml
| ver. 1.4 |
Github
|
.
| PHP 7.4.3-4ubuntu2.24 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка