first commit
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Word_Count_Background_Process_Factory {
|
||||
|
||||
const PREFIX = 'wpml_tm';
|
||||
const ACTION_REQUESTED_TYPES = 'word_count_background_process_requested_types';
|
||||
|
||||
/**
|
||||
* @return WPML_TM_Word_Count_Background_Process_Requested_Types
|
||||
*/
|
||||
public function create_requested_types() {
|
||||
$records_factory = new WPML_TM_Word_Count_Records_Factory();
|
||||
$records = $records_factory->create();
|
||||
|
||||
$setters_factory = new WPML_TM_Word_Count_Setters_Factory();
|
||||
$setters = $setters_factory->create();
|
||||
|
||||
$requested_types_items = new WPML_TM_Word_Count_Queue_Items_Requested_Types( $records );
|
||||
|
||||
return new WPML_TM_Word_Count_Background_Process_Requested_Types( $requested_types_items, $setters, $records );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Word_Count_Background_Process_Requested_Types extends WPML_TM_Word_Count_Background_Process {
|
||||
|
||||
/** @var WPML_TM_Word_Count_Queue_Items_Requested_Types $queue */
|
||||
protected $queue;
|
||||
|
||||
/** @var WPML_TM_Word_Count_Records $records */
|
||||
private $records;
|
||||
|
||||
/**
|
||||
* @param WPML_TM_Word_Count_Queue_Items_Requested_Types $queue_items
|
||||
* @param IWPML_TM_Word_Count_Set[] $setters
|
||||
*/
|
||||
public function __construct(
|
||||
WPML_TM_Word_Count_Queue_Items_Requested_Types $queue_items,
|
||||
array $setters,
|
||||
WPML_TM_Word_Count_Records $records
|
||||
) {
|
||||
/** We need to set the action before constructing the parent class `WP_Async_Request` */
|
||||
$this->action = WPML_TM_Word_Count_Background_Process_Factory::ACTION_REQUESTED_TYPES;
|
||||
parent::__construct( $queue_items, $setters );
|
||||
$this->records = $records;
|
||||
|
||||
add_filter(
|
||||
'wpml_tm_word_count_background_process_requested_types_memory_exceeded',
|
||||
array(
|
||||
$this,
|
||||
'memory_exceeded_filter',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function init( $requested_types ) {
|
||||
$this->queue->reset( $requested_types );
|
||||
$this->records->reset_all( $requested_types );
|
||||
$this->dispatch();
|
||||
}
|
||||
|
||||
public function dispatch() {
|
||||
update_option(
|
||||
WPML_TM_Word_Count_Hooks_Factory::OPTION_KEY_REQUESTED_TYPES_STATUS,
|
||||
WPML_TM_Word_Count_Hooks_Factory::PROCESS_IN_PROGRESS
|
||||
);
|
||||
|
||||
parent::dispatch();
|
||||
}
|
||||
|
||||
public function complete() {
|
||||
update_option(
|
||||
WPML_TM_Word_Count_Hooks_Factory::OPTION_KEY_REQUESTED_TYPES_STATUS,
|
||||
WPML_TM_Word_Count_Hooks_Factory::PROCESS_COMPLETED
|
||||
);
|
||||
|
||||
parent::complete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter result of memory_exceeded() function in WP_Background_Process class.
|
||||
* Used by it get_memory_limit() function of WP_Background_Process class contains a number of bugs,
|
||||
* producing wrong result when 'memory_limit' setting in php.ini is in human readable format like '1G'.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function memory_exceeded_filter() {
|
||||
$memory_limit = $this->get_memory_limit() * 0.9; // 90% of max memory
|
||||
$current_memory = memory_get_usage( true );
|
||||
|
||||
return $current_memory >= $memory_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit in bytes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function get_memory_limit() {
|
||||
if ( function_exists( 'ini_get' ) ) {
|
||||
$memory_limit = ini_get( 'memory_limit' );
|
||||
} else {
|
||||
// Sensible default.
|
||||
$memory_limit = '128M';
|
||||
}
|
||||
|
||||
if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) {
|
||||
// Unlimited, set to 32GB.
|
||||
$memory_limit = '32000M';
|
||||
}
|
||||
|
||||
return $this->convert_shorthand_to_bytes( $memory_limit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a shorthand byte value to an integer byte value.
|
||||
*
|
||||
* @param string $value A (PHP ini) byte value, either shorthand or ordinary.
|
||||
* @return int An integer byte value.
|
||||
*/
|
||||
protected function convert_shorthand_to_bytes( $value ) {
|
||||
$value = strtolower( trim( $value ) );
|
||||
$bytes = (int) $value;
|
||||
|
||||
if ( false !== strpos( $value, 'g' ) ) {
|
||||
$bytes *= 1024 * 1024 * 1024;
|
||||
} elseif ( false !== strpos( $value, 'm' ) ) {
|
||||
$bytes *= 1024 * 1024;
|
||||
} elseif ( false !== strpos( $value, 'k' ) ) {
|
||||
$bytes *= 1024;
|
||||
}
|
||||
|
||||
// Deal with large (float) values which run into the maximum integer size.
|
||||
return min( $bytes, PHP_INT_MAX );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
abstract class WPML_TM_Word_Count_Background_Process extends WP_Background_Process {
|
||||
|
||||
/** @var IWPML_TM_Word_Count_Queue_Items $queue */
|
||||
protected $queue;
|
||||
|
||||
/** @var IWPML_TM_Word_Count_Set[] $setters */
|
||||
private $setters;
|
||||
|
||||
/**
|
||||
* @param IWPML_TM_Word_Count_Queue_Items $queue
|
||||
* @param IWPML_TM_Word_Count_Set[] $setters
|
||||
*/
|
||||
public function __construct( IWPML_TM_Word_Count_Queue_Items $queue, array $setters ) {
|
||||
/** We need to set the prefix and the identifier before constructing the parent class `WP_Async_Request` */
|
||||
$this->prefix = WPML_TM_Word_Count_Background_Process_Factory::PREFIX;
|
||||
$this->action = WPML_TM_Word_Count_Background_Process_Factory::ACTION_REQUESTED_TYPES;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->queue = $queue;
|
||||
$this->setters = $setters;
|
||||
}
|
||||
|
||||
/**
|
||||
* This abstract method is not implemented because we override the `handle` method.
|
||||
*/
|
||||
protected function task( $item ) {}
|
||||
|
||||
protected function handle() {
|
||||
$this->lock_process();
|
||||
|
||||
while ( ! $this->time_exceeded() && ! $this->memory_exceeded() && ! $this->queue->is_completed() ) {
|
||||
|
||||
list( $id, $type ) = $this->queue->get_next();
|
||||
|
||||
if ( $id && $type ) {
|
||||
$this->setters[ $type ]->process( $id );
|
||||
$this->queue->remove( $id, $type );
|
||||
}
|
||||
}
|
||||
|
||||
$this->queue->save();
|
||||
$this->unlock_process();
|
||||
|
||||
if ( $this->queue->is_completed() ) {
|
||||
$this->complete();
|
||||
} else {
|
||||
$this->dispatch();
|
||||
}
|
||||
|
||||
wp_die();
|
||||
}
|
||||
|
||||
protected function is_queue_empty() {
|
||||
return $this->queue->is_completed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
interface IWPML_TM_Word_Count_Queue_Items {
|
||||
|
||||
/**
|
||||
* @return array|null a tuple containing the element id and type or null if queue is empty
|
||||
*/
|
||||
public function get_next();
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $type
|
||||
*/
|
||||
public function remove( $id, $type );
|
||||
|
||||
/** @return bool */
|
||||
public function is_completed();
|
||||
|
||||
public function save();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
class WPML_TM_Word_Count_Queue_Items_Requested_Types implements IWPML_TM_Word_Count_Queue_Items {
|
||||
|
||||
const OPTION_KEY = 'wpml_word_count_queue_items_requested_types';
|
||||
|
||||
const STEP_STANDALONE_PACKAGES = 1;
|
||||
const STEP_POST_PACKAGES = 2;
|
||||
const STEP_POSTS = 3;
|
||||
const STEP_COMPLETED = 4;
|
||||
|
||||
/** @var WPML_TM_Word_Count_Records $records */
|
||||
private $records;
|
||||
|
||||
/** @var array $requested_types to be processed */
|
||||
private $requested_types;
|
||||
|
||||
/** @var string $step */
|
||||
private $step;
|
||||
|
||||
/** @var array|null $items */
|
||||
private $items = array(
|
||||
'string' => array(),
|
||||
'package' => array(),
|
||||
'post' => array(),
|
||||
);
|
||||
|
||||
public function __construct( WPML_TM_Word_Count_Records $records ) {
|
||||
$this->records = $records;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null a tuple containing the element id and type or null if queue is empty
|
||||
*/
|
||||
public function get_next() {
|
||||
$this->init_queue();
|
||||
|
||||
foreach ( array( 'string', 'package', 'post' ) as $type ) {
|
||||
|
||||
if ( $this->items[ $type ] ) {
|
||||
return array( reset( $this->items[ $type ] ), $type );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function init_queue() {
|
||||
if ( ! $this->step ) {
|
||||
$this->restore_queue_from_db();
|
||||
}
|
||||
|
||||
if ( ! $this->has_items() ) {
|
||||
$this->init_step();
|
||||
}
|
||||
}
|
||||
|
||||
private function restore_queue_from_db() {
|
||||
$this->step = self::STEP_STANDALONE_PACKAGES;
|
||||
$options = get_option( self::OPTION_KEY, array() );
|
||||
|
||||
if ( isset( $options['step'] ) ) {
|
||||
$this->step = $options['step'];
|
||||
}
|
||||
|
||||
if ( isset( $options['requested_types'] ) ) {
|
||||
$this->requested_types = $options['requested_types'];
|
||||
}
|
||||
|
||||
if ( isset( $options['items'] ) ) {
|
||||
$this->items = $options['items'];
|
||||
}
|
||||
}
|
||||
|
||||
private function init_step() {
|
||||
switch ( $this->step ) {
|
||||
|
||||
case self::STEP_STANDALONE_PACKAGES:
|
||||
$this->add_standalone_packages_to_queue();
|
||||
break;
|
||||
|
||||
case self::STEP_POST_PACKAGES:
|
||||
$this->add_post_packages_to_queue();
|
||||
break;
|
||||
|
||||
case self::STEP_POSTS:
|
||||
$this->add_posts_to_queue();
|
||||
break;
|
||||
}
|
||||
|
||||
$this->make_item_keys_equals_to_id();
|
||||
$this->maybe_move_to_next_step();
|
||||
}
|
||||
|
||||
private function add_standalone_packages_to_queue() {
|
||||
if ( ! empty( $this->requested_types['package_kinds'] ) ) {
|
||||
$this->items['package'] = $this->records->get_package_ids_from_kind_slugs( $this->requested_types['package_kinds'] );
|
||||
$this->items['string'] = $this->records->get_strings_ids_from_package_ids( $this->items['package'] );
|
||||
}
|
||||
}
|
||||
|
||||
private function add_post_packages_to_queue() {
|
||||
if ( ! empty( $this->requested_types['post_types'] ) ) {
|
||||
$this->items['package'] = $this->records->get_package_ids_from_post_types( $this->requested_types['post_types'] );
|
||||
$this->items['string'] = $this->records->get_strings_ids_from_package_ids( $this->items['package'] );
|
||||
}
|
||||
}
|
||||
|
||||
private function add_posts_to_queue() {
|
||||
if ( ! empty( $this->requested_types['post_types'] ) ) {
|
||||
$this->items['post'] = $this->records->get_post_source_ids_from_types( $this->requested_types['post_types'] );
|
||||
}
|
||||
}
|
||||
|
||||
private function make_item_keys_equals_to_id() {
|
||||
foreach ( $this->items as $type => $ids ) {
|
||||
if ( $this->items[ $type ] ) {
|
||||
$this->items[ $type ] = array_combine( array_values( $this->items[ $type ] ), $this->items[ $type ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function maybe_move_to_next_step() {
|
||||
if ( ! $this->has_items() && ! $this->is_completed() ) {
|
||||
$this->step++;
|
||||
$this->init_step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $type
|
||||
*/
|
||||
public function remove( $id, $type ) {
|
||||
if ( isset( $this->items[ $type ][ $id ] ) ) {
|
||||
unset( $this->items[ $type ][ $id ] );
|
||||
}
|
||||
|
||||
$this->maybe_move_to_next_step();
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
private function has_items() {
|
||||
return ! empty( $this->items['string'] )
|
||||
|| ! empty( $this->items['package'] )
|
||||
|| ! empty( $this->items['post'] );
|
||||
}
|
||||
|
||||
/** @return bool */
|
||||
public function is_completed() {
|
||||
return $this->step === self::STEP_COMPLETED;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
$options = array(
|
||||
'step' => $this->step,
|
||||
'requested_types' => $this->requested_types,
|
||||
'items' => $this->items,
|
||||
);
|
||||
|
||||
update_option( self::OPTION_KEY, $options, false );
|
||||
}
|
||||
|
||||
public function reset( array $requested_types ) {
|
||||
$this->step = self::STEP_STANDALONE_PACKAGES;
|
||||
$this->requested_types = $requested_types;
|
||||
$this->items = null;
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user