first commit
This commit is contained in:
21
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/LICENSE.md
vendored
Normal file
21
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/LICENSE.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
||||
124
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/README.md
vendored
Normal file
124
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/README.md
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
# 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.
|
||||
6
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/bootstrap.php.dist
vendored
Normal file
6
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/bootstrap.php.dist
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
WP_Mock::setUsePatchwork( false );
|
||||
WP_Mock::bootstrap();
|
||||
37
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/composer.json
vendored
Normal file
37
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/composer.json
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"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/"]
|
||||
}
|
||||
}
|
||||
14
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/phpunit.xml.dist
vendored
Normal file
14
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/phpunit.xml.dist
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<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>
|
||||
438
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/tests/phpunit/WP-Async-TaskTest.php
vendored
Normal file
438
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/tests/phpunit/WP-Async-TaskTest.php
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
<?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;
|
||||
}
|
||||
|
||||
}
|
||||
11
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/tests/phpunit/mocks/Async.php
vendored
Normal file
11
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/tests/phpunit/mocks/Async.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
class Async extends BaseAsync {
|
||||
|
||||
public function __construct( $init = parent::BOTH ) {
|
||||
if ( $init ) {
|
||||
parent::__construct( $init );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
class BaseAsync extends WP_Async_Task {
|
||||
|
||||
protected $action = 'async';
|
||||
|
||||
protected function prepare_data( $data ) {
|
||||
}
|
||||
|
||||
protected function run_action() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
class EmptyAsync extends BaseAsync {
|
||||
|
||||
protected $action = null;
|
||||
|
||||
}
|
||||
266
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/wp-async-task.php
vendored
Normal file
266
wp-content/plugins/pixelyoursite/vendor/techcrunch/wp-async-task/wp-async-task.php
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
<?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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user