<?php namespace FluentForm\Framework\Foundation; trait AsyncRequestTrait { protected $asyncActions = []; protected $launchableActions = []; public function addAsyncAction($action, $handler) { $this->asyncActions[$action] = $handler; } public function doAsyncAction($action, $data = []) { $this->launchableActions[$action] = $data; } protected function registerAsyncActions() { if ($this->hasAsyncActions()) { $this->registerAdminPostAction( $this->config->get('app.slug') ); } } protected function hasAsyncActions() { return count($this->asyncActions) > 0; } protected function registerAdminPostAction($slug) { add_action("admin_post_wpfluent_async_$slug", [$this, 'callback']); add_action("admin_post_nopriv_wpfluent_async_$slug", [$this, 'callback']); if (!has_action('shutdown', [$this, 'launchPostRequest'])) { add_action('shutdown', [$this, 'launchPostRequest']); } } public function callback() { add_filter('wp_die_handler', function() { die(); }); try { $this->verifyNonce(); foreach ($_POST['actions'] as $action => $data) { try { $handler = $this->parseHookHandler( $this->asyncActions[$action] ); $handler($data); } catch (\Exception $e) { continue; } } } catch (\Exception $e) { return; } } public function launchPostRequest() { if (!$this->hasLaunchableActions()) { return; } $args = [ 'timeout' => 0.01, 'blocking' => false, 'sslverify' => apply_filters('https_local_ssl_verify', true), 'body' => $this->getData(), 'headers' => [ 'cookie' => $this->getCookie(), ], ]; wp_remote_post(admin_url('admin-post.php'), $args); } protected function getData() { return [ '_nonce' => $this->createNonce(), 'action' => $this->getMainAction(), 'actions' => $this->launchableActions, ]; } protected function getCookie() { $cookies = []; foreach ($_COOKIE as $name => $value) { $cookies[] = "$name=" . urlencode(is_array($value) ? serialize($value) : $value); } return implode('; ', $cookies); } protected function hasLaunchableActions() { return count($this->launchableActions) > 0; } protected function createNonce() { $i = wp_nonce_tick(); $action = $this->getMainAction(); return wp_hash($i . $action, 'nonce'); } protected function verifyNonce() { if (!isset($_POST['_nonce'])) { throw new \Exception('Invalid Request.'); } $nonce = $_POST['_nonce']; $i = wp_nonce_tick(); $action = $this->getMainAction(); // Nonce generated 0-12 hours ago if (wp_hash($i . $action, 'nonce') == $nonce) { return 1; } // Nonce generated 12-24 hours ago if (wp_hash(($i - 1) . $action , 'nonce') == $nonce) { return 2; } // Invalid nonce return false; } protected function getMainAction() { $action = $this->config->get('app.slug'); $action = "wpfluent_async_$action"; return $action; } }