first
This commit is contained in:
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\AttachmentPathsGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Initializes conversion of all image sizes for attachment.
|
||||
*/
|
||||
class ConvertAttachmentAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_convert_attachment', [ $this, 'convert_files_by_attachment' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all sizes of attachment to output formats.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param bool $regenerate_force .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function convert_files_by_attachment( int $post_id, bool $regenerate_force = false, int $quality_level = null ) {
|
||||
$attachment = new AttachmentPathsGenerator( $this->plugin_data );
|
||||
|
||||
if ( $quality_level === 0 ) {
|
||||
do_action( 'webpc_delete_paths', $attachment->get_attachment_paths( $post_id ), true );
|
||||
} else {
|
||||
do_action( 'webpc_convert_paths', $attachment->get_attachment_paths( $post_id ), $regenerate_force, $quality_level );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\Method\MethodFactory;
|
||||
use WebpConverter\Conversion\Method\MethodIntegrator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Initializes conversion of all images in list of paths.
|
||||
*/
|
||||
class ConvertPathsAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
public function __construct( PluginData $plugin_data, MethodFactory $method_factory ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_convert_paths', [ $this, 'convert_files_by_paths' ], 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts all given images to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of images.
|
||||
* @param bool $regenerate_force .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function convert_files_by_paths( array $paths, bool $regenerate_force = false, int $quality_level = null ) {
|
||||
( new MethodIntegrator( $this->plugin_data, $this->method_factory ) )
|
||||
->init_conversion( $this->remove_paths_from_excluded_paths( $paths ), $regenerate_force, false, $quality_level );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes paths of source images from excluded paths.
|
||||
*
|
||||
* @param string[] $source_paths Server paths of images.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function remove_paths_from_excluded_paths( array $source_paths ): array {
|
||||
foreach ( $source_paths as $path_index => $path ) {
|
||||
if ( ! apply_filters( 'webpc_supported_source_file', true, basename( $path ), $path ) ) {
|
||||
unset( $source_paths[ $path_index ] );
|
||||
}
|
||||
}
|
||||
return $source_paths;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Removes image from its output format when removing image from media library.
|
||||
*/
|
||||
class DeleteFileHandler implements HookableInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'wp_delete_file', [ $this, 'delete_attachment_file' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes output image based on server path of source image.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
* @internal
|
||||
*/
|
||||
public function delete_attachment_file( string $path ): string {
|
||||
do_action( 'webpc_delete_paths', [ $path ] );
|
||||
return $path;
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Deletes all images in list of paths.
|
||||
*/
|
||||
class DeletePathsAction implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'webpc_delete_paths', [ $this, 'delete_files_by_paths' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes images from output directory.
|
||||
*
|
||||
* @param string[] $paths Server paths of output images.
|
||||
* @param bool $set_skipped_flag .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function delete_files_by_paths( array $paths, bool $set_skipped_flag = false ) {
|
||||
foreach ( $paths as $path ) {
|
||||
$this->delete_file_by_path( $path, $set_skipped_flag );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes image from output directory.
|
||||
*
|
||||
* @param string $path Server path of output image.
|
||||
* @param bool $set_skipped_flag .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function delete_file_by_path( string $path, bool $set_skipped_flag ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$output_formats = ( $set_skipped_flag ) ? $plugin_settings[ OutputFormatsOption::OPTION_NAME ] : null;
|
||||
|
||||
if ( ! ( $output_paths = $this->output_path->get_paths( $path, $set_skipped_flag, $output_formats ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $output_paths as $output_path ) {
|
||||
if ( is_writable( $output_path ) ) {
|
||||
unlink( $output_path );
|
||||
}
|
||||
|
||||
if ( is_writable( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION );
|
||||
}
|
||||
|
||||
if ( $set_skipped_flag ) {
|
||||
$file = fopen( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION, 'w' );
|
||||
if ( $file !== false ) {
|
||||
fclose( $file );
|
||||
}
|
||||
} elseif ( is_writable( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Action;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronInitiator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\AutoConversionOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Initializes image conversion when uploading images to media library.
|
||||
*/
|
||||
class UploadFileHandler implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
/**
|
||||
* Paths of converted images.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $uploaded_paths = [];
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'init', [ $this, 'init_hooks_after_setup' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_hooks_after_setup() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ AutoConversionOption::OPTION_NAME ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_filter( 'wp_update_attachment_metadata', [ $this, 'init_attachment_conversion' ], 10, 2 );
|
||||
add_filter( 'image_make_intermediate_size', [ $this, 'init_image_conversion' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting attachment images while attachment is uploaded.
|
||||
*
|
||||
* @param mixed[]|null $data Updated attachment meta data.
|
||||
* @param int|null $attachment_id ID of attachment.
|
||||
*
|
||||
* @return mixed[]|null Attachment meta data.
|
||||
* @internal
|
||||
*/
|
||||
public function init_attachment_conversion( array $data = null, int $attachment_id = null ) {
|
||||
if ( ( $data === null ) || ( $attachment_id === null )
|
||||
|| ! isset( $data['file'] ) || ! isset( $data['sizes'] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$file_extension = strtolower( pathinfo( $data['file'], PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $file_extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$paths = $this->get_sizes_paths( $data );
|
||||
$paths = apply_filters( 'webpc_attachment_paths', $paths, $attachment_id );
|
||||
|
||||
$this->uploaded_paths = array_merge( $this->uploaded_paths, $paths );
|
||||
add_action( 'shutdown', [ $this, 'save_paths_to_conversion' ] );
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting attachment images after file is saved by Image Editor.
|
||||
*
|
||||
* @param string $filename Path of image.
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function init_image_conversion( string $filename ): string {
|
||||
$upload = wp_upload_dir();
|
||||
if ( strpos( $filename, $upload['basedir'] ) !== 0 ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$file_extension = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $file_extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $filename;
|
||||
}
|
||||
|
||||
$this->uploaded_paths[] = str_replace( '\\', '/', $filename );
|
||||
|
||||
add_action( 'shutdown', [ $this, 'save_paths_to_conversion' ] );
|
||||
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths of attachment image sizes.
|
||||
*
|
||||
* @param mixed[] $data Updated attachment meta data.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_sizes_paths( array $data ): array {
|
||||
$directory = $this->get_attachment_directory( $data['file'] );
|
||||
$list = [];
|
||||
|
||||
if ( isset( $data['original_image'] ) ) {
|
||||
$list[] = $directory . $data['original_image'];
|
||||
}
|
||||
|
||||
$list[] = $directory . basename( $data['file'] );
|
||||
foreach ( $data['sizes'] as $size ) {
|
||||
$path = $directory . $size['file'];
|
||||
if ( ! in_array( $path, $list ) ) {
|
||||
$list[] = $path;
|
||||
}
|
||||
}
|
||||
return array_values( array_unique( $list ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of source image.
|
||||
*
|
||||
* @param string $path Relative path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
*/
|
||||
private function get_attachment_directory( string $path ): string {
|
||||
$upload = wp_upload_dir();
|
||||
$path_directory = rtrim( dirname( $path ), '/\\.' );
|
||||
$source = rtrim( $upload['basedir'], '/\\' ) . '/' . $path_directory . '/';
|
||||
|
||||
return str_replace( '\\', '/', $source );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function save_paths_to_conversion() {
|
||||
$paths = array_unique( $this->uploaded_paths );
|
||||
if ( ! $paths ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cron_initiator->add_paths_to_conversion( $paths, true );
|
||||
$this->cron_initiator->init_async_conversion( true );
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns all image paths for attachment.
|
||||
*/
|
||||
class AttachmentPathsGenerator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* Current upload directory path and URL.
|
||||
*
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $upload_dir;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->upload_dir = wp_upload_dir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths to source images of attachment.
|
||||
*
|
||||
* @param int $attachment_id ID of attachment.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
public function get_attachment_paths( int $attachment_id ): array {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
return $this->get_paths_by_attachment( $attachment_id, $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server paths to source images of attachment by file extensions.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_paths_by_attachment( int $post_id, array $settings ): array {
|
||||
$list = [];
|
||||
$metadata = wp_get_attachment_metadata( $post_id );
|
||||
if ( ! $metadata || ! isset( $metadata['file'] ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$extension = strtolower( pathinfo( $metadata['file'], PATHINFO_EXTENSION ) );
|
||||
if ( ! in_array( $extension, $settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
$paths = $this->get_paths_by_sizes( $post_id, $metadata );
|
||||
return apply_filters( 'webpc_attachment_paths', $paths, $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique server paths to source images of attachment.
|
||||
*
|
||||
* @param int $post_id ID of attachment.
|
||||
* @param mixed[] $metadata Data of attachment.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function get_paths_by_sizes( int $post_id, array $metadata ): array {
|
||||
$main_file = str_replace( '\\', '/', ( $this->upload_dir['basedir'] . '/' . $metadata['file'] ) );
|
||||
$file_path = dirname( $main_file ) . '/';
|
||||
$list = [ $main_file ];
|
||||
|
||||
foreach ( $metadata['sizes'] ?? [] as $size => $size_data ) {
|
||||
$list[] = $file_path . $size_data['file'];
|
||||
}
|
||||
return array_values( array_unique( $list ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
/**
|
||||
* Excludes re-conversion of files that caused converting error.
|
||||
*/
|
||||
class CrashedFilesOperator {
|
||||
|
||||
const CRASHED_FILE_EXTENSION = 'crashed';
|
||||
|
||||
/**
|
||||
* @param string $output_path .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create_crashed_file( string $output_path ) {
|
||||
$file = fopen( $output_path . '.' . self::CRASHED_FILE_EXTENSION, 'w' );
|
||||
if ( $file === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
fclose( $file );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $output_path .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_crashed_file( string $output_path ) {
|
||||
if ( ! file_exists( $output_path ) || ! file_exists( $output_path . '.' . self::CRASHED_FILE_EXTENSION ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
unlink( $output_path . '.' . self::CRASHED_FILE_EXTENSION );
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Adds cron event that converts images.
|
||||
*/
|
||||
class CronEventGenerator implements HookableInterface {
|
||||
|
||||
const CRON_PATHS_ACTION = 'webpc_cron_paths';
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'init', [ $this, 'add_cron_event' ] );
|
||||
add_action( self::CRON_PATHS_ACTION, [ $this, 'get_paths_to_conversion' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes cron event to convert all images.
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function add_cron_event() {
|
||||
if ( wp_next_scheduled( self::CRON_PATHS_ACTION )
|
||||
|| ! ( $settings = $this->plugin_data->get_plugin_settings() )
|
||||
|| ! in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $settings[ ExtraFeaturesOption::OPTION_NAME ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_schedule_event( time(), CronSchedulesGenerator::CRON_PATHS_SCHEDULE, self::CRON_PATHS_ACTION );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function get_paths_to_conversion() {
|
||||
$this->cron_initiator->refresh_paths_to_conversion();
|
||||
$this->cron_initiator->init_conversion();
|
||||
}
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\Conversion\Endpoint\CronConversionEndpoint;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\PathsFinder;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Manages automatic conversion of images.
|
||||
*/
|
||||
class CronInitiator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
/**
|
||||
* @var PathsFinder
|
||||
*/
|
||||
private $paths_finder;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronStatusManager $cron_status_manager = null,
|
||||
PathsFinder $paths_finder = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
$this->paths_finder = $paths_finder ?: new PathsFinder( $plugin_data, $token_repository, $format_factory );
|
||||
}
|
||||
|
||||
public function refresh_paths_to_conversion( bool $force_init = false ): bool {
|
||||
$saved_request_id = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $this->cron_status_manager->is_conversion_locked()
|
||||
|| ( ! $force_init && ( $saved_request_id !== null ) ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$cron_enabled = in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$this->cron_status_manager->set_conversion_status_locked( true, true );
|
||||
|
||||
$paths = $this->paths_finder->get_paths( true );
|
||||
$this->cron_status_manager->set_paths_to_conversion( $paths, $cron_enabled );
|
||||
$this->cron_status_manager->set_paths_skipped( ( $cron_enabled ) ? $paths : [] );
|
||||
|
||||
$this->cron_status_manager->set_conversion_status_locked( false );
|
||||
|
||||
return (bool) $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $new_paths .
|
||||
* @param bool $force_convert_modified .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_paths_to_conversion( array $new_paths, bool $force_convert_modified = false ) {
|
||||
$paths = $this->cron_status_manager->get_paths_to_conversion();
|
||||
$valid_new_paths = $this->paths_finder->skip_converted_paths( $new_paths, null, $force_convert_modified );
|
||||
|
||||
$this->cron_status_manager->set_paths_to_conversion( array_merge( $valid_new_paths, $paths ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $request_id .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_conversion( string $request_id = null ) {
|
||||
$saved_request_id = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $this->cron_status_manager->is_conversion_locked()
|
||||
|| ( ( $saved_request_id !== null ) && ( $request_id !== $saved_request_id ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paths = $this->cron_status_manager->get_paths_to_conversion();
|
||||
if ( ! $paths ) {
|
||||
$this->try_restart_conversion();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cron_status_manager->set_paths_to_conversion( array_slice( $paths, 1 ) );
|
||||
do_action( 'webpc_convert_paths', array_slice( $paths, 0, 1 ) );
|
||||
|
||||
$this->init_async_conversion();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $upload_request .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_async_conversion( bool $upload_request = false ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$service_mode = in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$headers = [
|
||||
CronConversionEndpoint::ROUTE_NONCE_HEADER => CronConversionEndpoint::get_route_nonce(),
|
||||
];
|
||||
if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) {
|
||||
$headers['Authorization'] = 'Basic ' . base64_encode( $_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
}
|
||||
|
||||
$args = [
|
||||
'timeout' => 0.01,
|
||||
'blocking' => false,
|
||||
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
|
||||
'headers' => $headers,
|
||||
];
|
||||
if ( $service_mode && $upload_request ) {
|
||||
unset( $args['timeout'] );
|
||||
unset( $args['blocking'] );
|
||||
}
|
||||
|
||||
$response = wp_remote_post( CronConversionEndpoint::get_route_url(), $args );
|
||||
if ( $service_mode && $upload_request ) {
|
||||
$this->cron_status_manager->set_conversion_request_response( $response );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function try_restart_conversion() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$cron_enabled = in_array( ExtraFeaturesOption::OPTION_VALUE_CRON_ENABLED, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] );
|
||||
|
||||
$this->cron_status_manager->reset_conversion_request_id();
|
||||
if ( ! $cron_enabled || ! $this->cron_status_manager->get_paths_counter() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->refresh_paths_to_conversion( true );
|
||||
$this->init_async_conversion();
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Adds time intervals to cron event.
|
||||
*/
|
||||
class CronSchedulesGenerator implements HookableInterface {
|
||||
|
||||
const CRON_PATHS_SCHEDULE = 'webpc_cron_paths';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'cron_schedules', [ $this, 'add_cron_interval' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new cron schedules.
|
||||
*
|
||||
* @param mixed[] $schedules Cron schedules.
|
||||
*
|
||||
* @return mixed[] Cron schedules.
|
||||
* @internal
|
||||
*/
|
||||
public function add_cron_interval( array $schedules ): array {
|
||||
$schedules[ self::CRON_PATHS_SCHEDULE ] = [
|
||||
'interval' => apply_filters( 'webpc_cron_paths_interval', 3600 ),
|
||||
'display' => 'Converter for Media',
|
||||
];
|
||||
return $schedules;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
/**
|
||||
* Stores data related to automatic image conversion.
|
||||
*/
|
||||
class CronStatusManager {
|
||||
|
||||
const CRON_PATHS_TRANSIENT = 'webpc_cron_paths';
|
||||
const CRON_PATHS_SKIPPED_TRANSIENT = 'webpc_cron_paths_skipped';
|
||||
const CRON_STATUS_LOCKED_TRANSIENT = 'webpc_cron_locked';
|
||||
const CRON_REQUEST_ID_TRANSIENT = 'webpc_cron_request_id';
|
||||
const CRON_REQUEST_RESPONSE_TRANSIENT = 'webpc_cron_request_response';
|
||||
const CRON_PATHS_LIMIT = 1000;
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
* @param bool $use_paths_limit .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_paths_to_conversion( array $paths, bool $use_paths_limit = true ) {
|
||||
set_site_transient(
|
||||
self::CRON_PATHS_TRANSIENT,
|
||||
( $use_paths_limit ) ? array_slice( $paths, 0, self::CRON_PATHS_LIMIT ) : $paths,
|
||||
3600
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_paths_skipped( array $paths ) {
|
||||
$counter = max( ( count( $paths ) - self::CRON_PATHS_LIMIT ), 0 );
|
||||
set_site_transient( self::CRON_PATHS_SKIPPED_TRANSIENT, $counter, 3600 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_paths_to_conversion(): array {
|
||||
$paths = get_site_transient( self::CRON_PATHS_TRANSIENT );
|
||||
return $paths ?: [];
|
||||
}
|
||||
|
||||
public function get_paths_counter(): int {
|
||||
$paths_count = count( $this->get_paths_to_conversion() );
|
||||
$paths_skipped = get_site_transient( self::CRON_PATHS_SKIPPED_TRANSIENT ) ?: 0;
|
||||
return ( $paths_count + $paths_skipped );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $new_status .
|
||||
* @param bool $is_long_expiration .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_conversion_status_locked( bool $new_status = true, bool $is_long_expiration = false ) {
|
||||
set_site_transient(
|
||||
self::CRON_STATUS_LOCKED_TRANSIENT,
|
||||
( $new_status ) ? 'yes' : null,
|
||||
( $is_long_expiration ) ? 900 : 60
|
||||
);
|
||||
if ( $new_status === true ) {
|
||||
$this->reset_conversion_request_id();
|
||||
}
|
||||
}
|
||||
|
||||
public function is_conversion_locked(): bool {
|
||||
return ( get_site_transient( self::CRON_STATUS_LOCKED_TRANSIENT ) === 'yes' );
|
||||
}
|
||||
|
||||
public function refresh_conversion_request_id(): string {
|
||||
$request_id = uniqid( '', true );
|
||||
set_site_transient( self::CRON_REQUEST_ID_TRANSIENT, $request_id, 60 );
|
||||
return $request_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset_conversion_request_id() {
|
||||
set_site_transient( self::CRON_REQUEST_ID_TRANSIENT, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function get_conversion_request_id() {
|
||||
$request_id = get_site_transient( self::CRON_REQUEST_ID_TRANSIENT );
|
||||
return $request_id ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[]|\WP_Error $response .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_conversion_request_response( $response ) {
|
||||
set_site_transient( self::CRON_REQUEST_RESPONSE_TRANSIENT, $response );
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Cron;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Settings\Page\PageIntegrator;
|
||||
|
||||
/**
|
||||
* Displays converting status on top menu bar in the WordPress Dashboard.
|
||||
*/
|
||||
class CronStatusViewer implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $paths_preview_count = 0;
|
||||
|
||||
public function __construct( CronStatusManager $cron_status_manager = null ) {
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'admin_init', [ $this, 'init_status_preview' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_status_preview() {
|
||||
if ( $this->cron_status_manager->get_conversion_request_id() === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->paths_preview_count = $this->cron_status_manager->get_paths_counter();
|
||||
if ( ! $this->paths_preview_count ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action( 'admin_bar_menu', [ $this, 'add_menu_to_top_bar' ], 1000 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_Admin_Bar $wp_admin_bar .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function add_menu_to_top_bar( \WP_Admin_Bar $wp_admin_bar ) {
|
||||
$count = number_format( $this->paths_preview_count, 0, '', ' ' );
|
||||
$menu_parent = [
|
||||
'id' => 'webpc-menu',
|
||||
'href' => PageIntegrator::get_settings_page_url(),
|
||||
'title' => sprintf(
|
||||
'<span class="ab-icon"></span><span class="ab-label">%1$s</span>',
|
||||
$count
|
||||
),
|
||||
];
|
||||
$menu_child = [
|
||||
'id' => 'webpc-menu-message',
|
||||
'title' => sprintf(
|
||||
/* translators: %1$s: progress percent */
|
||||
__( 'Converting images (%s) is in progress.', 'webp-converter-for-media' ),
|
||||
$count
|
||||
),
|
||||
'parent' => $menu_parent['id'],
|
||||
];
|
||||
|
||||
$wp_admin_bar->add_menu( $menu_parent );
|
||||
$wp_admin_bar->add_menu( $menu_child );
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports data about directory.
|
||||
*/
|
||||
abstract class DirectoryAbstract implements DirectoryInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return '/' . $this->get_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return ( file_exists( $this->get_server_path() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_output_directory(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_server_path(): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', $this->get_relative_path(), $this->get_type() );
|
||||
return sprintf(
|
||||
'%1$s/%2$s',
|
||||
rtrim( PathsGenerator::get_wordpress_root_path(), DIRECTORY_SEPARATOR ),
|
||||
$directory_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_path_url(): string {
|
||||
$source_url = apply_filters( 'webpc_site_url', ( defined( 'WP_HOME' ) ) ? WP_HOME : get_site_url() );
|
||||
$directory_name = apply_filters( 'webpc_dir_name', $this->get_relative_path(), $this->get_type() );
|
||||
return sprintf( '%1$s/%2$s', $source_url, $directory_name );
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Plugin\Uninstall\OutputFilesRemover;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Initializes integration for all directories.
|
||||
*/
|
||||
class DirectoryFactory implements HookableInterface {
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* Object of directories integration.
|
||||
*
|
||||
* @var DirectoryIntegrator
|
||||
*/
|
||||
private $directories_integration;
|
||||
|
||||
public function __construct( FormatFactory $format_factory ) {
|
||||
$this->format_factory = $format_factory;
|
||||
|
||||
$this->set_integration( new SourceDirectory( 'cache' ) );
|
||||
$this->set_integration( new SourceDirectory( 'gallery' ) );
|
||||
$this->set_integration( new SourceDirectory( 'plugins' ) );
|
||||
$this->set_integration( new SourceDirectory( 'themes' ) );
|
||||
$this->set_integration( new UploadsDirectory() );
|
||||
foreach ( apply_filters( 'webpc_source_directories', [] ) as $directory_name ) {
|
||||
$this->set_integration( new SourceDirectory( $directory_name ) );
|
||||
}
|
||||
$this->set_integration( new UploadsWebpcDirectory() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for directory.
|
||||
*
|
||||
* @param DirectoryInterface $directory .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( DirectoryInterface $directory ) {
|
||||
if ( $this->directories_integration === null ) {
|
||||
$this->directories_integration = new DirectoryIntegrator( $this->format_factory );
|
||||
}
|
||||
$this->directories_integration->add_directory( $directory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
$this->directories_integration->init_hooks();
|
||||
add_action( 'init', [ $this, 'init_hooks_after_setup' ] );
|
||||
add_action( 'webpc_settings_updated', [ $this, 'remove_unused_output_directories' ], 10, 2 );
|
||||
add_action( 'webpc_settings_updated', [ $this, 'remove_unused_output_format' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function init_hooks_after_setup() {
|
||||
foreach ( apply_filters( 'webpc_source_directories', [] ) as $directory_name ) {
|
||||
$this->set_integration( new SourceDirectory( $directory_name ) );
|
||||
}
|
||||
$this->directories_integration->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_directories(): array {
|
||||
return $this->directories_integration->get_source_directories();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $previous_plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function remove_unused_output_directories( array $plugin_settings, array $previous_plugin_settings ) {
|
||||
if ( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] === $previous_plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_dirs = $this->directories_integration->get_output_directories();
|
||||
foreach ( $all_dirs as $output_dir => $output_path ) {
|
||||
if ( in_array( $output_dir, $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paths = OutputFilesRemover::get_paths_from_location( $output_path );
|
||||
$paths[] = $output_path;
|
||||
OutputFilesRemover::remove_files( $paths );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $previous_plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function remove_unused_output_format( array $plugin_settings, array $previous_plugin_settings ) {
|
||||
if ( ( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] === $previous_plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| in_array( WebpFormat::FORMAT_EXTENSION, $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
$paths = OutputFilesRemover::get_paths_from_location( $path );
|
||||
OutputFilesRemover::remove_files( $paths, [ WebpFormat::FORMAT_EXTENSION ] );
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Returns various types of paths for directories.
|
||||
*/
|
||||
class DirectoryIntegrator implements HookableInterface {
|
||||
|
||||
/**
|
||||
* Objects of supported directories.
|
||||
*
|
||||
* @var DirectoryInterface[]
|
||||
*/
|
||||
private $directories = [];
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct( FormatFactory $format_factory, OutputPathGenerator $output_path = null ) {
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'webpc_dir_name', [ $this, 'get_dir_as_name' ], 0, 2 );
|
||||
add_filter( 'webpc_dir_path', [ $this, 'get_dir_as_path' ], 0, 2 );
|
||||
add_filter( 'webpc_dir_url', [ $this, 'get_dir_as_url' ], 0, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds support of directory, if available.
|
||||
*
|
||||
* @param DirectoryInterface $directory .
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add_directory( DirectoryInterface $directory ): self {
|
||||
$this->directories[ $directory->get_type() ] = $directory;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_source_directories(): array {
|
||||
$values = [];
|
||||
foreach ( $this->directories as $directory ) {
|
||||
if ( ! $directory->is_output_directory() && $directory->is_available() ) {
|
||||
$values[ $directory->get_type() ] = $directory->get_label();
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of output directories.
|
||||
*
|
||||
* @return string[] Types of directories with labels.
|
||||
*/
|
||||
public function get_output_directories(): array {
|
||||
$values = [];
|
||||
foreach ( $this->directories as $directory ) {
|
||||
if ( ! $directory->is_output_directory()
|
||||
&& ( $output_path = $this->output_path->get_directory_path( $directory->get_server_path() ) )
|
||||
&& ( $output_path !== $directory->get_server_path() ) ) {
|
||||
$values[ $directory->get_type() ] = $output_path;
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of directory relative to WordPress root directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string Relative server path of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_name( $value, string $directory_type ): string {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
$value = $this->directories[ $directory_type ]->get_relative_path();
|
||||
}
|
||||
return str_replace( '\\', '/', $value );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path of directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string Server path of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_path( $value, string $directory_type ): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', '', $directory_type );
|
||||
if ( $directory_name === '' ) {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
return $this->directories[ $directory_type ]->get_server_path();
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s/%2$s',
|
||||
rtrim( PathsGenerator::get_wordpress_root_path(), DIRECTORY_SEPARATOR ),
|
||||
$directory_name
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns URL of directory.
|
||||
*
|
||||
* @param string $value Default value.
|
||||
* @param string $directory_type Type of directory.
|
||||
*
|
||||
* @return string URL of directory.
|
||||
* @internal
|
||||
*/
|
||||
public function get_dir_as_url( $value, string $directory_type ): string {
|
||||
$directory_name = apply_filters( 'webpc_dir_name', '', $directory_type );
|
||||
if ( $directory_name === '' ) {
|
||||
if ( isset( $this->directories[ $directory_type ] ) ) {
|
||||
return $this->directories[ $directory_type ]->get_path_url();
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
$source_url = apply_filters( 'webpc_site_url', ( defined( 'WP_HOME' ) ) ? WP_HOME : get_site_url() );
|
||||
return sprintf( '%1$s/%2$s', $source_url, $directory_name );
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Interface for class that supports data about directory.
|
||||
*/
|
||||
interface DirectoryInterface {
|
||||
|
||||
/**
|
||||
* Returns type of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_type(): string;
|
||||
|
||||
/**
|
||||
* Returns label of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* Returns status if directory is available.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_available(): bool;
|
||||
|
||||
/**
|
||||
* Returns status if directory is destined for output.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_output_directory(): bool;
|
||||
|
||||
/**
|
||||
* Returns relative path of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_relative_path(): string;
|
||||
|
||||
/**
|
||||
* Returns server path of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_server_path(): string;
|
||||
|
||||
/**
|
||||
* Returns URL of directory.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_path_url(): string;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about source directory.
|
||||
*/
|
||||
class SourceDirectory extends DirectoryAbstract {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory_type;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $directory_path;
|
||||
|
||||
public function __construct( string $directory_name ) {
|
||||
$this->directory_type = trim( $directory_name, '/\\' );
|
||||
$this->directory_path = '%s/' . $this->directory_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return $this->directory_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
return sprintf( $this->directory_path, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about /uploads directory.
|
||||
*/
|
||||
class UploadsDirectory extends DirectoryAbstract {
|
||||
|
||||
const DIRECTORY_TYPE = 'uploads';
|
||||
const DIRECTORY_PATH = '%s/uploads';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::DIRECTORY_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
if ( defined( 'UPLOADS' ) ) {
|
||||
return trim( UPLOADS, '/\\' );
|
||||
}
|
||||
|
||||
return sprintf( self::DIRECTORY_PATH, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Directory;
|
||||
|
||||
/**
|
||||
* Supports data about /uploads-webpc directory.
|
||||
*/
|
||||
class UploadsWebpcDirectory extends DirectoryAbstract {
|
||||
|
||||
const DIRECTORY_TYPE = 'webp';
|
||||
const DIRECTORY_PATH = '%s/uploads-webpc';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::DIRECTORY_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_output_directory(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_relative_path(): string {
|
||||
if ( defined( 'UPLOADS' ) ) {
|
||||
$uploads_dir = trim( UPLOADS, '/\\' );
|
||||
return trim( sprintf( self::DIRECTORY_PATH, dirname( $uploads_dir ) ), '/\\.' );
|
||||
}
|
||||
|
||||
return sprintf( self::DIRECTORY_PATH, basename( WP_CONTENT_DIR ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns paths to files in given directory.
|
||||
*/
|
||||
class DirectoryFilesFinder {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
ServerConfigurator $server_configurator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
* @internal
|
||||
*/
|
||||
public function get_files_by_directory( string $dir_path ): array {
|
||||
if ( ! file_exists( $dir_path ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time( 900 );
|
||||
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
return $this->find_files_in_directory(
|
||||
$dir_path,
|
||||
$settings[ SupportedExtensionsOption::OPTION_NAME ]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
* @param string[] $allowed_source_exts File extensions to find.
|
||||
* @param string $path_prefix File path related to directory path.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
private function find_files_in_directory( string $dir_path, array $allowed_source_exts, string $path_prefix = '' ): array {
|
||||
$paths = scandir( $dir_path );
|
||||
$list = [];
|
||||
if ( ! is_array( $paths ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
if ( $path_prefix === '' ) {
|
||||
$paths = array_diff( $paths, [ basename( RewritesErrorsDetector::PATH_OUTPUT_FILE_PNG ) ] );
|
||||
}
|
||||
|
||||
rsort( $paths );
|
||||
foreach ( $paths as $path ) {
|
||||
$current_path = $dir_path . '/' . $path;
|
||||
|
||||
if ( is_dir( $current_path ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_directory', true, basename( $current_path ), $current_path ) ) {
|
||||
$list = array_merge(
|
||||
$list,
|
||||
$this->find_files_in_directory( $current_path, $allowed_source_exts, trim( $path_prefix . '/' . $path, '/' ) )
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$filename = basename( $current_path );
|
||||
$parts = array_reverse( explode( '.', $filename ) );
|
||||
if ( in_array( strtolower( $parts[0] ?? '' ), $allowed_source_exts ) && ! in_array( strtolower( $parts[1] ?? '' ), ExcludedPathsOperator::EXCLUDED_SUB_EXTENSIONS ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_file', true, $filename, $current_path ) ) {
|
||||
$list[] = trim( $path_prefix . '/' . $path, '/' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronInitiator;
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* .
|
||||
*/
|
||||
class CronConversionEndpoint extends EndpointAbstract {
|
||||
|
||||
const ROUTE_NONCE_HEADER = 'Webpc-Nonce';
|
||||
|
||||
/**
|
||||
* @var CronInitiator
|
||||
*/
|
||||
private $cron_initiator;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronInitiator $cron_initiator = null,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->cron_initiator = $cron_initiator ?: new CronInitiator( $plugin_data, $token_repository, $format_factory );
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'cron-conversion';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool {
|
||||
$nonce_value = $this->cron_status_manager->get_conversion_request_id();
|
||||
if ( $nonce_value === null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( $request_nonce === $nonce_value );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_url(): string {
|
||||
return get_rest_url(
|
||||
null,
|
||||
sprintf(
|
||||
'%1$s/%2$s',
|
||||
EndpointIntegrator::ROUTE_NAMESPACE,
|
||||
self::get_route_name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_nonce(): string {
|
||||
return ( new CronStatusManager() )->refresh_conversion_request_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_nonce_header(): string {
|
||||
return self::ROUTE_NONCE_HEADER;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$request_id = $request->get_header( $this->get_route_nonce_header() ) ?: '';
|
||||
$this->cron_initiator->init_conversion( $request_id );
|
||||
|
||||
return new \WP_REST_Response( null, 200 );
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports image conversion method.
|
||||
*/
|
||||
abstract class EndpointAbstract implements EndpointInterface {
|
||||
|
||||
const ROUTE_NONCE_HEADER = 'X-WP-Nonce';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool {
|
||||
return (bool) wp_verify_nonce( $request_nonce, 'wp_rest' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_url(): string {
|
||||
return get_rest_url(
|
||||
null,
|
||||
sprintf(
|
||||
'%1$s/%2$s',
|
||||
EndpointIntegrator::ROUTE_NAMESPACE,
|
||||
static::get_route_name()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_nonce(): string {
|
||||
return wp_create_nonce( 'wp_rest' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_nonce_header(): string {
|
||||
return self::ROUTE_NONCE_HEADER;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
|
||||
/**
|
||||
* Integrates endpoint class by registering REST API route.
|
||||
*/
|
||||
class EndpointIntegrator implements HookableInterface {
|
||||
|
||||
const ROUTE_NAMESPACE = 'webp-converter/v1';
|
||||
|
||||
/**
|
||||
* Objects of supported REST API endpoints.
|
||||
*
|
||||
* @var EndpointInterface
|
||||
*/
|
||||
private $endpoint_object;
|
||||
|
||||
public function __construct( EndpointInterface $endpoint_object ) {
|
||||
$this->endpoint_object = $endpoint_object;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_action( 'rest_api_init', [ $this, 'register_rest_route' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers new endpoint in REST API.
|
||||
*
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function register_rest_route() {
|
||||
register_rest_route(
|
||||
self::ROUTE_NAMESPACE,
|
||||
$this->endpoint_object->get_route_name(),
|
||||
[
|
||||
'methods' => $this->endpoint_object->get_http_methods(),
|
||||
'permission_callback' => function ( \WP_REST_Request $request ) {
|
||||
$header_value = $request->get_header( $this->endpoint_object->get_route_nonce_header() );
|
||||
if ( $header_value === null ) {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_token_not_found',
|
||||
__( 'Sorry, you do not have permission to do that.' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
|
||||
[ 'status' => rest_authorization_required_code() ]
|
||||
);
|
||||
} elseif ( ! $this->endpoint_object->is_valid_request( $header_value ) ) {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_token_invalid',
|
||||
__( 'Sorry, you do not have permission to do that.' ), // phpcs:ignore WordPress.WP.I18n.MissingArgDomain
|
||||
[ 'status' => rest_authorization_required_code() ]
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
'callback' => [ $this, 'get_route_response' ],
|
||||
'args' => $this->endpoint_object->get_route_args(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WP_REST_Request $request .
|
||||
*
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
* @internal
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
nocache_headers();
|
||||
do_action( 'litespeed_control_set_nocache', 'Converter for Media' );
|
||||
|
||||
return $this->endpoint_object->get_route_response( $request );
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Interface for class that supports endpoint.
|
||||
*/
|
||||
interface EndpointInterface {
|
||||
|
||||
/**
|
||||
* Returns route of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_name(): string;
|
||||
|
||||
/**
|
||||
* Returns methods separated by space.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_http_methods(): string;
|
||||
|
||||
/**
|
||||
* Returns whether request can be executed.
|
||||
*
|
||||
* @param string $request_nonce .
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_valid_request( string $request_nonce ): bool;
|
||||
|
||||
/**
|
||||
* Returns list of params for endpoint.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function get_route_args(): array;
|
||||
|
||||
/**
|
||||
* Returns URL of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_url(): string;
|
||||
|
||||
/**
|
||||
* Returns authorization code of endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_route_nonce(): string;
|
||||
|
||||
/**
|
||||
* Returns header name with nonce value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_route_nonce_header(): string;
|
||||
|
||||
/**
|
||||
* Returns response to endpoint.
|
||||
*
|
||||
* @param \WP_REST_Request $request REST request object.
|
||||
*
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
* @internal
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request );
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\FilesTreeFinder;
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Generated tree of files that can be optimized.
|
||||
*/
|
||||
class FilesStatsEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
public function __construct( PluginData $plugin_data, FormatFactory $format_factory ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'images-stats';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::READABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
if ( ! in_array( AvifFormat::FORMAT_EXTENSION, $allowed_formats ) ) {
|
||||
$allowed_formats[] = AvifFormat::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
$stats_data = ( new FilesTreeFinder( $this->plugin_data, $this->format_factory ) )
|
||||
->get_tree( $allowed_formats );
|
||||
|
||||
return new \WP_REST_Response(
|
||||
[
|
||||
'value_webp_all' => ( $stats_data['files_converted'][ WebpFormat::FORMAT_EXTENSION ] + $stats_data['files_unconverted'][ WebpFormat::FORMAT_EXTENSION ] ),
|
||||
'value_webp_converted' => $stats_data['files_converted'][ WebpFormat::FORMAT_EXTENSION ],
|
||||
'value_avif_all' => ( $stats_data['files_converted'][ AvifFormat::FORMAT_EXTENSION ] + $stats_data['files_unconverted'][ AvifFormat::FORMAT_EXTENSION ] ),
|
||||
'value_avif_converted' => $stats_data['files_converted'][ AvifFormat::FORMAT_EXTENSION ],
|
||||
'tree' => $stats_data['files_tree'],
|
||||
],
|
||||
200
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\PathsFinder;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Supports endpoint to get list of image paths to be converted.
|
||||
*/
|
||||
class PathsEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository;
|
||||
$this->format_factory = $format_factory;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'paths';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'regenerate_force' => [
|
||||
'description' => 'Option to force all images to be converted again (set `1` to enable)',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'sanitize_callback' => function ( $value ) {
|
||||
return ( (string) $value === '1' );
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked( true, true );
|
||||
$this->cron_status_manager->set_paths_to_conversion( [] );
|
||||
$this->cron_status_manager->set_paths_skipped( [] );
|
||||
|
||||
$params = $request->get_params();
|
||||
$skip_converted = ( $params['regenerate_force'] !== true );
|
||||
$paths = ( new PathsFinder( $this->plugin_data, $this->token_repository, $this->format_factory ) )
|
||||
->get_paths_by_chunks( $skip_converted );
|
||||
|
||||
if ( ! $paths ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked( false );
|
||||
}
|
||||
|
||||
return new \WP_REST_Response(
|
||||
$paths,
|
||||
200
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
/**
|
||||
* Supports endpoint for converting all sub-sizes of attachment.
|
||||
*/
|
||||
class RegenerateAttachmentEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'regenerate-attachment';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'post_id' => [
|
||||
'description' => 'Attachment ID',
|
||||
'required' => true,
|
||||
'validate_callback' => function ( $value ) {
|
||||
return is_int( $value );
|
||||
},
|
||||
],
|
||||
'quality_level' => [
|
||||
'description' => 'Conversion strategy',
|
||||
'required' => true,
|
||||
'validate_callback' => function ( $value ) {
|
||||
return in_array(
|
||||
$value,
|
||||
array_merge(
|
||||
apply_filters( 'webpc_option_quality_levels', [ '75', '80', '85', '90', '95' ] ),
|
||||
[ '0' ]
|
||||
)
|
||||
);
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$params = $request->get_params();
|
||||
|
||||
return new \WP_REST_Response(
|
||||
[
|
||||
'output_html' => $this->convert_images( $params['post_id'], $params['quality_level'] ),
|
||||
],
|
||||
200
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes image conversion to output formats.
|
||||
*
|
||||
* @param int $post_id .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return string|false Status of conversion.
|
||||
*/
|
||||
public function convert_images( int $post_id, int $quality_level ) {
|
||||
do_action( 'webpc_convert_attachment', $post_id, true, $quality_level );
|
||||
|
||||
return apply_filters( 'webpc_attachment_stats', '', $post_id, $quality_level );
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Endpoint;
|
||||
|
||||
use WebpConverter\Conversion\Cron\CronStatusManager;
|
||||
use WebpConverter\Conversion\Method\MethodFactory;
|
||||
use WebpConverter\Conversion\Method\MethodIntegrator;
|
||||
use WebpConverter\PluginData;
|
||||
|
||||
/**
|
||||
* Supports endpoint for converting list of paths to images.
|
||||
*/
|
||||
class RegenerateEndpoint extends EndpointAbstract {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
/**
|
||||
* @var CronStatusManager
|
||||
*/
|
||||
private $cron_status_manager;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
MethodFactory $method_factory,
|
||||
CronStatusManager $cron_status_manager = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
$this->cron_status_manager = $cron_status_manager ?: new CronStatusManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function get_route_name(): string {
|
||||
return 'regenerate';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_http_methods(): string {
|
||||
return \WP_REST_Server::CREATABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_args(): array {
|
||||
return array_merge(
|
||||
parent::get_route_args(),
|
||||
[
|
||||
'regenerate_force' => [
|
||||
'description' => 'Option to force all images to be converted again (set `1` to enable)',
|
||||
'required' => false,
|
||||
'default' => false,
|
||||
'sanitize_callback' => function ( $value ) {
|
||||
return ( (string) $value === '1' );
|
||||
},
|
||||
],
|
||||
'paths' => [
|
||||
'description' => 'Array of file paths (server paths)',
|
||||
'required' => true,
|
||||
'default' => [],
|
||||
'validate_callback' => function ( $value ) {
|
||||
return ( is_array( $value ) && $value );
|
||||
},
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_route_response( \WP_REST_Request $request ) {
|
||||
$this->cron_status_manager->set_conversion_status_locked();
|
||||
|
||||
$params = $request->get_params();
|
||||
$data = $this->convert_images( $params['paths'], $params['regenerate_force'] );
|
||||
|
||||
if ( $data !== false ) {
|
||||
return new \WP_REST_Response(
|
||||
$data,
|
||||
200
|
||||
);
|
||||
} else {
|
||||
return new \WP_Error(
|
||||
'webpc_rest_api_error',
|
||||
'',
|
||||
[
|
||||
'status' => 405,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes image conversion to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of source images.
|
||||
* @param bool $regenerate_force .
|
||||
*
|
||||
* @return mixed[]|false Status of conversion.
|
||||
*/
|
||||
public function convert_images( array $paths, bool $regenerate_force ) {
|
||||
$response = ( new MethodIntegrator( $this->plugin_data, $this->method_factory ) )->init_conversion( $paths, $regenerate_force );
|
||||
if ( $response === null ) {
|
||||
return false;
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ExcludedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Removes from list of source directory paths those that are excluded.
|
||||
*/
|
||||
class ExcludedPathsOperator implements HookableInterface {
|
||||
|
||||
const EXCLUDED_SUB_EXTENSIONS = [
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'bk',
|
||||
'bak',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $excluded_dirs = [
|
||||
'.',
|
||||
'..',
|
||||
'.git',
|
||||
'.svn',
|
||||
'node_modules',
|
||||
'wpmc-trash',
|
||||
'__MACOSX',
|
||||
'ShortpixelBackups',
|
||||
'backup',
|
||||
'wio_backup',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$saved_dirs = $plugin_settings[ ExcludedDirectoriesOption::OPTION_NAME ];
|
||||
$this->excluded_dirs = array_merge(
|
||||
$this->excluded_dirs,
|
||||
( $saved_dirs !== '' ) ? explode( ',', $saved_dirs ) : []
|
||||
);
|
||||
|
||||
add_filter( 'webpc_supported_source_directory', [ $this, 'skip_excluded_directory' ], 0, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the status if the given directory path should be converted.
|
||||
*
|
||||
* @param bool $path_status .
|
||||
* @param string $dirname .
|
||||
* @param string $server_path .
|
||||
*
|
||||
* @return bool Status if the given path is not excluded.
|
||||
* @internal
|
||||
*/
|
||||
public function skip_excluded_directory( bool $path_status, string $dirname, string $server_path ): bool {
|
||||
if ( in_array( $dirname, $this->excluded_dirs ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $path_status;
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Returns tree of paths to files to conversion.
|
||||
*/
|
||||
class FilesTreeFinder {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $files_converted;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
private $files_unconverted;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
ServerConfigurator $server_configurator = null,
|
||||
StatsManager $stats_manager = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images for directory.
|
||||
*
|
||||
* @param string[] $output_formats Allowed extensions.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type int[] $files_converted .
|
||||
* @type int[] $files_unconverted .
|
||||
* @type mixed[] $files_tree .
|
||||
* }
|
||||
* @internal
|
||||
*/
|
||||
public function get_tree( array $output_formats ): array {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time( 900 );
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
$this->files_converted[ $output_format ] = 0;
|
||||
$this->files_unconverted[ $output_format ] = 0;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
$force_convert_crashed = ( in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
$values = [];
|
||||
foreach ( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] as $dir_name ) {
|
||||
$source_dir = apply_filters( 'webpc_dir_path', '', $dir_name );
|
||||
$values[] = $this->find_tree_in_directory(
|
||||
$source_dir,
|
||||
$plugin_settings[ SupportedExtensionsOption::OPTION_NAME ],
|
||||
$output_formats,
|
||||
$force_convert_deleted,
|
||||
$force_convert_crashed
|
||||
);
|
||||
}
|
||||
|
||||
$this->stats_manager->set_images_webp_all( $this->files_converted[ WebpFormat::FORMAT_EXTENSION ] + $this->files_unconverted[ WebpFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_webp_unconverted( $this->files_unconverted[ WebpFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_avif_all( $this->files_converted[ AvifFormat::FORMAT_EXTENSION ] + $this->files_unconverted[ AvifFormat::FORMAT_EXTENSION ] );
|
||||
$this->stats_manager->set_images_avif_unconverted( $this->files_unconverted[ AvifFormat::FORMAT_EXTENSION ] );
|
||||
|
||||
return [
|
||||
'files_converted' => $this->files_converted,
|
||||
'files_unconverted' => $this->files_unconverted,
|
||||
'files_tree' => $values,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of source images as tree.
|
||||
*
|
||||
* @param string $dir_path Server path of source directory.
|
||||
* @param string[] $source_formats Allowed extensions.
|
||||
* @param string[] $output_formats Allowed extensions.
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
* @param int $nesting_level .
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
private function find_tree_in_directory(
|
||||
string $dir_path,
|
||||
array $source_formats,
|
||||
array $output_formats,
|
||||
bool $force_convert_deleted,
|
||||
bool $force_convert_crashed,
|
||||
int $nesting_level = 0
|
||||
): array {
|
||||
$paths = scandir( $dir_path );
|
||||
$list = [
|
||||
'name' => basename( $dir_path ),
|
||||
'items' => [],
|
||||
'files' => [],
|
||||
'count' => 0,
|
||||
];
|
||||
if ( ! is_array( $paths ) ) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
if ( $nesting_level === 0 ) {
|
||||
$paths = array_diff( $paths, [ basename( RewritesErrorsDetector::PATH_OUTPUT_FILE_PNG ) ] );
|
||||
}
|
||||
|
||||
sort( $paths, SORT_NATURAL | SORT_FLAG_CASE );
|
||||
foreach ( $paths as $path ) {
|
||||
$current_path = $dir_path . '/' . $path;
|
||||
|
||||
if ( is_dir( $current_path ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_directory', true, basename( $current_path ), $current_path ) ) {
|
||||
$children = $this->find_tree_in_directory( $current_path, $source_formats, $output_formats, $force_convert_deleted, $force_convert_crashed, ( $nesting_level + 1 ) );
|
||||
if ( $children['items'] || $children['files'] ) {
|
||||
$list['items'][] = $children;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$filename = basename( $current_path );
|
||||
$parts = array_reverse( explode( '.', $filename ) );
|
||||
if ( in_array( strtolower( $parts[0] ?? '' ), $source_formats ) && ! in_array( strtolower( $parts[1] ?? '' ), ExcludedPathsOperator::EXCLUDED_SUB_EXTENSIONS ) ) {
|
||||
if ( apply_filters( 'webpc_supported_source_file', true, $filename, $current_path )
|
||||
&& ! $this->is_converted_file( $current_path, $output_formats, $force_convert_deleted, $force_convert_crashed ) ) {
|
||||
$list['files'][] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$list['count'] = $this->calculate_tree_count( $list );
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string[] $output_formats .
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_converted_file( string $source_path, array $output_formats, bool $force_convert_deleted, bool $force_convert_crashed ): bool {
|
||||
$is_not_converted = false;
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
if ( $output_path === null ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( file_exists( $output_path ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} elseif ( ! $force_convert_deleted && file_exists( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} elseif ( ! $force_convert_crashed && file_exists( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} else {
|
||||
$this->files_unconverted[ $output_format ]++;
|
||||
$is_not_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ! $is_not_converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tree .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private function calculate_tree_count( array $tree ): int {
|
||||
$count = count( $tree['files'] );
|
||||
|
||||
foreach ( $tree['items'] as $tree_item ) {
|
||||
$count += $this->calculate_tree_count( $tree_item );
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Supports AVIF as output format for images.
|
||||
*/
|
||||
class AvifFormat extends FormatAbstract {
|
||||
|
||||
const FORMAT_EXTENSION = 'avif';
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
public function __construct( TokenRepository $token_repository ) {
|
||||
$this->token_repository = $token_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_extension(): string {
|
||||
return self::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_mime_type(): string {
|
||||
return 'image/avif';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
if ( $this->token_repository->get_token()->get_valid_status() ) {
|
||||
return 'AVIF';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s (%2$s)',
|
||||
'AVIF',
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'available in %1$sthe PRO version%2$s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-field-output-format-avif-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool {
|
||||
return ( $this->token_repository->get_token()->get_valid_status() && ( $conversion_method === RemoteMethod::METHOD_NAME ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports output format for images.
|
||||
*/
|
||||
abstract class FormatAbstract implements FormatInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return sprintf( '.%s', $this->get_extension() );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool {
|
||||
switch ( $conversion_method ) {
|
||||
case ImagickMethod::METHOD_NAME:
|
||||
return ImagickMethod::is_method_active( $this->get_extension() );
|
||||
case GdMethod::METHOD_NAME:
|
||||
return GdMethod::is_method_active( $this->get_extension() );
|
||||
case RemoteMethod::METHOD_NAME:
|
||||
return RemoteMethod::is_method_active( $this->get_extension() );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
|
||||
/**
|
||||
* Adds support for all output formats and returns information about them.
|
||||
*/
|
||||
class FormatFactory {
|
||||
|
||||
/**
|
||||
* Objects of supported output formats.
|
||||
*
|
||||
* @var FormatInterface[]
|
||||
*/
|
||||
private $formats = [];
|
||||
|
||||
/**
|
||||
* @var string[][]
|
||||
*/
|
||||
private $available_formats = [];
|
||||
|
||||
public function __construct( TokenRepository $token_repository ) {
|
||||
$this->set_integration( new AvifFormat( $token_repository ) );
|
||||
$this->set_integration( new WebpFormat() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for format.
|
||||
*
|
||||
* @param FormatInterface $format .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( FormatInterface $format ) {
|
||||
$this->formats[] = $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of output formats.
|
||||
*
|
||||
* @return string[] Extensions of output formats with labels.
|
||||
*/
|
||||
public function get_formats(): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
$values[ $format->get_extension() ] = $format->get_label();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of available output formats.
|
||||
*
|
||||
* @param string|null $conversion_method Name of conversion method.
|
||||
*
|
||||
* @return string[] Extensions of output formats with labels.
|
||||
*/
|
||||
public function get_available_formats( string $conversion_method = null ): array {
|
||||
if ( $conversion_method === null ) {
|
||||
return [];
|
||||
} elseif ( isset( $this->available_formats[ $conversion_method ] ) ) {
|
||||
return $this->available_formats[ $conversion_method ];
|
||||
}
|
||||
|
||||
$this->available_formats[ $conversion_method ] = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
if ( ! $format->is_available( $conversion_method ) ) {
|
||||
continue;
|
||||
}
|
||||
$this->available_formats[ $conversion_method ][ $format->get_extension() ] = $format->get_label();
|
||||
}
|
||||
return $this->available_formats[ $conversion_method ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function reset_available_formats() {
|
||||
$this->available_formats = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns extensions of output formats.
|
||||
*
|
||||
* @return string[] Extensions of output formats.
|
||||
*/
|
||||
public function get_format_extensions(): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
$values[] = $format->get_extension();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns mime types of output formats.
|
||||
*
|
||||
* @param string[]|null $output_formats Extensions of output formats.
|
||||
*
|
||||
* @return string[] Mime types of output formats.
|
||||
*/
|
||||
public function get_mime_types( array $output_formats = null ): array {
|
||||
$values = [];
|
||||
foreach ( $this->formats as $format ) {
|
||||
if ( ( $output_formats !== null ) && ! in_array( $format->get_extension(), $output_formats ) ) {
|
||||
continue;
|
||||
}
|
||||
$values[ $format->get_extension() ] = $format->get_mime_type();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
/**
|
||||
* Interface for class that supports output format for images.
|
||||
*/
|
||||
interface FormatInterface {
|
||||
|
||||
/**
|
||||
* Returns extension of output format.
|
||||
*
|
||||
* @return string Format extension
|
||||
*/
|
||||
public function get_extension(): string;
|
||||
|
||||
/**
|
||||
* Returns mime type of output format.
|
||||
*
|
||||
* @return string Format mime type
|
||||
*/
|
||||
public function get_mime_type(): string;
|
||||
|
||||
/**
|
||||
* Returns label of output format.
|
||||
*
|
||||
* @return string Format label.
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* Returns status is output format available?
|
||||
*
|
||||
* @param string $conversion_method Type of conversion method.
|
||||
*
|
||||
* @return bool Is format available?
|
||||
*/
|
||||
public function is_available( string $conversion_method ): bool;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Format;
|
||||
|
||||
/**
|
||||
* Supports WebP as output format for images.
|
||||
*/
|
||||
class WebpFormat extends FormatAbstract {
|
||||
|
||||
const FORMAT_EXTENSION = 'webp';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_extension(): string {
|
||||
return self::FORMAT_EXTENSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_mime_type(): string {
|
||||
return 'image/webp';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'WebP';
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Exception;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
|
||||
/**
|
||||
* Deletes output after conversion if it is larger than original.
|
||||
*/
|
||||
class LargerFilesOperator {
|
||||
|
||||
const DELETED_FILE_EXTENSION = 'deleted';
|
||||
|
||||
/**
|
||||
* Removes converted output image if it is larger than original image.
|
||||
*
|
||||
* @param string $output_path .
|
||||
* @param string $source_path .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception\LargerThanOriginalException
|
||||
*/
|
||||
public function remove_image_if_is_larger( string $output_path, string $source_path, array $plugin_settings ) {
|
||||
if ( file_exists( $output_path . '.' . self::DELETED_FILE_EXTENSION ) ) {
|
||||
unlink( $output_path . '.' . self::DELETED_FILE_EXTENSION );
|
||||
}
|
||||
|
||||
if ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] )
|
||||
|| ( ! file_exists( $output_path ) || ! file_exists( $source_path ) )
|
||||
|| ( filesize( $output_path ) < filesize( $source_path ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file = fopen( $output_path . '.' . self::DELETED_FILE_EXTENSION, 'w' );
|
||||
if ( $file !== false ) {
|
||||
fclose( $file );
|
||||
unlink( $output_path );
|
||||
}
|
||||
|
||||
throw new Exception\LargerThanOriginalException( [ $source_path, $output_path ] );
|
||||
}
|
||||
}
|
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Exception\ConversionErrorException;
|
||||
use WebpConverter\Exception\ExtensionUnsupportedException;
|
||||
use WebpConverter\Exception\FunctionUnavailableException;
|
||||
use WebpConverter\Exception\ImageAnimatedException;
|
||||
use WebpConverter\Exception\ImageInvalidException;
|
||||
use WebpConverter\Exception\ResolutionOversizeException;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using GD library.
|
||||
*/
|
||||
class GdMethod extends LibraryMethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'gd';
|
||||
const MAX_METHOD_QUALITY = 99.9;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'GD';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return ( extension_loaded( 'gd' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
if ( ! self::is_method_installed() || ! ( $function = self::get_format_function( $format ) ) ) {
|
||||
return false;
|
||||
}
|
||||
return function_exists( $function );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of function to convert source image to output image.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string|null Function name using for conversion.
|
||||
*/
|
||||
private static function get_format_function( string $format ) {
|
||||
switch ( $format ) {
|
||||
case WebpFormat::FORMAT_EXTENSION:
|
||||
return 'imagewebp';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return resource Image object.
|
||||
* @throws ExtensionUnsupportedException
|
||||
* @throws FunctionUnavailableException
|
||||
* @throws ImageInvalidException
|
||||
* @throws ImageAnimatedException
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings ) {
|
||||
$extension = strtolower( pathinfo( $source_path, PATHINFO_EXTENSION ) );
|
||||
$methods = apply_filters(
|
||||
'webpc_gd_create_methods',
|
||||
[
|
||||
'imagecreatefromjpeg' => [ 'jpg', 'jpeg' ],
|
||||
'imagecreatefrompng' => [ 'png' ],
|
||||
'imagecreatefromgif' => [ 'gif' ],
|
||||
]
|
||||
);
|
||||
|
||||
if ( ( $extension === 'gif' ) && $this->is_animated( $source_path ) ) {
|
||||
throw new ImageAnimatedException( $source_path );
|
||||
}
|
||||
|
||||
foreach ( $methods as $method => $extensions ) {
|
||||
if ( ! in_array( $extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ! in_array( $extension, $extensions ) ) {
|
||||
continue;
|
||||
} elseif ( ! function_exists( $method ) ) {
|
||||
throw new FunctionUnavailableException( $method );
|
||||
} elseif ( ! $image = @$method( $source_path ) ) { // phpcs:ignore
|
||||
throw new ImageInvalidException( $source_path );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $image ) ) {
|
||||
throw new ExtensionUnsupportedException( [ $extension, $source_path ] );
|
||||
}
|
||||
|
||||
$exif = ( function_exists( 'exif_read_data' ) )
|
||||
? ( @exif_read_data( $source_path ) ?: [] ) // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
: [];
|
||||
|
||||
switch ( $exif['Orientation'] ?? '' ) {
|
||||
case 2:
|
||||
imageflip( $image, IMG_FLIP_HORIZONTAL );
|
||||
break;
|
||||
case 3:
|
||||
$image = imagerotate( $image, 180, 0 );
|
||||
break;
|
||||
case 4:
|
||||
imageflip( $image, IMG_FLIP_HORIZONTAL );
|
||||
$image = imagerotate( $image, 180, 0 );
|
||||
break;
|
||||
case 5:
|
||||
imageflip( $image, IMG_FLIP_VERTICAL );
|
||||
$image = imagerotate( $image, -90, 0 );
|
||||
break;
|
||||
case 6:
|
||||
$image = imagerotate( $image, -90, 0 );
|
||||
break;
|
||||
case 7:
|
||||
imageflip( $image, IMG_FLIP_VERTICAL );
|
||||
$image = imagerotate( $image, 90, 0 );
|
||||
break;
|
||||
case 8:
|
||||
$image = imagerotate( $image, 90, 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->update_image_resource( $image, $extension );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates image object before converting to output format.
|
||||
*
|
||||
* @param resource $image Image object.
|
||||
* @param string $extension Extension of output format.
|
||||
*
|
||||
* @return resource Image object.
|
||||
* @throws FunctionUnavailableException
|
||||
*/
|
||||
private function update_image_resource( $image, string $extension ) {
|
||||
if ( ! function_exists( 'imageistruecolor' ) ) {
|
||||
throw new FunctionUnavailableException( 'imageistruecolor' );
|
||||
}
|
||||
|
||||
if ( ! imageistruecolor( $image ) ) {
|
||||
if ( ! function_exists( 'imagepalettetotruecolor' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagepalettetotruecolor' );
|
||||
}
|
||||
imagepalettetotruecolor( $image );
|
||||
}
|
||||
|
||||
switch ( $extension ) {
|
||||
case 'png':
|
||||
if ( ! function_exists( 'imagealphablending' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagealphablending' );
|
||||
}
|
||||
imagealphablending( $image, false );
|
||||
|
||||
if ( ! function_exists( 'imagesavealpha' ) ) {
|
||||
throw new FunctionUnavailableException( 'imagesavealpha' );
|
||||
}
|
||||
imagesavealpha( $image, true );
|
||||
break;
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ConversionErrorException
|
||||
* @throws FunctionUnavailableException
|
||||
* @throws ResolutionOversizeException
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings ) {
|
||||
$function = self::get_format_function( $format );
|
||||
if ( $function === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image = apply_filters( 'webpc_gd_before_saving', $image, $source_path );
|
||||
$output_quality = min( $plugin_settings[ ImagesQualityOption::OPTION_NAME ], self::MAX_METHOD_QUALITY );
|
||||
|
||||
if ( ! function_exists( $function ) ) {
|
||||
throw new FunctionUnavailableException( $function );
|
||||
} elseif ( ( imagesx( $image ) > 8192 ) || ( imagesy( $image ) > 8192 ) ) {
|
||||
throw new ResolutionOversizeException( $source_path );
|
||||
} elseif ( is_callable( $function ) && ! $function( $image, $output_path, $output_quality ) ) {
|
||||
throw new ConversionErrorException( $source_path );
|
||||
}
|
||||
|
||||
if ( filesize( $output_path ) % 2 === 1 ) {
|
||||
file_put_contents( $output_path, "\0", FILE_APPEND );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
*
|
||||
* @link https://www.php.net/manual/en/function.imagecreatefromgif.php#104473
|
||||
*/
|
||||
private function is_animated( string $source_path ): bool {
|
||||
if ( ! ( $fh = @fopen( $source_path, 'rb' ) ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
return false;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
while ( ! feof( $fh ) && ( $count < 2 ) ) {
|
||||
$chunk = fread( $fh, 1024 * 100 );
|
||||
$count = $count + preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk ?: '', $matches );
|
||||
}
|
||||
|
||||
fclose( $fh );
|
||||
return ( $count > 1 );
|
||||
}
|
||||
}
|
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Exception\ConversionErrorException;
|
||||
use WebpConverter\Exception\ExtensionUnsupportedException;
|
||||
use WebpConverter\Exception\ImageAnimatedException;
|
||||
use WebpConverter\Exception\ImageInvalidException;
|
||||
use WebpConverter\Exception\ImagickNotSupportWebpException;
|
||||
use WebpConverter\Exception\ImagickUnavailableException;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using Imagick library.
|
||||
*/
|
||||
class ImagickMethod extends LibraryMethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'imagick';
|
||||
const MAX_METHOD_QUALITY = 99.9;
|
||||
const PROTECTED_IMAGE_PROFILES = [
|
||||
'icc',
|
||||
'icm',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
return 'Imagick';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return ( extension_loaded( 'imagick' ) && class_exists( '\Imagick' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
if ( ! self::is_method_installed()
|
||||
|| ! ( $formats = \Imagick::queryformats( 'WEBP' ) )
|
||||
|| ! ( $extension = self::get_format_extension( $format ) ) ) {
|
||||
return false;
|
||||
}
|
||||
return in_array( $extension, $formats );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of supported format to convert source image to output image.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string|null Supported format using for conversion.
|
||||
*/
|
||||
private static function get_format_extension( string $format ) {
|
||||
switch ( $format ) {
|
||||
case WebpFormat::FORMAT_EXTENSION:
|
||||
return 'WEBP';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Imagick .
|
||||
* @throws ExtensionUnsupportedException
|
||||
* @throws ImagickUnavailableException
|
||||
* @throws ImageInvalidException
|
||||
* @throws ImageAnimatedException
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings ) {
|
||||
$extension = strtolower( pathinfo( $source_path, PATHINFO_EXTENSION ) );
|
||||
|
||||
if ( ! extension_loaded( 'imagick' ) || ! class_exists( 'Imagick' ) ) {
|
||||
throw new ImagickUnavailableException();
|
||||
} elseif ( ! in_array( $extension, $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] ) ) {
|
||||
throw new ExtensionUnsupportedException( [ $extension, $source_path ] );
|
||||
}
|
||||
|
||||
try {
|
||||
$imagick = new \Imagick( $source_path );
|
||||
if ( ( $extension === 'gif' ) && ( $imagick->identifyFormat( '%n' ) > 1 ) ) {
|
||||
throw new ImageAnimatedException( $source_path );
|
||||
}
|
||||
|
||||
switch ( $imagick->getImageProperty( 'exif:Orientation' ) ) {
|
||||
case 2:
|
||||
$imagick->flopImage();
|
||||
break;
|
||||
case 3:
|
||||
$imagick->rotateImage( '#000000', 180 );
|
||||
break;
|
||||
case 4:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', 180 );
|
||||
break;
|
||||
case 5:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', -90 );
|
||||
break;
|
||||
case 6:
|
||||
$imagick->rotateImage( '#000000', 90 );
|
||||
break;
|
||||
case 7:
|
||||
$imagick->flopImage();
|
||||
$imagick->rotateImage( '#000000', 90 );
|
||||
break;
|
||||
case 8:
|
||||
$imagick->rotateImage( '#000000', -90 );
|
||||
break;
|
||||
}
|
||||
|
||||
return $imagick;
|
||||
} catch ( \ImagickException $e ) {
|
||||
throw new ImageInvalidException( $source_path );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ConversionErrorException
|
||||
* @throws ImagickNotSupportWebpException
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings ) {
|
||||
$extension = self::get_format_extension( $format );
|
||||
$image = apply_filters( 'webpc_imagick_before_saving', $image, $source_path );
|
||||
$output_quality = min( $plugin_settings[ ImagesQualityOption::OPTION_NAME ], self::MAX_METHOD_QUALITY );
|
||||
|
||||
if ( ! in_array( $extension, $image->queryFormats() ) ) {
|
||||
throw new ImagickNotSupportWebpException();
|
||||
}
|
||||
|
||||
$image->setImageFormat( $extension );
|
||||
if ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_KEEP_METADATA, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) ) {
|
||||
$image_profiles = $image->getImageProfiles( '*', true );
|
||||
foreach ( $image_profiles as $profile_name => $image_profile ) {
|
||||
if ( ! in_array( $profile_name, self::PROTECTED_IMAGE_PROFILES ) ) {
|
||||
try {
|
||||
$image->removeImageProfile( $profile_name );
|
||||
} catch ( \ImagickException $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$image->setImageCompressionQuality( $output_quality );
|
||||
$blob = $image->getImageBlob();
|
||||
|
||||
if ( ! file_put_contents( $output_path, $blob ) ) {
|
||||
throw new ConversionErrorException( $source_path );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Exception\ExceptionInterface;
|
||||
use WebpConverter\Exception\LargerThanOriginalException;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Abstract class for class that converts images using the PHP library.
|
||||
*/
|
||||
abstract class LibraryMethodAbstract extends MethodAbstract implements LibraryMethodInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force ) {
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
foreach ( $paths as $path ) {
|
||||
$this->files_available[ $output_format ]++;
|
||||
$this->convert_path( $path, $output_format, $plugin_settings );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts source path to output formats.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param string $format Extension of output format.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function convert_path( string $path, string $format, array $plugin_settings ) {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time();
|
||||
|
||||
try {
|
||||
$source_path = $this->get_image_source_path( $path );
|
||||
$output_path = $this->get_image_output_path( $source_path, $format );
|
||||
|
||||
$this->skip_crashed->create_crashed_file( $output_path );
|
||||
|
||||
$image = $this->create_image_by_path( $source_path, $plugin_settings );
|
||||
$this->convert_image_to_output( $image, $source_path, $output_path, $format, $plugin_settings );
|
||||
do_action( 'webpc_after_conversion', $output_path, $source_path );
|
||||
|
||||
$this->files_converted[ $format ]++;
|
||||
|
||||
$this->skip_crashed->delete_crashed_file( $output_path );
|
||||
$this->skip_larger->remove_image_if_is_larger( $output_path, $source_path, $plugin_settings );
|
||||
$this->update_conversion_stats( $source_path, $output_path, $format );
|
||||
} catch ( LargerThanOriginalException $e ) {
|
||||
$this->files_converted[ $format ]--;
|
||||
} catch ( ExceptionInterface $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
} catch ( \Exception $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
/**
|
||||
* Interface for class that converts images using the PHP library.
|
||||
*/
|
||||
interface LibraryMethodInterface {
|
||||
|
||||
/**
|
||||
* Creates image object based on source path.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return mixed Image object.
|
||||
*/
|
||||
public function create_image_by_path( string $source_path, array $plugin_settings );
|
||||
|
||||
/**
|
||||
* Converts image and saves to output location.
|
||||
*
|
||||
* @param mixed $image Image object.
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $output_path Server path for output image.
|
||||
* @param string $format Extension of output format.
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function convert_image_to_output( $image, string $source_path, string $output_path, string $format, array $plugin_settings );
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\Exception;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
|
||||
/**
|
||||
* Abstract class for class that converts images.
|
||||
*/
|
||||
abstract class MethodAbstract implements MethodInterface {
|
||||
|
||||
/**
|
||||
* @var CrashedFilesOperator
|
||||
*/
|
||||
protected $skip_crashed;
|
||||
|
||||
/**
|
||||
* @var LargerFilesOperator
|
||||
*/
|
||||
protected $skip_larger;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
protected $server_configurator;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
public function __construct(
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed,
|
||||
LargerFilesOperator $skip_larger,
|
||||
ServerConfigurator $server_configurator,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->skip_crashed = $skip_crashed;
|
||||
$this->skip_larger = $skip_larger;
|
||||
$this->server_configurator = $server_configurator;
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
}
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $is_fatal_error = false;
|
||||
|
||||
/**
|
||||
* Messages of errors that occurred during conversion.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $errors = [];
|
||||
|
||||
/**
|
||||
* Sum of size of source images before conversion.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $size_before = 0;
|
||||
|
||||
/**
|
||||
* Sum of size of output images after conversion.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $size_after = 0;
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $files_available = [
|
||||
WebpFormat::FORMAT_EXTENSION => 0,
|
||||
AvifFormat::FORMAT_EXTENSION => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int[]
|
||||
*/
|
||||
protected $files_converted = [
|
||||
WebpFormat::FORMAT_EXTENSION => 0,
|
||||
AvifFormat::FORMAT_EXTENSION => 0,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_fatal_error(): bool {
|
||||
return $this->is_fatal_error;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_errors(): array {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_size_before(): int {
|
||||
return $this->size_before;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_files_available( string $output_format ): int {
|
||||
return $this->files_available[ $output_format ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_files_converted( string $output_format ): int {
|
||||
return $this->files_converted[ $output_format ];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_size_after(): int {
|
||||
return $this->size_after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks server path of source image.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
*
|
||||
* @return string Server path of source image.
|
||||
*
|
||||
* @throws Exception\SourcePathException
|
||||
*/
|
||||
protected function get_image_source_path( string $source_path ): string {
|
||||
if ( ! is_readable( $source_path ) ) {
|
||||
throw new Exception\SourcePathException( $source_path );
|
||||
}
|
||||
|
||||
return $source_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns server path for output image.
|
||||
*
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return string Server path of output image.
|
||||
*
|
||||
* @throws Exception\OutputPathException
|
||||
*/
|
||||
protected function get_image_output_path( string $source_path, string $format ): string {
|
||||
if ( ! $output_path = $this->output_path->get_path( $source_path, true, $format ) ) {
|
||||
throw new Exception\OutputPathException( $source_path );
|
||||
}
|
||||
|
||||
return $output_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path Server path of source image.
|
||||
* @param string $output_path Server path of output image.
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update_conversion_stats( string $source_path, string $output_path, string $output_format ) {
|
||||
$output_exist = file_exists( $output_path );
|
||||
$size_before = filesize( $source_path );
|
||||
$size_after = ( $output_exist ) ? filesize( $output_path ) : $size_before;
|
||||
|
||||
$this->size_before += $size_before ?: 0;
|
||||
$this->size_after += $size_after ?: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $error_message .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param bool $is_fatal_error .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function save_conversion_error( string $error_message, array $plugin_settings, bool $is_fatal_error = false ) {
|
||||
if ( $is_fatal_error ) {
|
||||
$this->is_fatal_error = true;
|
||||
}
|
||||
|
||||
$this->errors[] = $error_message;
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
|
||||
/**
|
||||
* Adds support for all conversion methods and returns information about them.
|
||||
*/
|
||||
class MethodFactory {
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var CrashedFilesOperator
|
||||
*/
|
||||
private $skip_crashed;
|
||||
|
||||
/**
|
||||
* @var LargerFilesOperator
|
||||
*/
|
||||
private $skip_larger;
|
||||
|
||||
/**
|
||||
* @var ServerConfigurator
|
||||
*/
|
||||
private $server_configurator;
|
||||
|
||||
/**
|
||||
* Objects of supported conversion methods.
|
||||
*
|
||||
* @var MethodInterface[]
|
||||
*/
|
||||
private $methods = [];
|
||||
|
||||
public function __construct(
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed = null,
|
||||
LargerFilesOperator $skip_larger = null,
|
||||
ServerConfigurator $server_configurator = null
|
||||
) {
|
||||
$this->token_repository = $token_repository;
|
||||
$this->format_factory = $format_factory;
|
||||
$this->skip_crashed = $skip_crashed ?: new CrashedFilesOperator();
|
||||
$this->skip_larger = $skip_larger ?: new LargerFilesOperator();
|
||||
$this->server_configurator = $server_configurator ?: new ServerConfigurator();
|
||||
|
||||
$this->set_integration( new ImagickMethod( $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
$this->set_integration( new GdMethod( $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
$this->set_integration( new RemoteMethod( $this->token_repository, $format_factory, $this->skip_crashed, $this->skip_larger, $this->server_configurator ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets integration for method.
|
||||
*
|
||||
* @param MethodInterface $method .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_integration( MethodInterface $method ) {
|
||||
$this->methods[ $method->get_name() ] = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects of conversion methods.
|
||||
*
|
||||
* @return MethodInterface[] .
|
||||
*/
|
||||
public function get_methods_objects(): array {
|
||||
$values = [];
|
||||
foreach ( $this->methods as $method ) {
|
||||
$values[ $method->get_name() ] = $method;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of conversion methods.
|
||||
*
|
||||
* @return string[] Names of conversion methods with labels.
|
||||
*/
|
||||
public function get_methods(): array {
|
||||
$values = [];
|
||||
foreach ( $this->get_methods_objects() as $method_name => $method ) {
|
||||
$values[ $method_name ] = $method->get_label();
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of installed conversion methods.
|
||||
*
|
||||
* @return string[] Names of conversion methods with labels.
|
||||
*/
|
||||
public function get_available_methods(): array {
|
||||
$token_status = $this->token_repository->get_token()->get_valid_status();
|
||||
$values = [];
|
||||
foreach ( $this->get_methods_objects() as $method_name => $method ) {
|
||||
if ( ! $method::is_method_installed()
|
||||
|| ( ! $this->format_factory->get_available_formats( $method_name ) ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( $token_status && $method::is_pro_feature() ) || ( ! $token_status && ! $method::is_pro_feature() ) ) {
|
||||
$values[ $method_name ] = $method->get_label();
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Initializes image conversion using active image conversion method.
|
||||
*/
|
||||
class MethodIntegrator {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var MethodFactory
|
||||
*/
|
||||
private $method_factory;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
public function __construct( PluginData $plugin_data, MethodFactory $method_factory, StatsManager $stats_manager = null ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->method_factory = $method_factory;
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes converting source images using active and set conversion method.
|
||||
*
|
||||
* @param string[] $paths Server paths for source images.
|
||||
* @param bool $regenerate_force .
|
||||
* @param bool $skip_server_errors .
|
||||
* @param int $quality_level .
|
||||
*
|
||||
* @return mixed[]|null Results data of conversion.
|
||||
*/
|
||||
public function init_conversion( array $paths, bool $regenerate_force, bool $skip_server_errors = false, int $quality_level = null ) {
|
||||
if ( ! $skip_server_errors && apply_filters( 'webpc_server_errors', [], true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = $this->get_method_used();
|
||||
if ( $method === null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $quality_level !== null ) {
|
||||
$plugin_settings[ ImagesQualityOption::OPTION_NAME ] = $quality_level;
|
||||
}
|
||||
|
||||
$this->stats_manager->set_images_webp_unconverted();
|
||||
$this->stats_manager->set_images_avif_unconverted();
|
||||
|
||||
$method->convert_paths( $paths, $plugin_settings, $regenerate_force );
|
||||
return [
|
||||
'is_fatal_error' => $method->is_fatal_error(),
|
||||
'errors' => apply_filters( 'webpc_convert_errors', $method->get_errors() ),
|
||||
'files' => [
|
||||
'webp_available' => $method->get_files_available( WebpFormat::FORMAT_EXTENSION ),
|
||||
'webp_converted' => $method->get_files_converted( WebpFormat::FORMAT_EXTENSION ),
|
||||
'avif_available' => $method->get_files_available( AvifFormat::FORMAT_EXTENSION ),
|
||||
'avif_converted' => $method->get_files_converted( AvifFormat::FORMAT_EXTENSION ),
|
||||
],
|
||||
'size' => [
|
||||
'before' => $method->get_size_before(),
|
||||
'after' => $method->get_size_after(),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns active and set conversion method.
|
||||
*
|
||||
* @return MethodInterface|null Object of conversion method.
|
||||
*/
|
||||
public function get_method_used() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ?? null;
|
||||
if ( ! $output_formats ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method_key = $plugin_settings[ ConversionMethodOption::OPTION_NAME ] ?? null;
|
||||
$methods = $this->method_factory->get_methods_objects();
|
||||
foreach ( $methods as $method_name => $method ) {
|
||||
if ( $method_key === $method_name ) {
|
||||
return $method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
/**
|
||||
* Interface for class that converts images.
|
||||
*/
|
||||
interface MethodInterface {
|
||||
|
||||
/**
|
||||
* Returns name of conversion method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_name(): string;
|
||||
|
||||
/**
|
||||
* Returns label of conversion method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_label(): string;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool;
|
||||
|
||||
/**
|
||||
* Returns status of whether method is installed.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_method_installed(): bool;
|
||||
|
||||
/**
|
||||
* Returns status of whether method is active.
|
||||
*
|
||||
* @param string $format Extension of output format.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function is_fatal_error(): bool;
|
||||
|
||||
/**
|
||||
* Returns errors generated during image conversion.
|
||||
*
|
||||
* @return string[] Errors messages.
|
||||
*/
|
||||
public function get_errors(): array;
|
||||
|
||||
/**
|
||||
* Returns weight of source files before converting.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_size_before(): int;
|
||||
|
||||
/**
|
||||
* Returns weight of output files after converting.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_size_after(): int;
|
||||
|
||||
/**
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_files_available( string $output_format ): int;
|
||||
|
||||
/**
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_files_converted( string $output_format ): int;
|
||||
|
||||
/**
|
||||
* Converts source paths to output formats.
|
||||
*
|
||||
* @param string[] $paths Server paths of source images.
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param bool $regenerate_force .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force );
|
||||
}
|
@ -0,0 +1,431 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion\Method;
|
||||
|
||||
use WebpConverter\Conversion\CrashedFilesOperator;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\LargerFilesOperator;
|
||||
use WebpConverter\Exception\ExceptionInterface;
|
||||
use WebpConverter\Exception\FilesizeOversizeException;
|
||||
use WebpConverter\Exception\LargerThanOriginalException;
|
||||
use WebpConverter\Exception\OutputPathException;
|
||||
use WebpConverter\Exception\RemoteErrorResponseException;
|
||||
use WebpConverter\Exception\RemoteRequestException;
|
||||
use WebpConverter\Exception\SourcePathException;
|
||||
use WebpConverter\Model\Token;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\ServerConfigurator;
|
||||
use WebpConverter\Settings\Option\AccessTokenOption;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\ImageResizeOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\WebpConverterConstants;
|
||||
|
||||
/**
|
||||
* Supports image conversion method using remote API.
|
||||
*/
|
||||
class RemoteMethod extends MethodAbstract {
|
||||
|
||||
const METHOD_NAME = 'remote';
|
||||
const MAX_FILESIZE_BYTES = ( 32 * 1024 * 1024 );
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var Token
|
||||
*/
|
||||
private $token;
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $failed_converted_source_files = [];
|
||||
|
||||
public function __construct(
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
CrashedFilesOperator $skip_crashed,
|
||||
LargerFilesOperator $skip_larger,
|
||||
ServerConfigurator $server_configurator
|
||||
) {
|
||||
parent::__construct( $format_factory, $skip_crashed, $skip_larger, $server_configurator );
|
||||
$this->token_repository = $token_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_name(): string {
|
||||
return self::METHOD_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_label(): string {
|
||||
if ( $this->token_repository->get_token()->get_valid_status() ) {
|
||||
return __( 'Remote server', 'webp-converter-for-media' );
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'%1$s (%2$s)',
|
||||
__( 'Remote server', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'available in %1$sthe PRO version%2$s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-field-conversion-method-remote-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_pro_feature(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_installed(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function is_method_active( string $format ): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function convert_paths( array $paths, array $plugin_settings, bool $regenerate_force ) {
|
||||
$this->server_configurator->set_memory_limit();
|
||||
$this->server_configurator->set_execution_time();
|
||||
|
||||
$output_formats = $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
$source_paths = [];
|
||||
$output_paths = [];
|
||||
$this->token = $this->token_repository->get_token();
|
||||
|
||||
foreach ( $output_formats as $output_format ) {
|
||||
try {
|
||||
$file_paths = $this->get_source_paths( $paths, $plugin_settings, $output_format );
|
||||
if ( ! $file_paths ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->files_available[ $output_format ] += count( $file_paths );
|
||||
|
||||
$output_paths[ $output_format ] = $this->get_output_paths( $file_paths, $output_format );
|
||||
$source_paths[ $output_format ] = $file_paths;
|
||||
} catch ( ExceptionInterface $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $regenerate_force ) {
|
||||
foreach ( $source_paths as $output_format => $extensions_paths ) {
|
||||
foreach ( $extensions_paths as $path_index => $extensions_path ) {
|
||||
if ( file_exists( $output_paths[ $output_format ][ $path_index ] )
|
||||
|| ( ! $force_convert_deleted && file_exists( $output_paths[ $output_format ][ $path_index ] . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) ) {
|
||||
unset( $source_paths[ $output_format ][ $path_index ] );
|
||||
unset( $output_paths[ $output_format ][ $path_index ] );
|
||||
|
||||
$this->files_available[ $output_format ]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$converted_files = $this->init_connections( $source_paths, $plugin_settings, $output_paths );
|
||||
$this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings );
|
||||
|
||||
if ( $this->failed_converted_source_files ) {
|
||||
$converted_files = $this->init_connections( $this->failed_converted_source_files, $plugin_settings, $output_paths );
|
||||
$this->save_converted_files( $converted_files, $source_paths, $output_paths, $plugin_settings );
|
||||
}
|
||||
} catch ( RemoteErrorResponseException $e ) {
|
||||
$this->save_conversion_error( $e->getMessage(), $plugin_settings, true );
|
||||
}
|
||||
|
||||
$this->token_repository->update_token( $this->token );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $converted_files .
|
||||
* @param mixed[] $source_paths .
|
||||
* @param mixed[] $output_paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function save_converted_files( array $converted_files, array $source_paths, array $output_paths, array $plugin_settings ) {
|
||||
foreach ( $converted_files as $output_format => $format_converted_files ) {
|
||||
foreach ( $format_converted_files as $path_index => $converted_file ) {
|
||||
$source_path = $source_paths[ $output_format ][ $path_index ];
|
||||
$output_path = $output_paths[ $output_format ][ $path_index ];
|
||||
|
||||
file_put_contents( $output_path, $converted_file );
|
||||
do_action( 'webpc_after_conversion', $output_path, $source_path );
|
||||
|
||||
try {
|
||||
$this->skip_crashed->delete_crashed_file( $output_path );
|
||||
$this->skip_larger->remove_image_if_is_larger( $output_path, $source_path, $plugin_settings );
|
||||
$this->update_conversion_stats( $source_path, $output_path, $output_format );
|
||||
} catch ( LargerThanOriginalException $e ) {
|
||||
$this->files_converted[ $output_format ]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @throws SourcePathException
|
||||
* @throws OutputPathException
|
||||
*/
|
||||
private function get_source_paths( array $paths, array $plugin_settings, string $output_format ): array {
|
||||
$max_filesize = apply_filters( 'webpc_remote_max_filesize', self::MAX_FILESIZE_BYTES );
|
||||
$source_paths = [];
|
||||
|
||||
foreach ( $paths as $path ) {
|
||||
$source_path = $this->get_image_source_path( $path );
|
||||
if ( filesize( $source_path ) > $max_filesize ) {
|
||||
$this->save_conversion_error(
|
||||
( new FilesizeOversizeException( [ $max_filesize, $source_path ] ) )->getMessage(),
|
||||
$plugin_settings
|
||||
);
|
||||
$this->skip_crashed->create_crashed_file( $this->get_image_output_path( $source_path, $output_format ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
$path_extension = strtolower( pathinfo( $path, PATHINFO_EXTENSION ) );
|
||||
if ( $path_extension === $output_format ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source_paths[] = $this->get_image_source_path( $path );
|
||||
}
|
||||
|
||||
return $source_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source_paths .
|
||||
* @param string $output_format .
|
||||
*
|
||||
* @return string[]
|
||||
*
|
||||
* @throws OutputPathException
|
||||
*/
|
||||
private function get_output_paths( array $source_paths, string $output_format ): array {
|
||||
$output_path = [];
|
||||
foreach ( $source_paths as $path ) {
|
||||
$output_path[] = $this->get_image_output_path( $path, $output_format );
|
||||
}
|
||||
|
||||
return $output_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $source_paths .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param mixed[] $output_paths .
|
||||
*
|
||||
* @return mixed[]
|
||||
*
|
||||
* @throws RemoteErrorResponseException
|
||||
*/
|
||||
private function init_connections( array $source_paths, array $plugin_settings, array $output_paths ): array {
|
||||
$mh_items = [];
|
||||
$values = [];
|
||||
|
||||
$mh = curl_multi_init();
|
||||
if ( ! $mh ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
foreach ( $source_paths as $output_format => $format_source_paths ) {
|
||||
foreach ( $format_source_paths as $resource_id => $source_path ) {
|
||||
$connect = $this->get_curl_connection( $source_path, $output_format, $plugin_settings );
|
||||
if ( ! $connect ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
curl_multi_add_handle( $mh, $connect );
|
||||
$mh_items[ $output_format ] = $mh_items[ $output_format ] ?? [];
|
||||
$mh_items[ $output_format ][ $resource_id ] = $connect;
|
||||
}
|
||||
}
|
||||
|
||||
$running = null;
|
||||
do {
|
||||
curl_multi_exec( $mh, $running );
|
||||
curl_multi_select( $mh );
|
||||
} while ( $running > 0 );
|
||||
|
||||
foreach ( $mh_items as $output_format => $format_mh_items ) {
|
||||
foreach ( $format_mh_items as $resource_id => $mh_item ) {
|
||||
$http_code = curl_getinfo( $mh_item, CURLINFO_HTTP_CODE );
|
||||
$response = curl_multi_getcontent( $mh_item );
|
||||
|
||||
if ( ( $http_code === 200 ) && ( strlen( $response ) > 10 ) ) {
|
||||
$values[ $output_format ] = $values[ $output_format ] ?? [];
|
||||
$values[ $output_format ][ $resource_id ] = $response;
|
||||
|
||||
$this->files_converted[ $output_format ]++;
|
||||
} else {
|
||||
$this->handle_request_error(
|
||||
$source_paths[ $output_format ][ $resource_id ],
|
||||
$output_paths[ $output_format ][ $resource_id ],
|
||||
$output_format,
|
||||
(int) $resource_id,
|
||||
$plugin_settings,
|
||||
$http_code,
|
||||
$response
|
||||
);
|
||||
}
|
||||
curl_multi_remove_handle( $mh, $mh_item );
|
||||
}
|
||||
}
|
||||
|
||||
curl_multi_close( $mh );
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_format .
|
||||
* @param mixed[] $plugin_settings .
|
||||
*
|
||||
* @return resource|null
|
||||
*/
|
||||
private function get_curl_connection( string $source_path, string $output_format, array $plugin_settings ) {
|
||||
$connect = curl_init( WebpConverterConstants::API_CONVERSION_URL );
|
||||
if ( ! $connect ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
curl_setopt( $connect, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $connect, CURLOPT_TIMEOUT, apply_filters( 'webpc_remote_timeout', 30 ) );
|
||||
curl_setopt( $connect, CURLOPT_POST, true );
|
||||
curl_setopt(
|
||||
$connect,
|
||||
CURLOPT_POSTFIELDS,
|
||||
[
|
||||
'access_token' => $plugin_settings[ AccessTokenOption::OPTION_NAME ],
|
||||
'domain_host' => parse_url( get_site_url(), PHP_URL_HOST ),
|
||||
'source_file' => curl_file_create( $source_path ),
|
||||
'output_format' => $output_format,
|
||||
'quality_level' => $plugin_settings[ ImagesQualityOption::OPTION_NAME ],
|
||||
'strip_metadata' => ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_KEEP_METADATA, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) ),
|
||||
'max_width' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' )
|
||||
? $plugin_settings[ ImageResizeOption::OPTION_NAME ][1]
|
||||
: 0,
|
||||
'max_height' => ( $plugin_settings[ ImageResizeOption::OPTION_NAME ][0] === 'yes' )
|
||||
? $plugin_settings[ ImageResizeOption::OPTION_NAME ][2]
|
||||
: 0,
|
||||
]
|
||||
);
|
||||
curl_setopt(
|
||||
$connect,
|
||||
CURLOPT_HTTPHEADER,
|
||||
[
|
||||
'Content-Type: multipart/form-data',
|
||||
'Expect:',
|
||||
]
|
||||
);
|
||||
curl_setopt( $connect, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $connect, CURLOPT_HEADERFUNCTION, [ $this, 'handle_request_header' ] );
|
||||
|
||||
return $connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $curl .
|
||||
* @param string $header .
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle_request_header( $curl, string $header ): int {
|
||||
$header_length = strlen( $header );
|
||||
$header_data = explode( ':', $header );
|
||||
if ( count( $header_data ) > 2 ) {
|
||||
return $header_length;
|
||||
}
|
||||
|
||||
$header_key = strtolower( trim( $header_data[0] ) );
|
||||
if ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_USAGE ) {
|
||||
$this->token->set_images_usage( intval( $header_data[1] ) );
|
||||
} elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_LIMIT_MAX ) {
|
||||
$this->token->set_images_limit( intval( $header_data[1] ) );
|
||||
} elseif ( $header_key === WebpConverterConstants::API_RESPONSE_VALUE_SUBSCRIPTION_ACTIVE ) {
|
||||
$this->token->set_valid_status( ( trim( $header_data[1] ) === '1' ) );
|
||||
}
|
||||
|
||||
return $header_length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_path .
|
||||
* @param string $output_format .
|
||||
* @param int $resource_id .
|
||||
* @param mixed[] $plugin_settings .
|
||||
* @param int $http_code .
|
||||
* @param string|null $response .
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws RemoteErrorResponseException
|
||||
*/
|
||||
private function handle_request_error(
|
||||
string $source_path,
|
||||
string $output_path,
|
||||
string $output_format,
|
||||
int $resource_id,
|
||||
array $plugin_settings,
|
||||
int $http_code,
|
||||
string $response = null
|
||||
) {
|
||||
$response_value = ( $response ) ? json_decode( $response, true ) : [];
|
||||
$error_message = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_MESSAGE ] ?? '';
|
||||
$error_fatal_status = $response_value[ WebpConverterConstants::API_RESPONSE_VALUE_ERROR_FATAL_STATUS ] ?? false;
|
||||
|
||||
if ( $error_message && $error_fatal_status ) {
|
||||
throw new RemoteErrorResponseException( $error_message );
|
||||
} elseif ( $error_message ) {
|
||||
$this->save_conversion_error( $error_message, $plugin_settings );
|
||||
} elseif ( $http_code === 200 ) {
|
||||
$this->skip_crashed->create_crashed_file( $output_path );
|
||||
|
||||
if ( ! isset( $this->failed_converted_source_files[ $output_format ] ) ) {
|
||||
$this->failed_converted_source_files[ $output_format ] = [];
|
||||
}
|
||||
$this->failed_converted_source_files[ $output_format ][ $resource_id ] = $source_path;
|
||||
} else {
|
||||
$this->save_conversion_error(
|
||||
( new RemoteRequestException( [ $http_code, $source_path ] ) )->getMessage(),
|
||||
$plugin_settings
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
|
||||
/**
|
||||
* Generates output paths from source paths.
|
||||
*/
|
||||
class OutputPathGenerator {
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $path_wp_content_dir = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $path_output_dir = null;
|
||||
|
||||
public function __construct( FormatFactory $format_factory ) {
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output path from path of source image.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param bool $create_dir Create output directory structure?
|
||||
* @param string $file_extension Output format extension.
|
||||
*
|
||||
* @return string|null Server path for output image.
|
||||
*/
|
||||
public function get_path( string $path, bool $create_dir = false, string $file_extension = '' ) {
|
||||
$paths = $this->get_paths( $path, $create_dir, [ $file_extension ] );
|
||||
return $paths[0] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output paths from paths of source image for all output formats.
|
||||
* Creates directory structure of output path, if it does not exist.
|
||||
*
|
||||
* @param string $path Server path of source image.
|
||||
* @param bool $create_dir Create output directory structure?
|
||||
* @param string[] $file_extensions Output format extensions.
|
||||
*
|
||||
* @return string[] Server paths for output images.
|
||||
*/
|
||||
public function get_paths( string $path, bool $create_dir = false, array $file_extensions = null ): array {
|
||||
$new_path = $this->get_directory_path( $path );
|
||||
if ( ! $new_path || ( $create_dir && ! $this->make_directories( $this->check_directories( $new_path ) ) ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$extensions = $this->format_factory->get_format_extensions();
|
||||
$path_extension = strtolower( pathinfo( $new_path, PATHINFO_EXTENSION ) );
|
||||
$paths = [];
|
||||
foreach ( $extensions as $extension ) {
|
||||
if ( $extension === $path_extension ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( $file_extensions === null ) || in_array( $extension, $file_extensions, true ) ) {
|
||||
$paths[] = sprintf( '%1$s.%2$s', $new_path, $extension );
|
||||
}
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates output path from path of source directory.
|
||||
*
|
||||
* @param string $path Server path of source directory.
|
||||
*
|
||||
* @return string|null Server paths for output directory.
|
||||
*/
|
||||
public function get_directory_path( string $path ) {
|
||||
$webp_root = $this->get_output_dir();
|
||||
$wp_content = $this->get_wp_content_dir();
|
||||
$output_path = str_replace(
|
||||
preg_replace( '/(\/|\\\\)/', DIRECTORY_SEPARATOR, $wp_content ) ?: '',
|
||||
'',
|
||||
preg_replace( '/(\/|\\\\)/', DIRECTORY_SEPARATOR, $path ) ?: ''
|
||||
);
|
||||
$output_path = trim( $output_path, '\/' );
|
||||
|
||||
if ( ! $output_path ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sprintf( '%1$s/%2$s', $webp_root, $output_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if directories for output path exist.
|
||||
*
|
||||
* @param string $path Server path of output.
|
||||
*
|
||||
* @return string[] Directory paths to be created.
|
||||
*/
|
||||
private function check_directories( string $path ): array {
|
||||
$current = dirname( $path );
|
||||
$paths = [];
|
||||
while ( ! file_exists( $current ) ) {
|
||||
$paths[] = $current;
|
||||
$current = dirname( $current );
|
||||
}
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new directories.
|
||||
*
|
||||
* @param string[] $paths Output directory paths to be created.
|
||||
*
|
||||
* @return bool Paths created successfully?
|
||||
*/
|
||||
private function make_directories( array $paths ): bool {
|
||||
$paths = array_reverse( $paths );
|
||||
foreach ( $paths as $path ) {
|
||||
if ( ! is_writable( dirname( $path ) ) ) {
|
||||
return false;
|
||||
}
|
||||
mkdir( $path );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function get_wp_content_dir(): string {
|
||||
if ( $this->path_wp_content_dir === null ) {
|
||||
$this->path_wp_content_dir = dirname( apply_filters( 'webpc_dir_path', '', 'uploads' ) );
|
||||
}
|
||||
return $this->path_wp_content_dir;
|
||||
}
|
||||
|
||||
private function get_output_dir(): string {
|
||||
if ( $this->path_output_dir === null ) {
|
||||
$this->path_output_dir = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
}
|
||||
return $this->path_output_dir;
|
||||
}
|
||||
}
|
@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Conversion;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\StatsManager;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Finds paths of images to be converted.
|
||||
*/
|
||||
class PathsFinder {
|
||||
|
||||
const PATHS_PER_REQUEST_LOCAL = 10;
|
||||
const PATHS_PER_REQUEST_REMOTE_SMALL = 3;
|
||||
const PATHS_PER_REQUEST_REMOTE_MEDIUM = 5;
|
||||
const PATHS_PER_REQUEST_REMOTE_LARGE = 10;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var StatsManager
|
||||
*/
|
||||
private $stats_manager;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var DirectoryFilesFinder
|
||||
*/
|
||||
private $files_finder;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository,
|
||||
FormatFactory $format_factory,
|
||||
StatsManager $stats_manager = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository;
|
||||
$this->stats_manager = $stats_manager ?: new StatsManager();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
$this->files_finder = new DirectoryFilesFinder( $plugin_data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of chunked server paths of source images to be converted.
|
||||
*
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type string $path Directory path.
|
||||
* @type string[] $files Files paths.
|
||||
* }
|
||||
*/
|
||||
public function get_paths_by_chunks( bool $skip_converted = false, array $allowed_output_formats = null ): array {
|
||||
$allowed_output_formats = $allowed_output_formats
|
||||
?: $this->plugin_data->get_plugin_settings()[ OutputFormatsOption::OPTION_NAME ];
|
||||
|
||||
$paths_chunks = $this->find_source_paths();
|
||||
$paths_chunks = $this->skip_converted_paths_chunks( $paths_chunks, $skip_converted, $allowed_output_formats );
|
||||
|
||||
$count = 0;
|
||||
foreach ( $paths_chunks as $dir_data ) {
|
||||
$count += count( $dir_data['files'] );
|
||||
}
|
||||
|
||||
$chunk_size = $this->get_paths_chunk_size( $count );
|
||||
foreach ( $paths_chunks as $dir_name => $dir_data ) {
|
||||
$paths_chunks[ $dir_name ]['files'] = array_chunk( $dir_data['files'], $chunk_size );
|
||||
}
|
||||
|
||||
$this->stats_manager->set_regeneration_images( $count );
|
||||
|
||||
return $paths_chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return string[] Server paths of source images to be converted.
|
||||
*/
|
||||
public function get_paths( bool $skip_converted = false, array $allowed_output_formats = null ): array {
|
||||
$allowed_output_formats = $allowed_output_formats
|
||||
?: $this->plugin_data->get_plugin_settings()[ OutputFormatsOption::OPTION_NAME ];
|
||||
|
||||
$paths_chunks = $this->find_source_paths();
|
||||
$paths_chunks = $this->skip_converted_paths_chunks( $paths_chunks, $skip_converted, $allowed_output_formats );
|
||||
|
||||
$paths = [];
|
||||
foreach ( $paths_chunks as $dir_data ) {
|
||||
foreach ( $dir_data['files'] as $source_path ) {
|
||||
$paths[] = $dir_data['path'] . '/' . $source_path;
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $source_paths Server paths of source images.
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
* @param bool $force_convert_modified Force re-conversion of images modified after previous conversion.
|
||||
*
|
||||
* @return string[] Server paths of source images.
|
||||
*/
|
||||
public function skip_converted_paths(
|
||||
array $source_paths,
|
||||
array $allowed_output_formats = null,
|
||||
bool $force_convert_modified = false
|
||||
): array {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_output_formats = $allowed_output_formats ?: $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
foreach ( $source_paths as $path_index => $source_path ) {
|
||||
$is_converted = true;
|
||||
foreach ( $allowed_output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
|
||||
if ( $output_path && ! $this->is_converted_file( $source_path, $output_path, $force_convert_deleted, false, $force_convert_modified ) ) {
|
||||
$is_converted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $is_converted ) {
|
||||
unset( $source_paths[ $path_index ] );
|
||||
}
|
||||
}
|
||||
|
||||
return $source_paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $source_dirs Server paths of source images.
|
||||
* @param bool $skip_converted Skip converted images?
|
||||
* @param string[]|null $allowed_output_formats List of extensions or use selected in plugin settings.
|
||||
*
|
||||
* @return mixed[] Server paths of source images.
|
||||
*/
|
||||
private function skip_converted_paths_chunks(
|
||||
array $source_dirs,
|
||||
bool $skip_converted,
|
||||
array $allowed_output_formats = null
|
||||
): array {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
$allowed_output_formats = $allowed_output_formats ?: $plugin_settings[ OutputFormatsOption::OPTION_NAME ];
|
||||
$force_convert_deleted = ( ! in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
$force_convert_crashed = ( in_array( ExtraFeaturesOption::OPTION_VALUE_SERVICE_MODE, $plugin_settings[ ExtraFeaturesOption::OPTION_NAME ] ) );
|
||||
|
||||
foreach ( $source_dirs as $dir_name => $dir_data ) {
|
||||
foreach ( $dir_data['files'] as $path_index => $source_file ) {
|
||||
$source_path = $dir_data['path'] . '/' . $source_file;
|
||||
$is_converted = true;
|
||||
foreach ( $allowed_output_formats as $output_format ) {
|
||||
$output_path = $this->output_path->get_path( $source_path, false, $output_format );
|
||||
|
||||
if ( $output_path && ( ! $skip_converted || ! $this->is_converted_file( $source_path, $output_path, $force_convert_deleted, $force_convert_crashed ) ) ) {
|
||||
$is_converted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $is_converted ) {
|
||||
unset( $source_dirs[ $dir_name ]['files'][ $path_index ] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $source_dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of server paths of source images to be converted.
|
||||
*
|
||||
* @return mixed[] {
|
||||
* @type string $path Directory path.
|
||||
* @type string[] $files Files paths.
|
||||
* }
|
||||
*/
|
||||
private function find_source_paths(): array {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
|
||||
$source_dirs = [];
|
||||
foreach ( $settings[ SupportedDirectoriesOption::OPTION_NAME ] as $dir_name ) {
|
||||
$source_dirs[ $dir_name ] = apply_filters( 'webpc_dir_path', '', $dir_name );
|
||||
}
|
||||
|
||||
$list = [];
|
||||
foreach ( $source_dirs as $dir_name => $dir_path ) {
|
||||
$list[ $dir_name ] = [
|
||||
'path' => $dir_path,
|
||||
'files' => $this->files_finder->get_files_by_directory( $dir_path ),
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $source_path .
|
||||
* @param string $output_path .
|
||||
* @param bool $force_convert_deleted Skip .deleted files.
|
||||
* @param bool $force_convert_crashed Skip .crashed files.
|
||||
* @param bool $force_convert_modified .
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_converted_file(
|
||||
string $source_path,
|
||||
string $output_path,
|
||||
bool $force_convert_deleted,
|
||||
bool $force_convert_crashed,
|
||||
bool $force_convert_modified = false
|
||||
): bool {
|
||||
if ( file_exists( $output_path ) ) {
|
||||
return ( $force_convert_modified ) ? ( filemtime( $source_path ) <= filemtime( $output_path ) ) : true;
|
||||
} elseif ( ! $force_convert_deleted && file_exists( $output_path . '.' . LargerFilesOperator::DELETED_FILE_EXTENSION ) ) {
|
||||
return true;
|
||||
} elseif ( ! $force_convert_crashed && file_exists( $output_path . '.' . CrashedFilesOperator::CRASHED_FILE_EXTENSION ) ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $paths_count .
|
||||
*
|
||||
* @return int<1, max>
|
||||
*/
|
||||
private function get_paths_chunk_size( int $paths_count ): int {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $settings[ ConversionMethodOption::OPTION_NAME ] !== RemoteMethod::METHOD_NAME ) {
|
||||
return self::PATHS_PER_REQUEST_LOCAL;
|
||||
}
|
||||
|
||||
$output_formats = count( $settings[ OutputFormatsOption::OPTION_NAME ] ) ?: 1;
|
||||
$images_count = $paths_count * $output_formats;
|
||||
$images_limit = $this->token_repository->get_token()->get_images_limit();
|
||||
$images_to_conversion = min( $images_count, $images_limit );
|
||||
|
||||
if ( $images_to_conversion <= 10000 ) {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_SMALL;
|
||||
} elseif ( $images_to_conversion <= 120000 ) {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_MEDIUM;
|
||||
} else {
|
||||
return self::PATHS_PER_REQUEST_REMOTE_LARGE;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\CloudflareSettingsIncorrectNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Service\CloudflareConfigurator;
|
||||
use WebpConverter\Service\OptionsAccessManager;
|
||||
use WebpConverter\Settings\Option\CloudflareZoneIdOption;
|
||||
|
||||
/**
|
||||
* Validates Cloudflare configuration.
|
||||
*/
|
||||
class CloudflareStatusDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ CloudflareZoneIdOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( OptionsAccessManager::get_option( CloudflareConfigurator::REQUEST_CACHE_PURGE_OPTION, 'yes' ) !== 'yes' ) {
|
||||
return new CloudflareSettingsIncorrectNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
|
||||
/**
|
||||
* Interface for class that checks for configuration errors.
|
||||
*/
|
||||
interface DetectorInterface {
|
||||
|
||||
/**
|
||||
* @return NoticeInterface|null
|
||||
*/
|
||||
public function get_error();
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Error\Notice\LibsNotInstalledNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about non-installed methods for converting images.
|
||||
*/
|
||||
class LibsNotInstalledDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] === RemoteMethod::METHOD_NAME ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( GdMethod::is_method_installed() || ImagickMethod::is_method_installed() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LibsNotInstalledNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\Method\GdMethod;
|
||||
use WebpConverter\Conversion\Method\ImagickMethod;
|
||||
use WebpConverter\Conversion\Method\RemoteMethod;
|
||||
use WebpConverter\Error\Notice\LibsWithoutWebpSupportNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about image conversion methods that do not support WebP output format.
|
||||
*/
|
||||
class LibsWithoutWebpSupportDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] === RemoteMethod::METHOD_NAME ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( GdMethod::is_method_active( WebpFormat::FORMAT_EXTENSION )
|
||||
|| ImagickMethod::is_method_active( WebpFormat::FORMAT_EXTENSION ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LibsWithoutWebpSupportNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Error\Notice\PassthruExecutionNotice;
|
||||
use WebpConverter\Loader\LoaderAbstract;
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about disabled file supports Pass Thru loader.
|
||||
*/
|
||||
class PassthruExecutionDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
public function __construct( PluginInfo $plugin_info, PluginData $plugin_data, FormatFactory $format_factory ) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ LoaderTypeOption::OPTION_NAME ] !== PassthruLoader::LOADER_TYPE ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true, true );
|
||||
|
||||
$has_error = false;
|
||||
if ( $this->if_passthru_execution_allowed() !== true ) {
|
||||
$has_error = true;
|
||||
}
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true );
|
||||
|
||||
return ( $has_error ) ? new PassthruExecutionNotice() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if PHP file required for Passthru loader is available.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_passthru_execution_allowed(): bool {
|
||||
$loader = new PassthruLoader( $this->plugin_info, $this->plugin_data, $this->format_factory );
|
||||
if ( $loader->is_active_loader() !== true ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$url = $loader::get_loader_url() . '?nocache=1';
|
||||
$ch = curl_init( $url );
|
||||
if ( $ch === false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
||||
curl_setopt( $ch, CURLOPT_NOBODY, true );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false );
|
||||
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 0 );
|
||||
curl_setopt( $ch, CURLOPT_TIMEOUT, 3 );
|
||||
curl_exec( $ch );
|
||||
$code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
||||
curl_close( $ch );
|
||||
|
||||
return ( $code === 200 );
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\PathHtaccessNotWritableNotice;
|
||||
use WebpConverter\Error\Notice\PathUploadsUnavailableNotice;
|
||||
use WebpConverter\Error\Notice\PathWebpDuplicatedNotice;
|
||||
use WebpConverter\Error\Notice\PathWebpNotWritableNotice;
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about incorrect paths of directories.
|
||||
*/
|
||||
class PathsErrorsDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
if ( $this->if_uploads_path_exists() !== true ) {
|
||||
return new PathUploadsUnavailableNotice();
|
||||
} elseif ( $this->if_htaccess_is_writeable() !== true ) {
|
||||
return new PathHtaccessNotWritableNotice();
|
||||
} elseif ( $this->if_paths_are_different() !== true ) {
|
||||
return new PathWebpDuplicatedNotice();
|
||||
} elseif ( $this->if_webp_path_is_writeable() !== true ) {
|
||||
return new PathWebpNotWritableNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path of uploads directory is exists.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_uploads_path_exists(): bool {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
return ( is_dir( $path ) && ( $path !== PathsGenerator::get_wordpress_root_path() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if paths of wp-content and uploads directories are writable.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_htaccess_is_writeable(): bool {
|
||||
$path_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$path_file = $path_dir . '/.htaccess';
|
||||
if ( file_exists( $path_file ) ) {
|
||||
return ( is_readable( $path_file ) && is_writable( $path_file ) );
|
||||
} else {
|
||||
return is_writable( $path_dir );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if uploads directory path and output directory are different.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_paths_are_different(): bool {
|
||||
$path_uploads = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$path_webp = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
return ( $path_uploads !== $path_webp );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path of output directory is writable.
|
||||
*
|
||||
* @return bool Verification status.
|
||||
*/
|
||||
private function if_webp_path_is_writeable(): bool {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
return ( is_dir( $path ) || is_writable( dirname( $path ) ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,325 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Conversion\Format\AvifFormat;
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Conversion\Format\WebpFormat;
|
||||
use WebpConverter\Conversion\OutputPathGenerator;
|
||||
use WebpConverter\Error\Notice\BypassingApacheNotice;
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
use WebpConverter\Error\Notice\PassthruNotWorkingNotice;
|
||||
use WebpConverter\Error\Notice\RewritesCachedNotice;
|
||||
use WebpConverter\Error\Notice\RewritesNotExecutedNotice;
|
||||
use WebpConverter\Error\Notice\RewritesNotWorkingNotice;
|
||||
use WebpConverter\Error\Notice\RewritesUploadsBlockedNotice;
|
||||
use WebpConverter\Loader\HtaccessBypassingLoader;
|
||||
use WebpConverter\Loader\HtaccessLoader;
|
||||
use WebpConverter\Loader\LoaderAbstract;
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Service\FileLoader;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about non-working HTTP rewrites.
|
||||
*/
|
||||
class RewritesErrorsDetector implements DetectorInterface {
|
||||
|
||||
const PATH_SOURCE_FILE_PNG = '/assets/img/icon-test.png';
|
||||
const PATH_SOURCE_FILE_WEBP = '/assets/img/icon-test.webp';
|
||||
const PATH_SOURCE_FILE_AVIF = '/assets/img/icon-test.avif';
|
||||
const PATH_OUTPUT_FILE_PNG = '/webp-converter-for-media-test.png';
|
||||
const PATH_OUTPUT_FILE_PNG2 = '/webp-converter-for-media-test.png2';
|
||||
const PATH_OUTPUT_FILE_PLUGINS = '/webp-converter-for-media/assets/img/icon-test.png';
|
||||
const URL_DEBUG_HTACCESS_FILE = 'assets/img/debug-htaccess/icon-test.png2';
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FileLoader
|
||||
*/
|
||||
private $file_loader;
|
||||
|
||||
/**
|
||||
* @var OutputPathGenerator
|
||||
*/
|
||||
private $output_path;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $test_version;
|
||||
|
||||
public function __construct(
|
||||
PluginInfo $plugin_info,
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory,
|
||||
FileLoader $file_loader = null,
|
||||
OutputPathGenerator $output_path = null
|
||||
) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->file_loader = $file_loader ?: new FileLoader();
|
||||
$this->output_path = $output_path ?: new OutputPathGenerator( $format_factory );
|
||||
$this->test_version = uniqid();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ]
|
||||
|| ! $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->convert_images_for_debug();
|
||||
|
||||
do_action( LoaderAbstract::ACTION_NAME, true, true );
|
||||
$error = $this->detect_rewrites_error();
|
||||
do_action( LoaderAbstract::ACTION_NAME, true );
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NoticeInterface|null
|
||||
*/
|
||||
private function detect_rewrites_error() {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
$loader_type = $settings[ LoaderTypeOption::OPTION_NAME ] ?? '';
|
||||
|
||||
switch ( $loader_type ) {
|
||||
case HtaccessLoader::LOADER_TYPE:
|
||||
case HtaccessBypassingLoader::LOADER_TYPE:
|
||||
if ( $this->if_redirects_are_works() === true ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $this->if_htaccess_can_be_overwritten() !== true ) {
|
||||
return new RewritesNotExecutedNotice();
|
||||
} elseif ( $this->if_bypassing_apache_is_active() === true ) {
|
||||
return new BypassingApacheNotice();
|
||||
} elseif ( $this->if_redirects_for_plugins_are_works() === true ) {
|
||||
return new RewritesUploadsBlockedNotice();
|
||||
}
|
||||
|
||||
return new RewritesNotWorkingNotice();
|
||||
case PassthruLoader::LOADER_TYPE:
|
||||
if ( $this->if_redirects_are_works() === true ) {
|
||||
break;
|
||||
}
|
||||
|
||||
return new PassthruNotWorkingNotice();
|
||||
}
|
||||
|
||||
$this->test_version = uniqid();
|
||||
if ( $this->if_redirects_are_cached() === true ) {
|
||||
return new RewritesCachedNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts and saves files needed for testing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function convert_images_for_debug() {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
if ( ! is_writable( $uploads_dir ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$path_file_png = $uploads_dir . self::PATH_OUTPUT_FILE_PNG;
|
||||
$path_file_png2 = $uploads_dir . self::PATH_OUTPUT_FILE_PNG2;
|
||||
$path_file_plugins = apply_filters( 'webpc_dir_path', '', 'plugins' ) . self::PATH_OUTPUT_FILE_PLUGINS;
|
||||
$file_statuses = [];
|
||||
|
||||
if ( ! file_exists( $path_file_png ) || ! file_exists( $path_file_png2 ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_PNG, $path_file_png );
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_PNG, $path_file_png2 );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png, true, AvifFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_AVIF, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_png2, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( ( $output_path = $this->output_path->get_path( $path_file_plugins, true, WebpFormat::FORMAT_EXTENSION ) )
|
||||
&& ! file_exists( $output_path ) ) {
|
||||
$file_statuses[] = copy( $this->plugin_info->get_plugin_directory_path() . self::PATH_SOURCE_FILE_WEBP, $output_path );
|
||||
} else {
|
||||
$file_statuses[] = true;
|
||||
}
|
||||
|
||||
if ( in_array( false, $file_statuses, true ) ) {
|
||||
$GLOBALS[ FileLoader::GLOBAL_LOGS_VARIABLE ][] = [
|
||||
'context' => __FUNCTION__,
|
||||
'status' => $file_statuses,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images are works.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_are_works(): bool {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_size = $this->file_loader->get_file_size_by_path(
|
||||
$uploads_dir . self::PATH_OUTPUT_FILE_PNG
|
||||
);
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( $file_webp > 0 ) {
|
||||
return ( $file_webp < $file_size );
|
||||
}
|
||||
|
||||
$file_png_status = $this->file_loader->get_file_status_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
false,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( $file_png_status === 500 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_webp_status = $this->file_loader->get_file_status_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
if ( ( $file_png_status === 200 ) && ( $file_webp_status === 404 ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if server supports using .htaccess files from custom locations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_htaccess_can_be_overwritten(): bool {
|
||||
$file_size = $this->file_loader->get_file_size_by_url(
|
||||
$this->plugin_info->get_plugin_directory_url() . self::URL_DEBUG_HTACCESS_FILE,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( $file_size === 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bypassing of redirects to output images is exists.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_bypassing_apache_is_active(): bool {
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_png = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
$file_png2 = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG2,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( $file_png > $file_png2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images from /plugins directory are works.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_for_plugins_are_works(): bool {
|
||||
$uploads_dir = apply_filters( 'webpc_dir_path', '', 'plugins' );
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'plugins' );
|
||||
|
||||
$file_size = $this->file_loader->get_file_size_by_path(
|
||||
$uploads_dir . self::PATH_OUTPUT_FILE_PLUGINS
|
||||
);
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PLUGINS,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( ( $file_webp < $file_size ) && ( $file_webp !== 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if redirects to output images are cached.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function if_redirects_are_cached(): bool {
|
||||
$uploads_url = apply_filters( 'webpc_dir_url', '', 'uploads' );
|
||||
|
||||
$file_webp = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
true,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
$file_original = $this->file_loader->get_file_size_by_url(
|
||||
$uploads_url . self::PATH_OUTPUT_FILE_PNG,
|
||||
false,
|
||||
$this->test_version,
|
||||
__FUNCTION__
|
||||
);
|
||||
|
||||
return ( ( $file_webp > 0 ) && ( $file_webp === $file_original ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\SettingsIncorrectNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\ConversionMethodOption;
|
||||
use WebpConverter\Settings\Option\ImagesQualityOption;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
use WebpConverter\Settings\Option\SupportedDirectoriesOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about incorrectly saved plugin settings.
|
||||
*/
|
||||
class SettingsIncorrectDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
|
||||
if ( ( ! isset( $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ SupportedExtensionsOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ SupportedDirectoriesOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ OutputFormatsOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ ConversionMethodOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ ConversionMethodOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ ImagesQualityOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ ImagesQualityOption::OPTION_NAME ] )
|
||||
|| ( ! isset( $plugin_settings[ LoaderTypeOption::OPTION_NAME ] )
|
||||
|| ! $plugin_settings[ LoaderTypeOption::OPTION_NAME ] ) ) {
|
||||
return new SettingsIncorrectNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\AccessTokenInvalidNotice;
|
||||
use WebpConverter\Error\Notice\ApiLimitExceededNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Repository\TokenRepository;
|
||||
use WebpConverter\Service\TokenValidator;
|
||||
use WebpConverter\Settings\Option\AccessTokenOption;
|
||||
|
||||
/**
|
||||
* Checks for the token status for the PRO version.
|
||||
*/
|
||||
class TokenStatusDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var TokenRepository
|
||||
*/
|
||||
private $token_repository;
|
||||
|
||||
/**
|
||||
* @var TokenValidator
|
||||
*/
|
||||
private $token_validator;
|
||||
|
||||
public function __construct(
|
||||
PluginData $plugin_data,
|
||||
TokenRepository $token_repository = null,
|
||||
TokenValidator $token_validator = null
|
||||
) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->token_repository = $token_repository ?: new TokenRepository();
|
||||
$this->token_validator = $token_validator ?: new TokenValidator( $token_repository );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( ! isset( $settings[ AccessTokenOption::OPTION_NAME ] ) || ! $settings[ AccessTokenOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$token = $this->token_repository->get_token();
|
||||
if ( ! $token->get_valid_status() ) {
|
||||
return new AccessTokenInvalidNotice();
|
||||
}
|
||||
|
||||
if ( ! $token->is_active() ) {
|
||||
$token = $this->token_validator->validate_token( $token->get_token_value() );
|
||||
}
|
||||
|
||||
if ( ! $token->is_active() ) {
|
||||
return new ApiLimitExceededNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\UnsupportedPlaygroundServerNotice;
|
||||
|
||||
/**
|
||||
* Checks for configuration errors about unsupported servers.
|
||||
*/
|
||||
class UnsupportedServerDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
if ( strpos( $_SERVER['SERVER_NAME'] ?? '', 'playground.wordpress.net' ) !== false ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
return new UnsupportedPlaygroundServerNotice();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Detector;
|
||||
|
||||
use WebpConverter\Error\Notice\WebpRequiredNotice;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\Settings\Option\OutputFormatsOption;
|
||||
|
||||
/**
|
||||
* Checks if the WebP as output format is active.
|
||||
*/
|
||||
class WebpFormatActivatedDetector implements DetectorInterface {
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
public function __construct( PluginData $plugin_data ) {
|
||||
$this->plugin_data = $plugin_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error() {
|
||||
$plugin_settings = $this->plugin_data->get_plugin_settings();
|
||||
if ( $plugin_settings[ OutputFormatsOption::OPTION_NAME ] ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new WebpRequiredNotice();
|
||||
}
|
||||
}
|
@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\Error\Detector\CloudflareStatusDetector;
|
||||
use WebpConverter\Error\Detector\LibsNotInstalledDetector;
|
||||
use WebpConverter\Error\Detector\LibsWithoutWebpSupportDetector;
|
||||
use WebpConverter\Error\Detector\PassthruExecutionDetector;
|
||||
use WebpConverter\Error\Detector\PathsErrorsDetector;
|
||||
use WebpConverter\Error\Detector\RewritesErrorsDetector;
|
||||
use WebpConverter\Error\Detector\SettingsIncorrectDetector;
|
||||
use WebpConverter\Error\Detector\TokenStatusDetector;
|
||||
use WebpConverter\Error\Detector\UnsupportedServerDetector;
|
||||
use WebpConverter\Error\Detector\WebpFormatActivatedDetector;
|
||||
use WebpConverter\Error\Notice\NoticeInterface;
|
||||
use WebpConverter\Error\Notice\RewritesCachedNotice;
|
||||
use WebpConverter\HookableInterface;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Service\OptionsAccessManager;
|
||||
|
||||
/**
|
||||
* Supports generating list of server configuration errors.
|
||||
*/
|
||||
class ErrorDetectorAggregator implements HookableInterface {
|
||||
|
||||
const ERRORS_CACHE_OPTION = 'webpc_errors_cache';
|
||||
const ERROR_DETECTOR_DATE_TRANSIENT = 'webpc_error_detector';
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
private $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
private $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
private $format_factory;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $not_fatal_errors = [
|
||||
RewritesCachedNotice::ERROR_KEY,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var NoticeInterface[]|null
|
||||
*/
|
||||
private $cached_errors = null;
|
||||
|
||||
public function __construct(
|
||||
PluginInfo $plugin_info,
|
||||
PluginData $plugin_data,
|
||||
FormatFactory $format_factory
|
||||
) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_hooks() {
|
||||
add_filter( 'webpc_server_errors', [ $this, 'get_server_errors' ], 10, 2 );
|
||||
add_filter( 'webpc_server_errors_messages', [ $this, 'get_server_errors_messages' ], 10, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of errors codes for server configuration.
|
||||
*
|
||||
* @param string[] $values Default value of filter.
|
||||
* @param bool $only_errors Only errors, no warnings?
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_server_errors( array $values, bool $only_errors = false ): array {
|
||||
$error_codes = $this->get_cached_error_codes();
|
||||
|
||||
return array_filter(
|
||||
$error_codes,
|
||||
function ( $error ) use ( $only_errors ) {
|
||||
return ( ! $only_errors || ! in_array( $error, $this->not_fatal_errors ) );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of errors messages for server configuration.
|
||||
*
|
||||
* @param string[] $values Default value of filter.
|
||||
*
|
||||
* @return string[][]
|
||||
*/
|
||||
public function get_server_errors_messages( array $values ): array {
|
||||
$detected_errors = $this->get_errors_list();
|
||||
$this->cache_errors( $detected_errors );
|
||||
|
||||
$error_messages = [];
|
||||
foreach ( $detected_errors as $error ) {
|
||||
$error_messages[] = $error->get_message();
|
||||
}
|
||||
return $error_messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function get_cached_error_codes(): array {
|
||||
$error_codes = [];
|
||||
|
||||
if ( $this->cached_errors !== null ) {
|
||||
foreach ( $this->cached_errors as $error ) {
|
||||
$error_codes[] = $error->get_key();
|
||||
}
|
||||
} else {
|
||||
$error_codes = OptionsAccessManager::get_option( self::ERRORS_CACHE_OPTION, [] );
|
||||
}
|
||||
|
||||
return $error_codes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param NoticeInterface[] $detected_errors .
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cache_errors( array $detected_errors ) {
|
||||
$error_codes = [];
|
||||
foreach ( $detected_errors as $error ) {
|
||||
$error_codes[] = $error->get_key();
|
||||
}
|
||||
|
||||
OptionsAccessManager::update_option( self::ERRORS_CACHE_OPTION, $error_codes );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for configuration errors according to specified logic.
|
||||
* Saves errors to cache.
|
||||
*
|
||||
* @return NoticeInterface[]
|
||||
*/
|
||||
private function get_errors_list(): array {
|
||||
if ( $this->cached_errors !== null ) {
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
$this->pause_duplicated_detection();
|
||||
$this->cached_errors = [];
|
||||
|
||||
if ( $new_error = ( new UnsupportedServerDetector() )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new TokenStatusDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new LibsNotInstalledDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new LibsWithoutWebpSupportDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new WebpFormatActivatedDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new PathsErrorsDetector() )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new PassthruExecutionDetector( $this->plugin_info, $this->plugin_data, $this->format_factory ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
} elseif ( $new_error = ( new RewritesErrorsDetector( $this->plugin_info, $this->plugin_data, $this->format_factory ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
if ( $this->cached_errors ) {
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
if ( $new_error = ( new SettingsIncorrectDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
if ( $new_error = ( new CloudflareStatusDetector( $this->plugin_data ) )->get_error() ) {
|
||||
$this->cached_errors[] = $new_error;
|
||||
}
|
||||
|
||||
return $this->cached_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
private function pause_duplicated_detection() {
|
||||
$current_date = ( new \DateTime() )->format( 'Uv' );
|
||||
$cached_date = get_site_transient( self::ERROR_DETECTOR_DATE_TRANSIENT );
|
||||
if ( $cached_date && ( $cached_date >= ( $current_date - 1000 ) ) ) {
|
||||
sleep( 1 );
|
||||
$current_date = ( new \DateTime() )->format( 'Uv' );
|
||||
}
|
||||
|
||||
set_site_transient( self::ERROR_DETECTOR_DATE_TRANSIENT, $current_date );
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class AccessTokenInvalidNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'token_invalid';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: field label, %2$s: button label */
|
||||
__( 'It appears that the value of the %1$s field is invalid or your subscription has expired. To use the service, please, check your subscription and click the %2$s button again.', 'webp-converter-for-media' ),
|
||||
__( 'Access Token', 'webp-converter-for-media' ),
|
||||
__( 'Activate Token', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'To manage your subscriptions, please visit %1$sour website%2$s.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-token-invalid-panel" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ApiLimitExceededNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'token_limit';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that you have reached the maximum number of image conversions for your current billing period. To continue using the service, we recommend upgrading your plan.', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'To manage your subscriptions, please visit %1$sour website%2$s.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-token-limit-panel" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Settings\Page\AdvancedSettingsPage;
|
||||
use WebpConverter\Settings\Page\PageIntegrator;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class BypassingApacheNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'bypassing_apache';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that the requests for images on your website are being processed by the Nginx server, bypassing Apache.', 'webp-converter-for-media' ),
|
||||
implode(
|
||||
' ',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please check %1$sour instruction%2$s which should help you solve your problem. This will allow the plugin to function properly.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-bypassing-apache-instruction" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
__( 'If you have trouble solving this problem, please, contact your hosting\'s technical support and provide them with the following message:', 'webp-converter-for-media' ),
|
||||
]
|
||||
),
|
||||
implode(
|
||||
'',
|
||||
[
|
||||
'<em>' . implode(
|
||||
' ',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: setting name, %2$s: setting name, %3$s: home URL */
|
||||
__( 'I would like to disable %1$s (or %2$s) for static content files like .jpg, .jpeg, .png, .gif and .webp on my website - %3$s. These files should have been handled by the Apache server instead of Nginx.', 'webp-converter-for-media' ),
|
||||
'Nginx Caching / Nginx Reverse Proxy',
|
||||
'Nginx Direct Delivery',
|
||||
get_home_url()
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'You can find more information in the instruction: %s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-bypassing-apache-message" target="_blank">https://url.mattplugins.com/converter-error-bypassing-apache-message</a>'
|
||||
),
|
||||
]
|
||||
) . '</em>',
|
||||
]
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag */
|
||||
__( '%1$sPlease, copy the above message and send it to the technical support of your hosting.%2$s They should help you in this matter.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: field value, %4$s: field label, %5$s: open anchor tag, %6$s: close anchor tag */
|
||||
__( '%1$sThe alternative solution to avoid this problem%2$s may be to set the %3$s option for the %4$s field in %5$sthe Advanced Settings tab%6$s.', 'webp-converter-for-media' ),
|
||||
'<span id="bypassing-notice">',
|
||||
'</span>',
|
||||
'"' . __( 'Bypassing Nginx', 'webp-converter-for-media' ) . '"',
|
||||
'"' . __( 'Image loading mode', 'webp-converter-for-media' ) . '"',
|
||||
'<a href="' . esc_attr( PageIntegrator::get_settings_page_url( AdvancedSettingsPage::PAGE_SLUG ) ) . '#bypassing-notice">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class CloudflareSettingsIncorrectNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'settings_cloudflare';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: field labels */
|
||||
__( 'Incorrect values were given in the plugin settings in the fields: %s.', 'webp-converter-for-media' ),
|
||||
implode( ', ', [ 'Cloudflare Zone ID', 'Cloudflare API Token' ] )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please, read %1$sour manual%2$s and follow the steps there.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-cloudflare-settings-docs" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LibsNotInstalledNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'libs_not_installed';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'GD or Imagick library is not installed on your server.', 'webp-converter-for-media' ) . ' ' . __( 'This means that you cannot convert images to the WebP format on your server, because it does not meet the plugin requirements described in %1$sthe plugin FAQ%2$s. This issue is not dependent on the plugin.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-not-installed-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
|
||||
if ( function_exists( 'curl_init' ) ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( '%1$sHowever, we have a solution for you!%2$s You can activate %3$sthe PRO version%4$s of the plugin that allows you to convert images using a remote server. This will allow you to convert images without any problems and speed up your website now.', 'webp-converter-for-media' ),
|
||||
'<strong class="webpcContent__tip">',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-not-installed-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LibsWithoutWebpSupportNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'libs_without_webp_support';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'The required GD or Imagick library is installed on your server, but it does not support the WebP format.', 'webp-converter-for-media' ) . ' ' . __( 'This means that you cannot convert images to the WebP format on your server, because it does not meet the plugin requirements described in %1$sthe plugin FAQ%2$s. This issue is not dependent on the plugin.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-without-webp-support-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
|
||||
if ( function_exists( 'curl_init' ) ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( '%1$sHowever, we have a solution for you!%2$s You can activate %3$sthe PRO version%4$s of the plugin that allows you to convert images using a remote server. This will allow you to convert images without any problems and speed up your website now.', 'webp-converter-for-media' ),
|
||||
'<strong class="webpcContent__tip">',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-libs-without-webp-support-upgrade" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* Stores information about server configuration error.
|
||||
*/
|
||||
interface NoticeInterface {
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function get_key(): string;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function get_message(): array;
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Loader\PassthruLoader;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PassthruExecutionNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'passthru_execution';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$passthru_url = PassthruLoader::get_loader_url();
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'Execution of the PHP file from the "%s" path is blocked on your server, or access to this file is blocked. Add an exception and enable this file to be executed via an HTTP request. To do this, check the security plugin settings (if you are using them) or the security settings of your server.', 'webp-converter-for-media' ),
|
||||
'<a href="' . $passthru_url . '" target="_blank">' . $passthru_url . '</a>',
|
||||
'<br><br>'
|
||||
),
|
||||
__( 'In this case, please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PassthruNotWorkingNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'passthru_not_working';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: loader name */
|
||||
__( 'The %1$s loading mode is not compatible with your server. Sorry for the inconvenience.', 'webp-converter-for-media' ),
|
||||
'Pass Thru'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathHtaccessNotWritableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_htaccess_not_writable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: server path */
|
||||
__( 'Unable to create or edit .htaccess file (is_readable() or is_writable() function returns false). Change directory permissions. The current path of the file is: %1$s. Please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '/.htaccess</strong>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathUploadsUnavailableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_uploads_unavailable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( 'The path for /uploads files does not exist (the is_dir() function returns false). Use the %1$s filter to set the correct path. The current path is: %2$s. Please, read %3$sthe plugin FAQ%4$s to learn more.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-path-uploads-unavailable-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathWebpDuplicatedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_webp_duplicated';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path */
|
||||
__( 'The paths for /uploads files and for saving converted WebP files are the same. Change them using the %1$s filter. The current path for them is: %2$s.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'uploads' ) . '</strong>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class PathWebpNotWritableNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'path_webp_not_writable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: filter name, %2$s: server path, %3$s: open anchor tag, %4$s: close anchor tag */
|
||||
__( 'The path for saving converted WebP files does not exist and cannot be created (the is_writable() function returns false). Use the %1$s filter to set the correct path. The current path is: %2$s. Please, read %3$sthe plugin FAQ%4$s to learn more.', 'webp-converter-for-media' ),
|
||||
'<strong>webpc_dir_path</strong>',
|
||||
'<strong>' . apply_filters( 'webpc_dir_path', '', 'webp' ) . '</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-path-webp-not-writable-faq" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
use WebpConverter\Service\EnvDetector;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesCachedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_cached';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$message = [
|
||||
__( 'It appears that your server uses the cache for HTTP requests. The rules from the .htaccess file or from the Nginx configuration are not executed every time when an image is loaded, but the last redirect from cache is performed.', 'webp-converter-for-media' ),
|
||||
];
|
||||
|
||||
if ( EnvDetector::is_cdn_bunny() ) {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: service name, %3$s: close strong tag, %4$s: open anchor tag, %5$s: close anchor tag */
|
||||
__( '%1$sIf you are using the %2$s service%3$s, please follow %4$sour manual%5$s first to allow the plugin to work properly.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'BunnyCDN',
|
||||
'</strong>',
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-cached-bunny-instruction" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
}
|
||||
|
||||
$message[] = implode(
|
||||
'',
|
||||
[
|
||||
'<em><strong>' . __( 'Please, contact your hosting\'s technical support or CDN\'s support and send them the following message:', 'webp-converter-for-media' ) . '</strong></em>',
|
||||
'<em>' . implode(
|
||||
'<br>',
|
||||
[
|
||||
sprintf(
|
||||
/* translators: %1$s: home URL */
|
||||
__( 'I have a problem with the cache for HTTP requests on my website - %1$s. This prevents JPEG or PNG files from being dynamically redirected to WebP or AVIF, depending on whether the browser supports the format. Here are potential sources of this issue:', 'webp-converter-for-media' ),
|
||||
get_home_url()
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: header name, %2$s: additional information */
|
||||
__( '- the server or CDN server does not support the %1$s HTTP header or handles it incorrectly (%2$s)', 'webp-converter-for-media' ),
|
||||
'<strong>"Vary: Accept"</strong>',
|
||||
__( 'the cache for redirects should be based not only on the URL to the file, but also on the value of the Accept header sent by the browser', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %1$s: header name, %2$s: additional information */
|
||||
__( '- the server or CDN server does not support the %1$s HTTP header or handles it incorrectly (%2$s)', 'webp-converter-for-media' ),
|
||||
'<strong>"Cache-Control: private"</strong>',
|
||||
__( 'this header should be able to disable caching for static files on the CDN server or proxy server', 'webp-converter-for-media' )
|
||||
),
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( '- the website is running on the Nginx server without support for .htaccess files and not all the steps described in our instruction (%s) have been followed correctly', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-cached-nginx-instruction" target="_blank">https://url.mattplugins.com/converter-error-rewrites-cached-nginx-instruction</a>'
|
||||
),
|
||||
]
|
||||
) . '</em>',
|
||||
]
|
||||
);
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesNotExecutedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_not_executed';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
$server_name = $this->get_nginx_server_name();
|
||||
$message = [];
|
||||
|
||||
if ( $server_name === null ) {
|
||||
$message[] = __( 'It appears that your server does not support using .htaccess files from custom locations, or it requires additional configuration for the plugin to function properly.', 'webp-converter-for-media' );
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'Please check %1$sour instruction%2$s which should help you solve your problem. This will allow the plugin to function properly.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-executed-instruction" target="_blank">',
|
||||
'</a>'
|
||||
);
|
||||
} else {
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: server name */
|
||||
__( 'For the %1$s server, please contact your hosting\'s technical support to allow the plugin to function properly. Your server needs additional configuration, but your hosting\'s technical support will take care of it for you. Please, send them the following message:', 'webp-converter-for-media' ),
|
||||
$server_name
|
||||
);
|
||||
$message[] = implode(
|
||||
'',
|
||||
[
|
||||
'<em>',
|
||||
sprintf(
|
||||
/* translators: %1$s: plugin name, %2$s: home URL */
|
||||
__( 'I am trying to configure the %1$s plugin that supports the WebP and AVIF format. I need your help in adding the required rules to the Nginx configuration of my website - %2$s.', 'webp-converter-for-media' ),
|
||||
'Converter for Media',
|
||||
get_home_url()
|
||||
),
|
||||
' ',
|
||||
sprintf(
|
||||
/* translators: %s: anchor tag */
|
||||
__( 'You can find more information in the instruction: %s', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-executed-message" target="_blank">https://url.mattplugins.com/converter-error-rewrites-not-executed-message</a>',
|
||||
'Configuration for Nginx'
|
||||
),
|
||||
'</em>',
|
||||
]
|
||||
);
|
||||
$message[] = sprintf(
|
||||
/* translators: %1$s: open strong tag, %2$s: close strong tag */
|
||||
__( '%1$sPlease, copy the above message and send it to the technical support of your hosting.%2$s They should help you in this matter.', 'webp-converter-for-media' ),
|
||||
'<strong>',
|
||||
'</strong>'
|
||||
);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
private function get_nginx_server_name() {
|
||||
if ( getenv( 'IS_WPE' ) ) {
|
||||
return 'WP Engine';
|
||||
} elseif ( strpos( strtolower( $_SERVER['SERVER_SOFTWARE'] ?? '' ), 'nginx' ) !== false ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
return 'Nginx';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesNotWorkingNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_not_working';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'It appears that redirects on your server are not working. This means that your server configuration is not compatible with this plugin. Adapting the redirects from the .htaccess file to your server configuration is necessary for the plugin to work properly.', 'webp-converter-for-media' ),
|
||||
sprintf(
|
||||
/* translators: %1$s: open anchor tag, %2$s: close anchor tag */
|
||||
__( 'In this case, please, %1$scontact us%2$s. We will try to help you.', 'webp-converter-for-media' ),
|
||||
'<a href="https://url.mattplugins.com/converter-error-rewrites-not-working-contact" target="_blank">',
|
||||
'</a>'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RewritesUploadsBlockedNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'rewrites_uploads_blocked';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %1$s: directory path */
|
||||
__( 'It appears that mod_rewrite is blocked on your server for the %1$s directory. The blocking issue applies to rewrites from the .htaccess file for files from the %1$s directory. Please, ask your hosting\'s technical support to unblock rewrites from the .htaccess file for files in the %1$s directory.', 'webp-converter-for-media' ),
|
||||
'/wp-content/uploads/'
|
||||
),
|
||||
__( 'In this case, please, contact your server administrator.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class SettingsIncorrectNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'settings_incorrect';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'The plugin settings are incorrect! Check them out and save them again. Please, remember that you need to have at least one option selected for each field.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class UnsupportedPlaygroundServerNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'unsupported_server';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
sprintf(
|
||||
/* translators: %s: server name */
|
||||
__( 'The %s environment on which you launched your website does not meet the technical requirements of our plugin. Unfortunately, we have no control over the configuration of this server. Please, test the plugin on a different server and you will surely be satisfied.', 'webp-converter-for-media' ),
|
||||
'WordPress Playground'
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Error\Notice;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class WebpRequiredNotice implements NoticeInterface {
|
||||
|
||||
const ERROR_KEY = 'webp_required';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_key(): string {
|
||||
return self::ERROR_KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_message(): array {
|
||||
return [
|
||||
__( 'WebP as an output format is required. In the "Output formats" option, select the WebP format.', 'webp-converter-for-media' ),
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ConversionErrorException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Error occurred while converting image: "%s".';
|
||||
const ERROR_CODE = 'convert_error';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
abstract class ExceptionAbstract extends \Exception implements ExceptionInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
final public function __construct( $value = [] ) {
|
||||
$this->code = $this->get_error_status();
|
||||
parent::__construct( $this->get_error_message( (array) $value ) );
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
interface ExceptionInterface {
|
||||
|
||||
/**
|
||||
* @param mixed[]|string $value Params of exception.
|
||||
*/
|
||||
public function __construct( $value = [] );
|
||||
|
||||
/**
|
||||
* Returns message of error.
|
||||
*
|
||||
* @param string[] $values Params from class constructor.
|
||||
*
|
||||
* @return string Error message.
|
||||
*/
|
||||
public function get_error_message( array $values ): string;
|
||||
|
||||
/**
|
||||
* Returns status of error.
|
||||
*
|
||||
* @return string Error status.
|
||||
*/
|
||||
public function get_error_status(): string;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ExtensionUnsupportedException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Unsupported extension "%s" for file "%s".';
|
||||
const ERROR_CODE = 'unsupported_extension';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0], $values[1] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class FilesizeOversizeException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Image is larger than the maximum size of %1$sMB: "%2$s".';
|
||||
const ERROR_CODE = 'max_filezile';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
$number = (int) $values[0];
|
||||
return sprintf(
|
||||
self::ERROR_MESSAGE,
|
||||
round( $number / 1024 / 1024 ),
|
||||
$values[1]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class FunctionUnavailableException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Server configuration: "%s" function is not available.';
|
||||
const ERROR_CODE = 'server_configuration';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ImageAnimatedException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = '"%s" is an unsupported animated image file.';
|
||||
const ERROR_CODE = 'invalid_animated_image';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ImageInvalidException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = '"%s" is not a valid image file.';
|
||||
const ERROR_CODE = 'invalid_image';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ImagickNotSupportWebpException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Server configuration: Imagick does not support WebP format.';
|
||||
const ERROR_CODE = 'server_configuration';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return self::ERROR_MESSAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ImagickUnavailableException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Server configuration: Imagick module is not available with this PHP installation.';
|
||||
const ERROR_CODE = 'server_configuration';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return self::ERROR_MESSAGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class LargerThanOriginalException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Image "%1$s" converted to .%2$s is larger than original and converted .%2$s file has been deleted.';
|
||||
const ERROR_CODE = 'larger_than_original';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf(
|
||||
self::ERROR_MESSAGE,
|
||||
$values[0],
|
||||
pathinfo( $values[1], PATHINFO_EXTENSION )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class OutputPathException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'An error occurred creating destination directory for "%s" file.';
|
||||
const ERROR_CODE = 'output_path';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RemoteErrorResponseException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = null;
|
||||
const ERROR_CODE = 'remote_response_error';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return $values[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class RemoteRequestException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'There was an error connecting to the API, received a response code of %1$s: "%2$s".';
|
||||
const ERROR_CODE = 'remote_request_failed';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0], $values[1] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ResolutionOversizeException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Image is larger than maximum 8K resolution: "%s".';
|
||||
const ERROR_CODE = 'max_resolution';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class ServerConfigurationException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Server configuration: "%s" function is not available.';
|
||||
const ERROR_CODE = 'server_configuration';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Exception;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
class SourcePathException extends ExceptionAbstract {
|
||||
|
||||
const ERROR_MESSAGE = 'Source path "%s" for image does not exist or is unreadable.';
|
||||
const ERROR_CODE = 'file_unreadable';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_message( array $values ): string {
|
||||
return sprintf( self::ERROR_MESSAGE, $values[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_error_status(): string {
|
||||
return self::ERROR_CODE;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter;
|
||||
|
||||
/**
|
||||
* Interface for class which has action call to integrates with WordPress hooks.
|
||||
*/
|
||||
interface HookableInterface {
|
||||
|
||||
/**
|
||||
* Integrates with WordPress hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init_hooks();
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Loader;
|
||||
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports method of loading images using rewrites from .htaccess file.
|
||||
*/
|
||||
class HtaccessBypassingLoader extends HtaccessLoader {
|
||||
|
||||
const LOADER_TYPE = 'htaccess_bypassing';
|
||||
const FILENAME_SUFFIX = '-optimized';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::LOADER_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_admin_hooks() {
|
||||
add_filter( 'webpc_debug_image_url', [ $this, 'update_image_urls' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_front_end_hooks() {
|
||||
add_action( 'init', [ $this, 'start_buffering' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @internal
|
||||
*/
|
||||
public function start_buffering() {
|
||||
if ( ! ( ( defined( 'DOING_AJAX' ) && DOING_AJAX ) || ( ! is_admin() && ! is_network_admin() ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start(
|
||||
function ( $buffer ) {
|
||||
return $this->update_image_urls( $buffer );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces URLs to source images in output buffer.
|
||||
*
|
||||
* @param string $buffer Contents of output buffer.
|
||||
*
|
||||
* @return string Contents of output buffer.
|
||||
* @internal
|
||||
*/
|
||||
public function update_image_urls( string $buffer ): string {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
$extensions = implode( '|', $settings[ SupportedExtensionsOption::OPTION_NAME ] );
|
||||
if ( ! $extensions ) {
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
$path_dir_uploads = apply_filters( 'webpc_dir_name', '', 'uploads' );
|
||||
return preg_replace_callback(
|
||||
'/((?:\/' . str_replace( '/', '\\/', $path_dir_uploads ) . '\/)(?:.*?))\.(' . $extensions . ')/',
|
||||
function ( $matches ) {
|
||||
return str_replace( self::FILENAME_SUFFIX, '', $matches[1] ) . self::FILENAME_SUFFIX . '.' . $matches[2];
|
||||
},
|
||||
$buffer
|
||||
) ?: $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_rules_to_wp_content( array $settings ): array {
|
||||
return [
|
||||
$this->get_suffix_redirect_rules( $settings ),
|
||||
$this->get_mod_rewrite_rules( $settings ),
|
||||
$this->get_mod_headers_rules( $settings ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates redirects for suffixed URLs.
|
||||
*
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
private function get_suffix_redirect_rules( array $settings ): string {
|
||||
$content = '';
|
||||
$extensions = implode( '|', $settings[ SupportedExtensionsOption::OPTION_NAME ] ) ?: '.+';
|
||||
|
||||
$content .= '<IfModule mod_rewrite.c>' . PHP_EOL;
|
||||
$content .= ' RewriteEngine On' . PHP_EOL;
|
||||
$content .= ' RewriteCond %{REQUEST_FILENAME} !-f' . PHP_EOL;
|
||||
$content .= ' RewriteRule ^(.+)' . self::FILENAME_SUFFIX . '\.(' . $extensions . ')$ $1.$2 [NC]' . PHP_EOL;
|
||||
$content .= '</IfModule>';
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
@ -0,0 +1,389 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Loader;
|
||||
|
||||
use WebpConverter\Service\CloudflareConfigurator;
|
||||
use WebpConverter\Service\EnvDetector;
|
||||
use WebpConverter\Service\OptionsAccessManager;
|
||||
use WebpConverter\Service\PathsGenerator;
|
||||
use WebpConverter\Settings\Option\CloudflareApiTokenOption;
|
||||
use WebpConverter\Settings\Option\CloudflareZoneIdOption;
|
||||
use WebpConverter\Settings\Option\ExtraFeaturesOption;
|
||||
use WebpConverter\Settings\Option\RewriteInheritanceOption;
|
||||
use WebpConverter\Settings\Option\SupportedExtensionsOption;
|
||||
|
||||
/**
|
||||
* Supports method of loading images using rewrites from .htaccess file.
|
||||
*/
|
||||
class HtaccessLoader extends LoaderAbstract {
|
||||
|
||||
const LOADER_TYPE = 'htaccess';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_type(): string {
|
||||
return self::LOADER_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_admin_hooks() {
|
||||
add_filter( 'webpc_htaccess_rewrite_root', [ $this, 'modify_document_root_path' ] );
|
||||
add_filter( 'webpc_htaccess_rewrite_output', [ $this, 'modify_output_root_path' ], 10, 2 );
|
||||
add_filter( 'webpc_debug_image_url', [ $this, 'update_image_urls_to_bunny_cdn' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_front_end_hooks() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function activate_loader( bool $is_debug = false ) {
|
||||
$settings = ( ! $is_debug ) ? $this->plugin_data->get_plugin_settings() : $this->plugin_data->get_debug_settings();
|
||||
|
||||
$this->deactivate_loader();
|
||||
|
||||
$this->add_rewrite_rules_to_wp_content( true, $settings );
|
||||
$this->add_rewrite_rules_to_uploads( true, $settings );
|
||||
$this->add_rewrite_rules_to_uploads_webp( true, $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deactivate_loader() {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
|
||||
$this->add_rewrite_rules_to_wp_content( false, $settings );
|
||||
$this->add_rewrite_rules_to_uploads( false, $settings );
|
||||
$this->add_rewrite_rules_to_uploads_webp( false, $settings );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $original_path .
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function modify_document_root_path( string $original_path ): string {
|
||||
if ( isset( $_SERVER['SERVER_ADMIN'] ) && strpos( $_SERVER['SERVER_ADMIN'], '.home.pl' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
|
||||
return '%{DOCUMENT_ROOT}' . str_replace( '//', '/', ABSPATH );
|
||||
}
|
||||
|
||||
return $original_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $output_path .
|
||||
* @param string $root_path .
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function modify_output_root_path( string $output_path, string $root_path ): string {
|
||||
if ( $output_path === $root_path ) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return $output_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url .
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function update_image_urls_to_bunny_cdn( string $url ): string {
|
||||
if ( ! class_exists( '\BunnyCDN' ) || ! EnvDetector::is_cdn_bunny() ) {
|
||||
return $url;
|
||||
}
|
||||
$options = \BunnyCDN::getOptions();
|
||||
|
||||
return str_replace( $options['site_url'], ( is_ssl() ? 'https://' : 'http://' ) . $options['cdn_domain_name'], $url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves rules to .htaccess file in /wp-content directory.
|
||||
*
|
||||
* @param bool $is_active Is loader active?
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_rewrite_rules_to_wp_content( bool $is_active, array $settings ) {
|
||||
$path = dirname( apply_filters( 'webpc_dir_path', '', 'uploads' ) );
|
||||
if ( ! $is_active ) {
|
||||
$this->save_rewrites_in_htaccesss( $path );
|
||||
return;
|
||||
}
|
||||
|
||||
$content = $this->add_comments_to_rules( $this->get_rules_to_wp_content( $settings ) );
|
||||
|
||||
$content = apply_filters( 'webpc_htaccess_rules', $content, $path . '/.htaccess' );
|
||||
$this->save_rewrites_in_htaccesss( $path, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function get_rules_to_wp_content( array $settings ): array {
|
||||
return [
|
||||
$this->get_mod_rewrite_rules( $settings ),
|
||||
$this->get_mod_headers_rules( $settings ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves rules to .htaccess file in /uploads directory.
|
||||
*
|
||||
* @param bool $is_active Is loader active?
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_rewrite_rules_to_uploads( bool $is_active, array $settings ) {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'uploads' );
|
||||
if ( ! $is_active ) {
|
||||
$this->save_rewrites_in_htaccesss( $path );
|
||||
return;
|
||||
}
|
||||
|
||||
$path_parts = explode( '/', apply_filters( 'webpc_dir_name', '', 'uploads' ) );
|
||||
$content = $this->add_comments_to_rules(
|
||||
[
|
||||
$this->get_mod_rewrite_rules( $settings, end( $path_parts ) ),
|
||||
]
|
||||
);
|
||||
|
||||
$content = apply_filters( 'webpc_htaccess_rules', $content, $path . '/.htaccess' );
|
||||
$this->save_rewrites_in_htaccesss( $path, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves rules to .htaccess file in /uploads-webpc directory.
|
||||
*
|
||||
* @param bool $is_active Is loader active?
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_rewrite_rules_to_uploads_webp( bool $is_active, array $settings ) {
|
||||
$path = apply_filters( 'webpc_dir_path', '', 'webp' );
|
||||
if ( ! $is_active ) {
|
||||
$this->save_rewrites_in_htaccesss( $path );
|
||||
return;
|
||||
}
|
||||
|
||||
$content = $this->add_comments_to_rules(
|
||||
[
|
||||
$this->get_mod_mime_rules( $settings ),
|
||||
$this->get_mod_expires_rules(),
|
||||
]
|
||||
);
|
||||
|
||||
$content = apply_filters( 'webpc_htaccess_rules', $content, $path . '/.htaccess' );
|
||||
$this->save_rewrites_in_htaccesss( $path, $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates rules for rewriting source images to output images.
|
||||
*
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
* @param string|null $output_path_suffix Location of .htaccess file.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
protected function get_mod_rewrite_rules( array $settings, string $output_path_suffix = null ): string {
|
||||
$content = '';
|
||||
if ( ! $settings[ SupportedExtensionsOption::OPTION_NAME ] ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$document_root = PathsGenerator::get_rewrite_root();
|
||||
$root_suffix = PathsGenerator::get_rewrite_path();
|
||||
$root_suffix_output = apply_filters( 'webpc_htaccess_rewrite_output', $root_suffix, $document_root );
|
||||
$output_path = apply_filters( 'webpc_dir_name', '', 'webp' );
|
||||
if ( $output_path_suffix !== null ) {
|
||||
$output_path .= '/' . $output_path_suffix;
|
||||
}
|
||||
|
||||
$content .= '<IfModule mod_rewrite.c>' . PHP_EOL;
|
||||
$content .= ' RewriteEngine On' . PHP_EOL;
|
||||
if ( apply_filters( 'webpc_htaccess_mod_rewrite_inherit', ! $settings[ RewriteInheritanceOption::OPTION_NAME ] ) === true ) {
|
||||
$content .= ' RewriteOptions Inherit' . PHP_EOL;
|
||||
}
|
||||
|
||||
$content .= PHP_EOL;
|
||||
$content .= ' ' . apply_filters( 'webpc_htaccess_original_cond', 'RewriteCond %{QUERY_STRING} original$' ) . PHP_EOL;
|
||||
$content .= ' RewriteCond %{REQUEST_FILENAME} -f' . PHP_EOL;
|
||||
$content .= ' RewriteRule . - [L]' . PHP_EOL;
|
||||
|
||||
foreach ( $this->format_factory->get_mime_types() as $format => $mime_type ) {
|
||||
$content .= PHP_EOL;
|
||||
foreach ( $settings[ SupportedExtensionsOption::OPTION_NAME ] as $ext ) {
|
||||
if ( $format === $ext ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$content .= " RewriteCond %{HTTP_ACCEPT} {$mime_type}" . PHP_EOL;
|
||||
if ( in_array( ExtraFeaturesOption::OPTION_VALUE_ONLY_SMALLER, $settings[ ExtraFeaturesOption::OPTION_NAME ] ) ) {
|
||||
$content .= " RewriteCond %{REQUEST_FILENAME} -f" . PHP_EOL;
|
||||
}
|
||||
|
||||
if ( $document_root === '%{DOCUMENT_ROOT}/' ) {
|
||||
$content .= " RewriteCond %{DOCUMENT_ROOT}/{$output_path}/$1.{$ext}.{$format} -f" . PHP_EOL;
|
||||
} elseif ( strpos( $document_root, '%{DOCUMENT_ROOT}' ) !== false ) {
|
||||
$content .= " RewriteCond {$document_root}{$output_path}/$1.{$ext}.{$format} -f [OR]" . PHP_EOL;
|
||||
$content .= " RewriteCond %{DOCUMENT_ROOT}/{$output_path}/$1.{$ext}.{$format} -f" . PHP_EOL;
|
||||
} else {
|
||||
$content .= " RewriteCond {$document_root}{$output_path}/$1.{$ext}.{$format} -f [OR]" . PHP_EOL;
|
||||
$content .= " RewriteCond %{DOCUMENT_ROOT}{$root_suffix}{$output_path}/$1.{$ext}.{$format} -f" . PHP_EOL;
|
||||
}
|
||||
|
||||
if ( apply_filters( 'webpc_htaccess_mod_rewrite_referer', false ) === true ) {
|
||||
$content .= " RewriteCond %{HTTP_HOST}@@%{HTTP_REFERER} ^([^@]*)@@https?://\\1/.*" . PHP_EOL;
|
||||
}
|
||||
$content .= " RewriteRule (.+)\.{$ext}$ {$root_suffix_output}{$output_path}/$1.{$ext}.{$format} [NC,T={$mime_type},L]" . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$content .= '</IfModule>' . PHP_EOL;
|
||||
|
||||
return apply_filters( 'webpc_htaccess_mod_rewrite', trim( $content ), $output_path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates rules for mod_headers.
|
||||
*
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
protected function get_mod_headers_rules( array $settings ): string {
|
||||
$content = '';
|
||||
$extensions = implode( '|', $settings[ SupportedExtensionsOption::OPTION_NAME ] );
|
||||
|
||||
$cache_control = true;
|
||||
if ( $settings[ CloudflareZoneIdOption::OPTION_NAME ] && $settings[ CloudflareApiTokenOption::OPTION_NAME ]
|
||||
&& OptionsAccessManager::get_option( CloudflareConfigurator::REQUEST_CACHE_CONFIG_OPTION ) === 'yes' ) {
|
||||
$cache_control = false;
|
||||
} elseif ( EnvDetector::is_cdn_bunny() ) {
|
||||
$cache_control = false;
|
||||
}
|
||||
|
||||
$content .= '<IfModule mod_headers.c>' . PHP_EOL;
|
||||
if ( $extensions ) {
|
||||
$content .= ' <FilesMatch "(?i)\.(' . $extensions . ')(\.(webp|avif))?$">' . PHP_EOL;
|
||||
}
|
||||
if ( apply_filters( 'webpc_htaccess_cache_control_private', $cache_control ) ) {
|
||||
$content .= ' Header always set Cache-Control "private"' . PHP_EOL;
|
||||
}
|
||||
$content .= ' Header append Vary "Accept"' . PHP_EOL;
|
||||
if ( $extensions ) {
|
||||
$content .= ' </FilesMatch>' . PHP_EOL;
|
||||
}
|
||||
$content .= '</IfModule>';
|
||||
|
||||
return apply_filters( 'webpc_htaccess_mod_headers', $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates rules for mod_expires.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
private function get_mod_expires_rules(): string {
|
||||
$content = '';
|
||||
|
||||
$content .= '<IfModule mod_expires.c>' . PHP_EOL;
|
||||
$content .= ' ExpiresActive On' . PHP_EOL;
|
||||
foreach ( $this->format_factory->get_mime_types() as $format => $mime_type ) {
|
||||
$content .= " ExpiresByType {$mime_type} \"access plus 1 year\"" . PHP_EOL;
|
||||
}
|
||||
$content .= '</IfModule>';
|
||||
|
||||
return apply_filters( 'webpc_htaccess_mod_expires', $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates rules that add support for output formats.
|
||||
*
|
||||
* @param mixed[] $settings Plugin settings.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
private function get_mod_mime_rules( array $settings ): string {
|
||||
$content = '';
|
||||
if ( ! $settings[ SupportedExtensionsOption::OPTION_NAME ] ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$content .= '<IfModule mod_mime.c>' . PHP_EOL;
|
||||
foreach ( $this->format_factory->get_mime_types() as $format => $mime_type ) {
|
||||
$content .= " AddType {$mime_type} .{$format}" . PHP_EOL;
|
||||
}
|
||||
$content .= '</IfModule>';
|
||||
|
||||
return apply_filters( 'webpc_htaccess_mod_mime', $content );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds comments before and after rules for .htaccess file.
|
||||
*
|
||||
* @param string[] $rules Rules for .htaccess file.
|
||||
*
|
||||
* @return string Rules for .htaccess file.
|
||||
*/
|
||||
private function add_comments_to_rules( array $rules ): string {
|
||||
if ( ! $rules ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$rows = [];
|
||||
$rows[] = '';
|
||||
$rows[] = '# BEGIN Converter for Media';
|
||||
$rows[] = '# ! --- DO NOT EDIT PREVIOUS LINE --- !';
|
||||
$rows = array_merge( $rows, array_filter( $rules ) );
|
||||
$rows[] = '# ! --- DO NOT EDIT NEXT LINE --- !';
|
||||
$rows[] = '# END Converter for Media';
|
||||
$rows[] = '';
|
||||
|
||||
return implode( PHP_EOL, $rows );
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves rules to .htaccess file in selected location.
|
||||
*
|
||||
* @param string $path_dir Location of .htaccess file.
|
||||
* @param string $rules Rules for .htaccess file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function save_rewrites_in_htaccesss( string $path_dir, string $rules = '' ) {
|
||||
$path_file = $path_dir . '/.htaccess';
|
||||
|
||||
$code = ( is_readable( $path_file ) ) ? file_get_contents( $path_file ) ?: '' : '';
|
||||
$code = preg_replace(
|
||||
'/((:?[\r\n|\r|\n]?)# BEGIN (Converter for Media|WebP Converter)(.*?)# END (Converter for Media|WebP Converter)(:?(:?[\r\n|\r|\n]+)?))/s',
|
||||
'',
|
||||
$code
|
||||
);
|
||||
if ( $rules && $code ) {
|
||||
$code = PHP_EOL . $code;
|
||||
}
|
||||
$code = $rules . $code;
|
||||
|
||||
if ( is_writable( $path_dir ) ) {
|
||||
file_put_contents( $path_file, $code );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace WebpConverter\Loader;
|
||||
|
||||
use WebpConverter\Conversion\Format\FormatFactory;
|
||||
use WebpConverter\PluginData;
|
||||
use WebpConverter\PluginInfo;
|
||||
use WebpConverter\Settings\Option\LoaderTypeOption;
|
||||
|
||||
/**
|
||||
* Abstract class for class that supports method of loading images.
|
||||
*/
|
||||
abstract class LoaderAbstract implements LoaderInterface {
|
||||
|
||||
const ACTION_NAME = 'webpc_refresh_loader';
|
||||
|
||||
/**
|
||||
* @var PluginInfo
|
||||
*/
|
||||
protected $plugin_info;
|
||||
|
||||
/**
|
||||
* @var PluginData
|
||||
*/
|
||||
protected $plugin_data;
|
||||
|
||||
/**
|
||||
* @var FormatFactory
|
||||
*/
|
||||
protected $format_factory;
|
||||
|
||||
public function __construct( PluginInfo $plugin_info, PluginData $plugin_data, FormatFactory $format_factory ) {
|
||||
$this->plugin_info = $plugin_info;
|
||||
$this->plugin_data = $plugin_data;
|
||||
$this->format_factory = $format_factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function is_active_loader(): bool {
|
||||
$settings = $this->plugin_data->get_plugin_settings();
|
||||
return ( $settings[ LoaderTypeOption::OPTION_NAME ] === $this->get_type() );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_admin_hooks() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function init_front_end_hooks() {
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user