Plugin Updates
This commit is contained in:
@@ -138,7 +138,7 @@ window.imagify = window.imagify || {};
|
||||
/**
|
||||
* Fade CDN URL field.
|
||||
*/
|
||||
$( '[name="imagify_settings[display_webp_method]"]' ).on( 'change.imagify init.imagify', function( e ) {
|
||||
$( '[name="imagify_settings[display_nextgen_method]"]' ).on( 'change.imagify init.imagify', function( e ) {
|
||||
if ( 'picture' === e.target.value ) {
|
||||
$( e.target ).closest( '.imagify-radio-group' ).next( '.imagify-options-line' ).removeClass( 'imagify-faded' );
|
||||
} else {
|
||||
@@ -496,7 +496,7 @@ window.imagify = window.imagify || {};
|
||||
.on( 'imagifybeat-send', this.addRequirementsImagifybeat )
|
||||
.on( 'imagifybeat-tick', { imagifyOptionsBulk: this }, this.processRequirementsImagifybeat );
|
||||
|
||||
if ( false !== imagifyOptions.bulk.progress_webp.total && false !== imagifyOptions.bulk.progress_webp.remaining ) {
|
||||
if ( false !== imagifyOptions.bulk.progress_next_gen.total && false !== imagifyOptions.bulk.progress_next_gen.remaining ) {
|
||||
// Reset properties.
|
||||
w.imagify.optionsBulk.error = false;
|
||||
w.imagify.optionsBulk.working = true;
|
||||
@@ -511,10 +511,10 @@ window.imagify = window.imagify || {};
|
||||
|
||||
this.$missingWebpMessage.hide().attr('aria-hidden', 'true');
|
||||
|
||||
processed = imagifyOptions.bulk.progress_webp.total - imagifyOptions.bulk.progress_webp.remaining;
|
||||
progress = Math.floor( processed / imagifyOptions.bulk.progress_webp.total * 100 );
|
||||
processed = imagifyOptions.bulk.progress_next_gen.total - imagifyOptions.bulk.progress_next_gen.remaining;
|
||||
progress = Math.floor( processed / imagifyOptions.bulk.progress_next_gen.total * 100 );
|
||||
this.$progressBar.css( 'width', progress + '%' );
|
||||
this.$progressText.text( processed + '/' + imagifyOptions.bulk.progress_webp.total );
|
||||
this.$progressText.text( processed + '/' + imagifyOptions.bulk.progress_next_gen.total );
|
||||
|
||||
this.$progressWrap.slideDown().attr( 'aria-hidden', 'false' ).removeClass( 'hidden' );
|
||||
}
|
||||
@@ -655,7 +655,7 @@ window.imagify = window.imagify || {};
|
||||
|
||||
_this = this;
|
||||
|
||||
$.get( this.getAjaxUrl( 'MissingWebp', imagifyOptions.bulk.contexts ) )
|
||||
$.get( this.getAjaxUrl( 'MissingNextGen', imagifyOptions.bulk.contexts ) )
|
||||
.done( function( response ) {
|
||||
var errorMessage;
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -44,7 +44,7 @@ class AdminBar {
|
||||
|
||||
if ( $user->is_free() ) {
|
||||
$text = esc_html__( 'Upgrade your plan now for more!', 'rocket' ) . '<br>' .
|
||||
esc_html__( 'From $4.99/month only, keep going with image optimization!', 'rocket' );
|
||||
esc_html__( 'From $5.99/month only, keep going with image optimization!', 'rocket' );
|
||||
$button_text = esc_html__( 'Upgrade My Plan', 'rocket' );
|
||||
$upgrade_link = IMAGIFY_APP_DOMAIN . '/subscription/?utm_source=plugin&utm_medium=notification';
|
||||
} elseif ( $user->is_growth() ) {
|
||||
|
||||
31
wp/wp-content/plugins/imagify/classes/Avif/Apache.php
Normal file
31
wp/wp-content/plugins/imagify/classes/Avif/Apache.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif;
|
||||
|
||||
use Imagify\WriteFile\AbstractApacheDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove contents to the .htaccess file to display AVIF images on the site.
|
||||
*/
|
||||
class Apache extends AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: avif file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
return trim( '
|
||||
<IfModule mod_mime.c>
|
||||
AddType image/avif .avif
|
||||
</IfModule>' );
|
||||
}
|
||||
}
|
||||
169
wp/wp-content/plugins/imagify/classes/Avif/Display.php
Normal file
169
wp/wp-content/plugins/imagify/classes/Avif/Display.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\WriteFile\WriteFileInterface;
|
||||
|
||||
/**
|
||||
* Display AVIF images on the site using picture tag.
|
||||
*/
|
||||
class Display implements SubscriberInterface {
|
||||
/**
|
||||
* Server conf object.
|
||||
*
|
||||
* @var WriteFileInterface|null
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $server_conf = null;
|
||||
|
||||
/**
|
||||
* Returns an array of events this subscriber listens to
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 12 ],
|
||||
'imagify_activation' => 'activate',
|
||||
'imagify_deactivation' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If display Next-Gen images, add the AVIF type to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$enabled = isset( $values['display_nextgen'] ) ? true : false;
|
||||
$result = false;
|
||||
|
||||
if ( $enabled ) {
|
||||
// Add the AVIF file type.
|
||||
$result = $this->get_server_conf()->add();
|
||||
} elseif ( ! $enabled ) {
|
||||
// Remove the AVIF file type.
|
||||
$result = $this->get_server_conf()->remove();
|
||||
}
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rules on plugin activation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function activate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove rules on plugin deactivation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function deactivate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_path = $conf->get_file_path();
|
||||
$filesystem = \Imagify_Filesystem::get_instance();
|
||||
|
||||
if ( ! $filesystem->exists( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $filesystem->is_writable( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param bool $relative True to get a path relative to the site’s root.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_file_path( $relative = false ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_path = $this->get_server_conf()->get_file_path();
|
||||
|
||||
if ( $relative ) {
|
||||
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server conf instance.
|
||||
* Note: nothing needed for nginx.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7;
|
||||
|
||||
if ( isset( $this->server_conf ) ) {
|
||||
return $this->server_conf;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$this->server_conf = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$this->server_conf = new IIS();
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
}
|
||||
}
|
||||
32
wp/wp-content/plugins/imagify/classes/Avif/IIS.php
Normal file
32
wp/wp-content/plugins/imagify/classes/Avif/IIS.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif;
|
||||
|
||||
use Imagify\WriteFile\AbstractIISDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove contents to the web.config file to display AVIF images on the site.
|
||||
*/
|
||||
class IIS extends AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: avif file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
return trim( '
|
||||
<!-- @parent /configuration/system.webServer -->
|
||||
<staticContent name="' . esc_attr( static::TAG_NAME ) . ' 1">
|
||||
<mimeMap fileExtension=".avif" mimeType="image/avif" />
|
||||
</staticContent>' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif\RewriteRules;
|
||||
|
||||
use Imagify\WriteFile\AbstractApacheDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the .htaccess file to display AVIF images on the site.
|
||||
*/
|
||||
class Apache extends AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for avif';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$extensions = str_replace( '|avif', '', $extensions );
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
<IfModule mod_setenvif.c>
|
||||
# Vary: Accept for all the requests to jpeg, png, and gif.
|
||||
SetEnvIf Request_URI "\.(' . $extensions . ')$" REQUEST_image
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase ' . $home_root . '
|
||||
|
||||
# Check if browser supports AVIF images.
|
||||
# Update the MIME type accordingly.
|
||||
RewriteCond %{HTTP_ACCEPT} image/avif
|
||||
|
||||
# Check if AVIF replacement image exists.
|
||||
RewriteCond %{REQUEST_FILENAME}.avif -f
|
||||
|
||||
# Serve AVIF image instead.
|
||||
RewriteRule (.+)\.(' . $extensions . ')$ $1.$2.avif [T=image/avif,NC]
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
# Update the MIME type accordingly.
|
||||
Header append Vary Accept env=REQUEST_image
|
||||
</IfModule>' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif\RewriteRules;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\WriteFile\WriteFileInterface;
|
||||
|
||||
/**
|
||||
* Display Avif images on the site with rewrite rules.
|
||||
*/
|
||||
class Display implements SubscriberInterface {
|
||||
/**
|
||||
* Configuration file writer.
|
||||
*
|
||||
* @var WriteFileInterface|null
|
||||
*/
|
||||
protected $server_conf = null;
|
||||
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_VALUE = 'rewrite';
|
||||
|
||||
/**
|
||||
* Returns an array of events this subscriber listens to
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 11 ],
|
||||
'imagify_settings_webp_info' => 'maybe_add_avif_info',
|
||||
'imagify_activation' => 'activate',
|
||||
'imagify_deactivation' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* If display AVIF images via rewrite rules, add the rules to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
$was_enabled = (bool) get_imagify_option( 'display_nextgen' );
|
||||
$is_enabled = ! empty( $values['display_nextgen'] );
|
||||
|
||||
// Which method?
|
||||
$old_value = get_imagify_option( 'display_nextgen_method' );
|
||||
$new_value = ! empty( $values['display_nextgen_method'] ) ? $values['display_nextgen_method'] : '';
|
||||
|
||||
// Decide when to add or remove rules.
|
||||
$is_rewrite = self::OPTION_VALUE === $new_value;
|
||||
$was_rewrite = self::OPTION_VALUE === $old_value;
|
||||
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( $is_enabled && $is_rewrite && ( ! $was_enabled || ! $was_rewrite ) ) {
|
||||
// Add the rewrite rules.
|
||||
$result = $this->get_server_conf()->add();
|
||||
} elseif ( $was_enabled && $was_rewrite && ( ! $is_enabled || ! $is_rewrite ) ) {
|
||||
// Remove the rewrite rules.
|
||||
$result = $this->get_server_conf()->remove();
|
||||
}
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the conf file is not writable, add a warning.
|
||||
*/
|
||||
public function maybe_add_avif_info() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$writable = $conf->is_file_writable();
|
||||
|
||||
if ( is_wp_error( $writable ) ) {
|
||||
$rules = $conf->get_new_contents();
|
||||
|
||||
if ( ! $rules ) {
|
||||
// Uh?
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
/* translators: %s is a file name. */
|
||||
esc_html__( 'If you choose to use rewrite rules, you will have to add the following lines manually to the %s file:', 'imagify' ),
|
||||
'<code>' . $this->get_file_path( true ) . '</code>'
|
||||
);
|
||||
|
||||
echo '<pre class="code">' . esc_html( $rules ) . '</pre>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add rules on plugin activation.
|
||||
*/
|
||||
public function activate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->add();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove rules on plugin deactivation.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function deactivate() {
|
||||
$conf = $this->get_server_conf();
|
||||
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_path = $conf->get_file_path();
|
||||
$filesystem = \Imagify_Filesystem::get_instance();
|
||||
|
||||
if ( ! $filesystem->exists( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
if ( ! $filesystem->is_writable( $file_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
* @param bool $relative True to get a path relative to the site’s root.
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_file_path( $relative = false ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file_path = $this->get_server_conf()->get_file_path();
|
||||
|
||||
if ( $relative ) {
|
||||
return \Imagify_Filesystem::get_instance()->make_path_relative( $file_path );
|
||||
}
|
||||
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server conf instance.
|
||||
*
|
||||
* @return WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7, $is_nginx;
|
||||
|
||||
if ( isset( $this->server_conf ) ) {
|
||||
return $this->server_conf;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$this->server_conf = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$this->server_conf = new IIS();
|
||||
} elseif ( $is_nginx ) {
|
||||
$this->server_conf = new Nginx();
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif\RewriteRules;
|
||||
|
||||
use Imagify\WriteFile\AbstractIISDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the web.config file to display AVIF images on the site.
|
||||
*/
|
||||
class IIS extends AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for avif';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @source https://github.com/igrigorik/webp-detect/blob/master/iis.config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$extensions = str_replace( '|avif', '', $extensions );
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
<!-- @parent /configuration/system.webServer/rewrite/rules -->
|
||||
<rule name="' . esc_attr( static::TAG_NAME ) . ' 2">
|
||||
<match url="^(' . $home_root . '.+)\.(' . $extensions . ')$" ignoreCase="true" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{HTTP_ACCEPT}" pattern="image/avif" ignoreCase="false" />
|
||||
<add input="{DOCUMENT_ROOT}/{R:1}{R:2}.avif" matchType="IsFile" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="{R:1}{R:2}.avif" logRewrittenUrl="true" />
|
||||
<serverVariables>
|
||||
<set name="ACCEPTS_AVIF" value="true" />
|
||||
</serverVariables>
|
||||
</rule>
|
||||
|
||||
<!-- @parent /configuration/system.webServer/rewrite/outboundRules -->
|
||||
<rule preCondition="IsAvif" name="' . esc_attr( static::TAG_NAME ) . ' 3">
|
||||
<match serverVariable="RESPONSE_Vary" pattern=".*" />
|
||||
<action type="Rewrite" value="Accept"/>
|
||||
</rule>
|
||||
<preConditions name="' . esc_attr( static::TAG_NAME ) . ' 4">
|
||||
<preCondition name="IsAvif">
|
||||
<add input="{ACCEPTS_AVIF}" pattern="true" ignoreCase="false" />
|
||||
</preCondition>
|
||||
</preConditions>' );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif\RewriteRules;
|
||||
|
||||
use Imagify\WriteFile\AbstractNginxDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the imagify.conf file to display AVIF images on the site.
|
||||
*/
|
||||
class Nginx extends AbstractNginxDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for avif';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Avif;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
use Imagify\Avif\RewriteRules\Display as RewriteRules;
|
||||
|
||||
/**
|
||||
* Service provider for AVIF rewrite rules
|
||||
*/
|
||||
class ServiceProvider extends AbstractServiceProvider {
|
||||
/**
|
||||
* Services provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [
|
||||
'avif_display',
|
||||
'avif_rewrite_rules',
|
||||
];
|
||||
|
||||
/**
|
||||
* Subscribers provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $subscribers = [
|
||||
'avif_display',
|
||||
'avif_rewrite_rules',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers the provided classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->getContainer()->share( 'avif_display', Display::class );
|
||||
$this->getContainer()->share( 'avif_rewrite_rules', RewriteRules::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscribers array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribers() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
}
|
||||
@@ -105,13 +105,19 @@ abstract class AbstractBulk implements BulkInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
* Tell if there are optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp() {
|
||||
return count( $this->get_optimized_media_ids_without_webp()['ids'] );
|
||||
public function has_optimized_media_without_nextgen() {
|
||||
$format = 'webp';
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$format = 'avif';
|
||||
}
|
||||
|
||||
return count( $this->get_optimized_media_ids_without_format( $format )['ids'] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,15 @@ class Bulk {
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'imagify_optimize_media', [ $this, 'optimize_media' ], 10, 3 );
|
||||
add_action( 'imagify_convert_webp', [ $this, 'generate_webp_versions' ], 10, 2 );
|
||||
add_action( 'imagify_convert_webp_finished', [ $this, 'clear_webp_transients' ], 10, 2 );
|
||||
add_action( 'imagify_convert_next_gen', [ $this, 'generate_nextgen_versions' ], 10, 2 );
|
||||
add_action( 'wp_ajax_imagify_bulk_optimize', [ $this, 'bulk_optimize_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_missing_webp_generation', [ $this, 'missing_webp_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_missing_nextgen_generation', [ $this, 'missing_nextgen_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_get_folder_type_data', [ $this, 'get_folder_type_data_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_bulk_info_seen', [ $this, 'bulk_info_seen_callback' ] );
|
||||
add_action( 'wp_ajax_imagify_bulk_get_stats', [ $this, 'bulk_get_stats_callback' ] );
|
||||
add_action( 'imagify_after_optimize', [ $this, 'check_optimization_status' ], 10, 2 );
|
||||
add_action( 'imagify_deactivation', [ $this, 'delete_transients_data' ] );
|
||||
add_action( 'update_option_imagify_settings', [ $this, 'maybe_generate_missing_nextgen' ], 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ class Bulk {
|
||||
delete_transient( 'imagify_custom-folders_optimize_running' );
|
||||
delete_transient( 'imagify_wp_optimize_running' );
|
||||
delete_transient( 'imagify_bulk_optimization_complete' );
|
||||
delete_transient( 'imagify_missing_webp_total' );
|
||||
delete_transient( 'imagify_missing_next_gen_total' );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,17 +173,31 @@ class Bulk {
|
||||
'message' => 'over-quota',
|
||||
];
|
||||
}
|
||||
$formats = imagify_nextgen_images_formats();
|
||||
$media_ids = [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
'no_file_path' => [],
|
||||
'no_backup' => [],
|
||||
],
|
||||
];
|
||||
foreach ( $formats as $format ) {
|
||||
$result = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_format( $format );
|
||||
$media_ids['ids'] = array_merge( $media_ids['ids'], $result['ids'] );
|
||||
}
|
||||
$get_unoptimized_media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level );
|
||||
|
||||
$media_ids = $this->get_bulk_instance( $context )->get_unoptimized_media_ids( $optimization_level );
|
||||
$media_ids['ids'] = array_merge( $media_ids['ids'], $get_unoptimized_media_ids );
|
||||
|
||||
if ( empty( $media_ids ) ) {
|
||||
if ( empty( $media_ids['ids'] ) ) {
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-images',
|
||||
];
|
||||
}
|
||||
$media_ids['ids'] = array_unique( $media_ids['ids'] );
|
||||
|
||||
foreach ( $media_ids as $media_id ) {
|
||||
foreach ( $media_ids['ids'] as $media_id ) {
|
||||
try {
|
||||
as_enqueue_async_action(
|
||||
'imagify_optimize_media',
|
||||
@@ -213,13 +227,14 @@ class Bulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the WebP generation
|
||||
* Runs the next-gen generation
|
||||
*
|
||||
* @param array $contexts An array of contexts (WP/Custom folders).
|
||||
* @param array $formats An array of format to generate.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function run_generate_webp( array $contexts ) {
|
||||
public function run_generate_nextgen( array $contexts, array $formats ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
return [
|
||||
'success' => false,
|
||||
@@ -227,28 +242,29 @@ class Bulk {
|
||||
];
|
||||
}
|
||||
|
||||
delete_transient( 'imagify_stat_without_webp' );
|
||||
delete_transient( 'imagify_stat_without_next_gen' );
|
||||
|
||||
$medias = [];
|
||||
|
||||
foreach ( $contexts as $context ) {
|
||||
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_webp();
|
||||
foreach ( $formats as $format ) {
|
||||
$media = $this->get_bulk_instance( $context )->get_optimized_media_ids_without_format( $format );
|
||||
if ( ! $media['ids'] && $media['errors']['no_backup'] ) {
|
||||
// No backup, no next-gen.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-backup',
|
||||
];
|
||||
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
|
||||
// Error.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( ! $media['ids'] && $media['errors']['no_backup'] ) {
|
||||
// No backup, no WebP.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => 'no-backup',
|
||||
];
|
||||
} elseif ( ! $media['ids'] && $media['errors']['no_file_path'] ) {
|
||||
// Error.
|
||||
return [
|
||||
'success' => false,
|
||||
'message' => __( 'The path to the selected files could not be retrieved.', 'imagify' ),
|
||||
];
|
||||
$medias[ $context ] = $media['ids'];
|
||||
}
|
||||
|
||||
$medias[ $context ] = $media['ids'];
|
||||
}
|
||||
|
||||
if ( empty( $medias ) ) {
|
||||
@@ -266,12 +282,12 @@ class Bulk {
|
||||
foreach ( $media_ids as $media_id ) {
|
||||
try {
|
||||
as_enqueue_async_action(
|
||||
'imagify_convert_webp',
|
||||
'imagify_convert_next_gen',
|
||||
[
|
||||
'id' => $media_id,
|
||||
'context' => $context,
|
||||
],
|
||||
"imagify-{$context}-convert-webp"
|
||||
"imagify-{$context}-convert-nextgen"
|
||||
);
|
||||
} catch ( Exception $exception ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
|
||||
// nothing to do.
|
||||
@@ -279,7 +295,7 @@ class Bulk {
|
||||
}
|
||||
}
|
||||
|
||||
set_transient( 'imagify_missing_webp_total', $total, HOUR_IN_SECONDS );
|
||||
set_transient( 'imagify_missing_next_gen_total', $total, HOUR_IN_SECONDS );
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
@@ -310,13 +326,13 @@ class Bulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the name of the class to use for bulk process.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $class_name The class name.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
* Filter the name of the class to use for bulk process.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $class_name The class name.
|
||||
* @param string $context The context name.
|
||||
*/
|
||||
$class_name = apply_filters( 'imagify_bulk_class_name', $class_name, $context );
|
||||
|
||||
return '\\' . ltrim( $class_name, '\\' );
|
||||
@@ -374,7 +390,7 @@ class Bulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate next-gen images if they are missing.
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
@@ -383,12 +399,12 @@ class Bulk {
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions( int $media_id, string $context ) {
|
||||
public function generate_nextgen_versions( int $media_id, string $context ) {
|
||||
if ( ! $this->can_optimize() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return imagify_get_optimization_process( $media_id, $context )->generate_webp_versions();
|
||||
return imagify_get_optimization_process( $media_id, $context )->generate_nextgen_versions();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -451,10 +467,6 @@ class Bulk {
|
||||
return (int) $level;
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BULK OPTIMIZATION CALLBACKS ============================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Launch the bulk optimization action
|
||||
*
|
||||
@@ -480,14 +492,14 @@ class Bulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch the missing WebP versions generation
|
||||
* Launch the missing Next-gen versions generation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function missing_webp_callback() {
|
||||
public function missing_nextgen_callback() {
|
||||
imagify_check_nonce( 'imagify-bulk-optimize' );
|
||||
|
||||
$contexts = explode( '_', sanitize_key( wp_unslash( $_GET['context'] ) ) );
|
||||
$contexts = $this->get_contexts();
|
||||
|
||||
foreach ( $contexts as $context ) {
|
||||
if ( ! imagify_get_context( $context )->current_user_can( 'bulk-optimize' ) ) {
|
||||
@@ -495,8 +507,9 @@ class Bulk {
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->run_generate_webp( $contexts );
|
||||
$formats = imagify_nextgen_images_formats();
|
||||
|
||||
$data = $this->run_generate_nextgen( $contexts, $formats );
|
||||
if ( false === $data['success'] ) {
|
||||
wp_send_json_error( [ 'message' => $data['message'] ] );
|
||||
}
|
||||
@@ -575,4 +588,87 @@ class Bulk {
|
||||
|
||||
wp_send_json_success( imagify_get_bulk_stats( array_flip( $folder_types ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Options callback to start bulk optimization.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param array $old_value The old option value.
|
||||
* @param array $value The new option value.
|
||||
*
|
||||
* Please note that the convert_to_avif new value is a checkbox,
|
||||
* so it equals 1 when it's set otherwise it's not set.
|
||||
* That's why we need to use empty function when checking its value.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_generate_missing_nextgen( $old_value, $value ) {
|
||||
if ( empty( $old_value['convert_to_avif'] ) === empty( $value['convert_to_avif'] ) ) {
|
||||
// Old value = new value so do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
if ( empty( $value['convert_to_avif'] ) ) {
|
||||
// new value is disabled, do nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
$contexts = $this->get_contexts();
|
||||
$formats = imagify_nextgen_images_formats();
|
||||
|
||||
$this->run_generate_nextgen( $contexts, $formats );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the context for the bulk optimization page.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @return array The array of unique contexts ('wp' or 'custom-folders').
|
||||
*/
|
||||
public function get_contexts() {
|
||||
$contexts = [];
|
||||
$types = [];
|
||||
|
||||
// Library: in each site.
|
||||
if ( ! is_network_admin() ) {
|
||||
$types['library|wp'] = 1;
|
||||
}
|
||||
|
||||
// Custom folders: in network admin only if network activated, in each site otherwise.
|
||||
if ( imagify_can_optimize_custom_folders() && ( imagify_is_active_for_network() && is_network_admin() || ! imagify_is_active_for_network() ) ) {
|
||||
$types['custom-folders|custom-folders'] = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the types to display in the bulk optimization page.
|
||||
*
|
||||
* @since 1.7.1
|
||||
*
|
||||
* @param array $types The folder types displayed on the page. If a folder type is "library", the context should be suffixed after a pipe character. They are passed as array keys.
|
||||
*/
|
||||
$types = apply_filters( 'imagify_bulk_page_types', $types );
|
||||
$types = array_filter( (array) $types );
|
||||
|
||||
if ( isset( $types['library|wp'] ) && ! in_array( 'wp', $contexts, true ) ) {
|
||||
$contexts[] = 'wp';
|
||||
}
|
||||
|
||||
if ( isset( $types['custom-folders|custom-folders'] ) ) {
|
||||
$folders_instance = \Imagify_Folders_DB::get_instance();
|
||||
|
||||
if ( ! $folders_instance->has_items() ) {
|
||||
// New Feature!
|
||||
if ( ! in_array( 'wp', $contexts, true ) ) {
|
||||
$contexts[] = 'wp';
|
||||
}
|
||||
} elseif ( $folders_instance->has_active_folders() && ! in_array( 'custom-folders', $contexts, true ) ) {
|
||||
$contexts[] = 'custom-folders';
|
||||
}
|
||||
}
|
||||
|
||||
return $contexts;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ interface BulkInterface {
|
||||
public function get_unoptimized_media_ids( $optimization_level );
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* Get ids of all optimized media without Next gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -31,16 +32,17 @@ interface BulkInterface {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp();
|
||||
public function get_optimized_media_ids_without_format( $format );
|
||||
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
* Tell if there are optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp();
|
||||
public function has_optimized_media_without_nextgen();
|
||||
|
||||
/**
|
||||
* Get the context data.
|
||||
|
||||
@@ -74,10 +74,11 @@ class CustomFolders extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* Get ids of all optimized media without Next gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -87,7 +88,7 @@ class CustomFolders extends AbstractBulk {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
public function get_optimized_media_ids_without_format( $format ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
@@ -95,9 +96,22 @@ class CustomFolders extends AbstractBulk {
|
||||
$files_table = Imagify_Files_DB::get_instance()->get_table_name();
|
||||
$folders_table = Imagify_Folders_DB::get_instance()->get_table_name();
|
||||
$mime_types = Imagify_DB::get_mime_types( 'image' );
|
||||
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::WEBP_SUFFIX' );
|
||||
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
// Remove single quotes and explode string into array.
|
||||
$mime_types_array = explode( ',', str_replace( "'", '', $mime_types ) );
|
||||
|
||||
// Iterate over array and check if string contains input.
|
||||
foreach ( $mime_types_array as $item ) {
|
||||
if ( strpos( $item, $format ) !== false ) {
|
||||
$mime = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ! isset( $mime ) && empty( $mime ) ) {
|
||||
$mime = 'image/webp';
|
||||
}
|
||||
$mime_types = str_replace( ",'" . $mime . "'", '', $mime_types );
|
||||
$nextgen_suffix = constant( imagify_get_optimization_process_class_name( 'custom-folders' ) . '::' . strtoupper( $format ) . '_SUFFIX' );
|
||||
$files = $wpdb->get_results( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT fi.file_id, fi.path
|
||||
FROM $files_table as fi
|
||||
@@ -108,11 +122,11 @@ class CustomFolders extends AbstractBulk {
|
||||
AND ( fi.status = 'success' OR fi.status = 'already_optimized' )
|
||||
AND ( fi.data NOT LIKE %s OR fi.data IS NULL )
|
||||
ORDER BY fi.file_id DESC",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
'%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
unset( $mime_types, $files_table, $folders_table, $webp_suffix );
|
||||
unset( $mime_types, $files_table, $folders_table, $nextgen_suffix, $mime );
|
||||
|
||||
$data = [
|
||||
'ids' => [],
|
||||
|
||||
@@ -20,10 +20,11 @@ class Noop extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* * Get ids of all optimized media without Next gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -33,7 +34,7 @@ class Noop extends AbstractBulk {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
public function get_optimized_media_ids_without_format( $format ) {
|
||||
return [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
|
||||
@@ -165,10 +165,11 @@ class WP extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* Get ids of all optimized media without Next gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -178,45 +179,61 @@ class WP extends AbstractBulk {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
public function get_optimized_media_ids_without_format( $format ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
|
||||
$mime_types = Imagify_DB::get_mime_types( 'image' );
|
||||
$mime_types = str_replace( ",'image/webp'", '', $mime_types );
|
||||
|
||||
// Remove single quotes and explode string into array.
|
||||
$mime_types_array = explode( ',', str_replace( "'", '', $mime_types ) );
|
||||
|
||||
// Iterate over array and check if string contains input.
|
||||
foreach ( $mime_types_array as $item ) {
|
||||
if ( strpos( $item, $format ) !== false ) {
|
||||
$mime = $item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ! isset( $mime ) && empty( $mime ) ) {
|
||||
$mime = 'image/webp';
|
||||
}
|
||||
$mime_types = str_replace( ",'" . $mime . "'", '', $mime_types );
|
||||
$statuses = Imagify_DB::get_post_statuses();
|
||||
$nodata_join = Imagify_DB::get_required_wp_metadata_join_clause();
|
||||
$nodata_where = Imagify_DB::get_required_wp_metadata_where_clause( [
|
||||
'prepared' => true,
|
||||
] );
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::WEBP_SUFFIX' );
|
||||
$nextgen_suffix = constant( imagify_get_optimization_process_class_name( 'wp' ) . '::' . strtoupper( $format ) . '_SUFFIX' );
|
||||
|
||||
$ids = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT p.ID
|
||||
FROM $wpdb->posts AS p
|
||||
$nodata_join
|
||||
LEFT JOIN $wpdb->postmeta AS mt1
|
||||
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
|
||||
LEFT JOIN $wpdb->postmeta AS mt2
|
||||
SELECT p.ID
|
||||
FROM $wpdb->posts AS p
|
||||
$nodata_join
|
||||
LEFT JOIN $wpdb->postmeta AS mt1
|
||||
ON ( p.ID = mt1.post_id AND mt1.meta_key = '_imagify_status' )
|
||||
LEFT JOIN $wpdb->postmeta AS mt2
|
||||
ON ( p.ID = mt2.post_id AND mt2.meta_key = '_imagify_data' )
|
||||
WHERE
|
||||
p.post_mime_type IN ( $mime_types )
|
||||
AND ( mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' )
|
||||
AND mt2.meta_value NOT LIKE %s
|
||||
AND p.post_type = 'attachment'
|
||||
AND p.post_status IN ( $statuses )
|
||||
$nodata_where
|
||||
ORDER BY p.ID DESC
|
||||
LIMIT 0, %d",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
|
||||
WHERE
|
||||
p.post_mime_type IN ( $mime_types )
|
||||
AND (mt1.meta_key IS NULL OR mt1.meta_value = 'success' OR mt1.meta_value = 'already_optimized' )
|
||||
AND mt2.meta_value NOT LIKE %s
|
||||
AND p.post_type = 'attachment'
|
||||
AND p.post_status IN ( $statuses )
|
||||
$nodata_where
|
||||
ORDER BY p.ID DESC
|
||||
LIMIT 0, %d",
|
||||
'%' . $wpdb->esc_like( $nextgen_suffix . '";a:4:{s:7:"success";b:1;' ) . '%',
|
||||
imagify_get_unoptimized_attachment_limit()
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
unset( $mime_types, $statuses, $webp_suffix );
|
||||
unset( $mime_types, $statuses, $nextgen_suffix, $mime );
|
||||
|
||||
$ids = array_filter( array_map( 'absint', $ids ) );
|
||||
|
||||
$data = [
|
||||
'ids' => [],
|
||||
'errors' => [
|
||||
@@ -243,7 +260,7 @@ class WP extends AbstractBulk {
|
||||
* @param array $metas An array of the data fetched from the database.
|
||||
* @param string $context The context.
|
||||
*/
|
||||
do_action( 'imagify_bulk_generate_webp_before_file_existence_tests', $ids, $metas, 'wp' );
|
||||
do_action( 'imagify_bulk_generate_nextgen_before_file_existence_tests', $ids, $metas, 'wp' );
|
||||
|
||||
foreach ( $ids as $i => $id ) {
|
||||
if ( empty( $metas['filenames'][ $id ] ) ) {
|
||||
|
||||
123
wp/wp-content/plugins/imagify/classes/CDN/CDN.php
Normal file
123
wp/wp-content/plugins/imagify/classes/CDN/CDN.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CDN;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
|
||||
/**
|
||||
* CDN subscriber
|
||||
*/
|
||||
class CDN implements SubscriberInterface {
|
||||
/**
|
||||
* Array of events this subscriber listens to
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_cdn_source_url' => 'get_cdn_source',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN "source".
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param string $option_url An URL to use instead of the one stored in the option. It is used only if no constant/filter.
|
||||
*
|
||||
* @return array {
|
||||
* @type string $source Where does it come from? Possible values are 'constant', 'filter', or 'option'.
|
||||
* @type string $name Who? Can be a constant name, a plugin name, or an empty string.
|
||||
* @type string $url The CDN URL, with a trailing slash. An empty string if no URL is set.
|
||||
* }
|
||||
*/
|
||||
public function get_cdn_source( $option_url = '' ) {
|
||||
if ( defined( 'IMAGIFY_CDN_URL' ) && IMAGIFY_CDN_URL && is_string( IMAGIFY_CDN_URL ) ) {
|
||||
// Use a constant.
|
||||
$source = [
|
||||
'source' => 'constant',
|
||||
'name' => 'IMAGIFY_CDN_URL',
|
||||
'url' => IMAGIFY_CDN_URL,
|
||||
];
|
||||
} else {
|
||||
// Maybe use a filter.
|
||||
$filter_source = [
|
||||
'name' => null,
|
||||
'url' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Provide a custom CDN source.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param array $filter_source {
|
||||
* @type $name string The name of which provides the URL (plugin name, etc).
|
||||
* @type $url string The CDN URL.
|
||||
* }
|
||||
*/
|
||||
$filter_source = apply_filters( 'imagify_cdn_source', $filter_source );
|
||||
|
||||
if ( ! empty( $filter_source['url'] ) ) {
|
||||
$source = [
|
||||
'source' => 'filter',
|
||||
'name' => ! empty( $filter_source['name'] ) ? $filter_source['name'] : '',
|
||||
'url' => $filter_source['url'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// No constant, no filter: use the option.
|
||||
$source = [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => $option_url && is_string( $option_url ) ? $option_url : get_imagify_option( 'cdn_url' ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Nothing set.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$source['url'] = $this->sanitize_cdn_url( $source['url'] );
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Not an URL.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the CDN URL value.
|
||||
*
|
||||
* @since 1.9.3
|
||||
*
|
||||
* @param string $url The URL to sanitize.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_cdn_url( $url ) {
|
||||
$url = sanitize_text_field( $url );
|
||||
|
||||
if ( ! $url || ! preg_match( '@^https?://.+\.[^.]+@i', $url ) ) {
|
||||
// Not an URL.
|
||||
return '';
|
||||
}
|
||||
|
||||
return trailingslashit( $url );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\CDN;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
|
||||
/**
|
||||
* Service provider for CDN compatibility
|
||||
*/
|
||||
class ServiceProvider extends AbstractServiceProvider {
|
||||
/**
|
||||
* Services provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [
|
||||
'cdn',
|
||||
];
|
||||
|
||||
/**
|
||||
* Subscribers provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $subscribers = [
|
||||
'cdn',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers the provided classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->getContainer()->share( 'cdn', CDN::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscribers array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribers() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ namespace Imagify\CLI;
|
||||
use Imagify\Bulk\Bulk;
|
||||
|
||||
/**
|
||||
* Command class for the missing WebP generation
|
||||
* Command class for the missing Nextgen generation
|
||||
*/
|
||||
class GenerateMissingWebpCommand extends AbstractCommand {
|
||||
class GenerateMissingNextgenCommand extends AbstractCommand {
|
||||
/**
|
||||
* Executes the command.
|
||||
*
|
||||
@@ -16,23 +16,23 @@ class GenerateMissingWebpCommand extends AbstractCommand {
|
||||
* @param array $options Optional arguments.
|
||||
*/
|
||||
public function __invoke( $arguments, $options ) {
|
||||
Bulk::get_instance()->run_generate_webp( $arguments );
|
||||
Bulk::get_instance()->run_generate_nextgen( $arguments );
|
||||
|
||||
\WP_CLI::log( 'Imagify missing WebP generation triggered.' );
|
||||
\WP_CLI::log( 'Imagify missing next-gen images generation triggered.' );
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function get_command_name(): string {
|
||||
return 'generate-missing-webp';
|
||||
return 'generate-missing-nextgen';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return 'Run the generation of the missing WebP versions';
|
||||
return 'Run the generation of the missing next-gen images versions';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ class GenerateMissingWebpCommand extends AbstractCommand {
|
||||
[
|
||||
'type' => 'positional',
|
||||
'name' => 'contexts',
|
||||
'description' => 'The context(s) to run the missing WebP generation for. Possible values are wp and custom-folders.',
|
||||
'description' => 'The context(s) to run the missing next-gen images generation for. Possible values are wp and custom-folders.',
|
||||
'optional' => false,
|
||||
'repeating' => true,
|
||||
],
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
use ReflectionFunctionAbstract;
|
||||
|
||||
interface ArgumentResolverInterface extends ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Resolve an array of arguments to their concrete implementations.
|
||||
*
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function resolveArguments(array $arguments) : array;
|
||||
|
||||
/**
|
||||
* Resolves the correct arguments to be passed to a method.
|
||||
*
|
||||
* @param ReflectionFunctionAbstract $method
|
||||
* @param array $args
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []) : array;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Container;
|
||||
use Imagify\Dependencies\League\Container\Exception\{ContainerException, NotFoundException};
|
||||
use Imagify\Dependencies\League\Container\ReflectionContainer;
|
||||
use Imagify\Dependencies\Psr\Container\ContainerInterface;
|
||||
use ReflectionFunctionAbstract;
|
||||
use ReflectionParameter;
|
||||
|
||||
trait ArgumentResolverTrait
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolveArguments(array $arguments) : array
|
||||
{
|
||||
return array_map(function ($argument) {
|
||||
$justStringValue = false;
|
||||
|
||||
if ($argument instanceof RawArgumentInterface) {
|
||||
return $argument->getValue();
|
||||
} elseif ($argument instanceof ClassNameInterface) {
|
||||
$id = $argument->getClassName();
|
||||
} elseif (!is_string($argument)) {
|
||||
return $argument;
|
||||
} else {
|
||||
$justStringValue = true;
|
||||
$id = $argument;
|
||||
}
|
||||
|
||||
$container = null;
|
||||
|
||||
try {
|
||||
$container = $this->getLeagueContainer();
|
||||
} catch (ContainerException $e) {
|
||||
if ($this instanceof ReflectionContainer) {
|
||||
$container = $this;
|
||||
}
|
||||
}
|
||||
|
||||
if ($container !== null) {
|
||||
try {
|
||||
return $container->get($id);
|
||||
} catch (NotFoundException $exception) {
|
||||
if ($argument instanceof ClassNameWithOptionalValue) {
|
||||
return $argument->getOptionalValue();
|
||||
}
|
||||
|
||||
if ($justStringValue) {
|
||||
return $id;
|
||||
}
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
if ($argument instanceof ClassNameWithOptionalValue) {
|
||||
return $argument->getOptionalValue();
|
||||
}
|
||||
|
||||
// Just a string value.
|
||||
return $id;
|
||||
}, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reflectArguments(ReflectionFunctionAbstract $method, array $args = []) : array
|
||||
{
|
||||
$arguments = array_map(function (ReflectionParameter $param) use ($method, $args) {
|
||||
$name = $param->getName();
|
||||
$type = $param->getType();
|
||||
|
||||
if (array_key_exists($name, $args)) {
|
||||
return new RawArgument($args[$name]);
|
||||
}
|
||||
|
||||
if ($type) {
|
||||
if (PHP_VERSION_ID >= 70100) {
|
||||
$typeName = $type->getName();
|
||||
} else {
|
||||
$typeName = (string) $type;
|
||||
}
|
||||
|
||||
$typeName = ltrim($typeName, '?');
|
||||
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
return new ClassNameWithOptionalValue($typeName, $param->getDefaultValue());
|
||||
}
|
||||
|
||||
return new ClassName($typeName);
|
||||
}
|
||||
|
||||
if ($param->isDefaultValueAvailable()) {
|
||||
return new RawArgument($param->getDefaultValue());
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf(
|
||||
'Unable to resolve a value for parameter (%s) in the function/method (%s)',
|
||||
$name,
|
||||
$method->getName()
|
||||
));
|
||||
}, $method->getParameters());
|
||||
|
||||
return $this->resolveArguments($arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
abstract public function getContainer() : ContainerInterface;
|
||||
|
||||
/**
|
||||
* @return Container
|
||||
*/
|
||||
abstract public function getLeagueContainer() : Container;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
class ClassName implements ClassNameInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getClassName() : string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
interface ClassNameInterface
|
||||
{
|
||||
/**
|
||||
* Return the class name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassName() : string;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
class ClassNameWithOptionalValue implements ClassNameInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $className;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $optionalValue;
|
||||
|
||||
/**
|
||||
* @param string $className
|
||||
* @param mixed $optionalValue
|
||||
*/
|
||||
public function __construct(string $className, $optionalValue)
|
||||
{
|
||||
$this->className = $className;
|
||||
$this->optionalValue = $optionalValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getClassName(): string
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
public function getOptionalValue()
|
||||
{
|
||||
return $this->optionalValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
class RawArgument implements RawArgumentInterface
|
||||
{
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Argument;
|
||||
|
||||
interface RawArgumentInterface
|
||||
{
|
||||
/**
|
||||
* Return the value of the raw argument.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue();
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Definition\{DefinitionAggregate, DefinitionInterface, DefinitionAggregateInterface};
|
||||
use Imagify\Dependencies\League\Container\Exception\{NotFoundException, ContainerException};
|
||||
use Imagify\Dependencies\League\Container\Inflector\{InflectorAggregate, InflectorInterface, InflectorAggregateInterface};
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\{
|
||||
ServiceProviderAggregate,
|
||||
ServiceProviderAggregateInterface,
|
||||
ServiceProviderInterface
|
||||
};
|
||||
use Imagify\Dependencies\Psr\Container\ContainerInterface;
|
||||
|
||||
class Container implements ContainerInterface
|
||||
{
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $defaultToShared = false;
|
||||
|
||||
/**
|
||||
* @var DefinitionAggregateInterface
|
||||
*/
|
||||
protected $definitions;
|
||||
|
||||
/**
|
||||
* @var ServiceProviderAggregateInterface
|
||||
*/
|
||||
protected $providers;
|
||||
|
||||
/**
|
||||
* @var InflectorAggregateInterface
|
||||
*/
|
||||
protected $inflectors;
|
||||
|
||||
/**
|
||||
* @var ContainerInterface[]
|
||||
*/
|
||||
protected $delegates = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param DefinitionAggregateInterface|null $definitions
|
||||
* @param ServiceProviderAggregateInterface|null $providers
|
||||
* @param InflectorAggregateInterface|null $inflectors
|
||||
*/
|
||||
public function __construct(
|
||||
DefinitionAggregateInterface $definitions = null,
|
||||
ServiceProviderAggregateInterface $providers = null,
|
||||
InflectorAggregateInterface $inflectors = null
|
||||
) {
|
||||
$this->definitions = $definitions ?? new DefinitionAggregate;
|
||||
$this->providers = $providers ?? new ServiceProviderAggregate;
|
||||
$this->inflectors = $inflectors ?? new InflectorAggregate;
|
||||
|
||||
if ($this->definitions instanceof ContainerAwareInterface) {
|
||||
$this->definitions->setLeagueContainer($this);
|
||||
}
|
||||
|
||||
if ($this->providers instanceof ContainerAwareInterface) {
|
||||
$this->providers->setLeagueContainer($this);
|
||||
}
|
||||
|
||||
if ($this->inflectors instanceof ContainerAwareInterface) {
|
||||
$this->inflectors->setLeagueContainer($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the container.
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $concrete
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function add(string $id, $concrete = null, bool $shared = null) : DefinitionInterface
|
||||
{
|
||||
$concrete = $concrete ?? $id;
|
||||
$shared = $shared ?? $this->defaultToShared;
|
||||
|
||||
return $this->definitions->add($id, $concrete, $shared);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy to add with shared as true.
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $concrete
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function share(string $id, $concrete = null) : DefinitionInterface
|
||||
{
|
||||
return $this->add($id, $concrete, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the container should default to defining shared definitions.
|
||||
*
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function defaultToShared(bool $shared = true) : ContainerInterface
|
||||
{
|
||||
$this->defaultToShared = $shared;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a definition to extend.
|
||||
*
|
||||
* @param string $id [description]
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function extend(string $id) : DefinitionInterface
|
||||
{
|
||||
if ($this->providers->provides($id)) {
|
||||
$this->providers->register($id);
|
||||
}
|
||||
|
||||
if ($this->definitions->has($id)) {
|
||||
return $this->definitions->getDefinition($id);
|
||||
}
|
||||
|
||||
throw new NotFoundException(
|
||||
sprintf('Unable to extend alias (%s) as it is not being managed as a definition', $id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a service provider.
|
||||
*
|
||||
* @param ServiceProviderInterface|string $provider
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addServiceProvider($provider) : self
|
||||
{
|
||||
$this->providers->add($provider);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($id, bool $new = false)
|
||||
{
|
||||
if ($this->definitions->has($id)) {
|
||||
$resolved = $this->definitions->resolve($id, $new);
|
||||
return $this->inflectors->inflect($resolved);
|
||||
}
|
||||
|
||||
if ($this->definitions->hasTag($id)) {
|
||||
$arrayOf = $this->definitions->resolveTagged($id, $new);
|
||||
|
||||
array_walk($arrayOf, function (&$resolved) {
|
||||
$resolved = $this->inflectors->inflect($resolved);
|
||||
});
|
||||
|
||||
return $arrayOf;
|
||||
}
|
||||
|
||||
if ($this->providers->provides($id)) {
|
||||
$this->providers->register($id);
|
||||
|
||||
if (!$this->definitions->has($id) && !$this->definitions->hasTag($id)) {
|
||||
throw new ContainerException(sprintf('Service provider lied about providing (%s) service', $id));
|
||||
}
|
||||
|
||||
return $this->get($id, $new);
|
||||
}
|
||||
|
||||
foreach ($this->delegates as $delegate) {
|
||||
if ($delegate->has($id)) {
|
||||
$resolved = $delegate->get($id);
|
||||
return $this->inflectors->inflect($resolved);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf('Alias (%s) is not being managed by the container or delegates', $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($id)
|
||||
{
|
||||
if ($this->definitions->has($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->definitions->hasTag($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->providers->provides($id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->delegates as $delegate) {
|
||||
if ($delegate->has($id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for manipulation of specific types on resolution.
|
||||
*
|
||||
* @param string $type
|
||||
* @param callable|null $callback
|
||||
*
|
||||
* @return InflectorInterface
|
||||
*/
|
||||
public function inflector(string $type, callable $callback = null) : InflectorInterface
|
||||
{
|
||||
return $this->inflectors->add($type, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate a backup container to be checked for services if it
|
||||
* cannot be resolved via this container.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function delegate(ContainerInterface $container) : self
|
||||
{
|
||||
$this->delegates[] = $container;
|
||||
|
||||
if ($container instanceof ContainerAwareInterface) {
|
||||
$container->setLeagueContainer($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container;
|
||||
|
||||
use Imagify\Dependencies\Psr\Container\ContainerInterface;
|
||||
|
||||
interface ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Set a container
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container) : ContainerAwareInterface;
|
||||
|
||||
/**
|
||||
* Get the container
|
||||
*
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function getContainer() : ContainerInterface;
|
||||
|
||||
/**
|
||||
* Set a container. This will be removed in favour of setContainer receiving Container in next major release.
|
||||
*
|
||||
* @param Container $container
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLeagueContainer(Container $container) : self;
|
||||
|
||||
/**
|
||||
* Get the container. This will be removed in favour of getContainer returning Container in next major release.
|
||||
*
|
||||
* @return Container
|
||||
*/
|
||||
public function getLeagueContainer() : Container;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Exception\ContainerException;
|
||||
use Imagify\Dependencies\Psr\Container\ContainerInterface;
|
||||
|
||||
trait ContainerAwareTrait
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $leagueContainer;
|
||||
|
||||
/**
|
||||
* Set a container.
|
||||
*
|
||||
* @param ContainerInterface $container
|
||||
*
|
||||
* @return ContainerAwareInterface
|
||||
*/
|
||||
public function setContainer(ContainerInterface $container) : ContainerAwareInterface
|
||||
{
|
||||
$this->container = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container.
|
||||
*
|
||||
* @return ContainerInterface
|
||||
*/
|
||||
public function getContainer() : ContainerInterface
|
||||
{
|
||||
if ($this->container instanceof ContainerInterface) {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
throw new ContainerException('No container implementation has been set.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a container.
|
||||
*
|
||||
* @param Container $container
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setLeagueContainer(Container $container) : ContainerAwareInterface
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->leagueContainer = $container;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container.
|
||||
*
|
||||
* @return Container
|
||||
*/
|
||||
public function getLeagueContainer() : Container
|
||||
{
|
||||
if ($this->leagueContainer instanceof Container) {
|
||||
return $this->leagueContainer;
|
||||
}
|
||||
|
||||
throw new ContainerException('No container implementation has been set.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Definition;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Argument\{
|
||||
ArgumentResolverInterface, ArgumentResolverTrait, ClassNameInterface, RawArgumentInterface
|
||||
};
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
||||
class Definition implements ArgumentResolverInterface, DefinitionInterface
|
||||
{
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $alias;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $concrete;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $shared = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $arguments = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $methods = [];
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected $resolved;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $concrete
|
||||
*/
|
||||
public function __construct(string $id, $concrete = null)
|
||||
{
|
||||
$concrete = $concrete ?? $id;
|
||||
|
||||
$this->alias = $id;
|
||||
$this->concrete = $concrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addTag(string $tag) : DefinitionInterface
|
||||
{
|
||||
$this->tags[$tag] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTag(string $tag) : bool
|
||||
{
|
||||
return isset($this->tags[$tag]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setAlias(string $id) : DefinitionInterface
|
||||
{
|
||||
$this->alias = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getAlias() : string
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setShared(bool $shared = true) : DefinitionInterface
|
||||
{
|
||||
$this->shared = $shared;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isShared() : bool
|
||||
{
|
||||
return $this->shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getConcrete()
|
||||
{
|
||||
return $this->concrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setConcrete($concrete) : DefinitionInterface
|
||||
{
|
||||
$this->concrete = $concrete;
|
||||
$this->resolved = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addArgument($arg) : DefinitionInterface
|
||||
{
|
||||
$this->arguments[] = $arg;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addArguments(array $args) : DefinitionInterface
|
||||
{
|
||||
foreach ($args as $arg) {
|
||||
$this->addArgument($arg);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMethodCall(string $method, array $args = []) : DefinitionInterface
|
||||
{
|
||||
$this->methods[] = [
|
||||
'method' => $method,
|
||||
'arguments' => $args
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addMethodCalls(array $methods = []) : DefinitionInterface
|
||||
{
|
||||
foreach ($methods as $method => $args) {
|
||||
$this->addMethodCall($method, $args);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve(bool $new = false)
|
||||
{
|
||||
$concrete = $this->concrete;
|
||||
|
||||
if ($this->isShared() && $this->resolved !== null && $new === false) {
|
||||
return $this->resolved;
|
||||
}
|
||||
|
||||
if (is_callable($concrete)) {
|
||||
$concrete = $this->resolveCallable($concrete);
|
||||
}
|
||||
|
||||
if ($concrete instanceof RawArgumentInterface) {
|
||||
$this->resolved = $concrete->getValue();
|
||||
|
||||
return $concrete->getValue();
|
||||
}
|
||||
|
||||
if ($concrete instanceof ClassNameInterface) {
|
||||
$concrete = $concrete->getClassName();
|
||||
}
|
||||
|
||||
if (is_string($concrete) && class_exists($concrete)) {
|
||||
$concrete = $this->resolveClass($concrete);
|
||||
}
|
||||
|
||||
if (is_object($concrete)) {
|
||||
$concrete = $this->invokeMethods($concrete);
|
||||
}
|
||||
|
||||
if (is_string($concrete) && $this->getContainer()->has($concrete)) {
|
||||
$concrete = $this->getContainer()->get($concrete);
|
||||
}
|
||||
|
||||
$this->resolved = $concrete;
|
||||
|
||||
return $concrete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a callable.
|
||||
*
|
||||
* @param callable $concrete
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resolveCallable(callable $concrete)
|
||||
{
|
||||
$resolved = $this->resolveArguments($this->arguments);
|
||||
|
||||
return call_user_func_array($concrete, $resolved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a class.
|
||||
*
|
||||
* @param string $concrete
|
||||
*
|
||||
* @return object
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
protected function resolveClass(string $concrete)
|
||||
{
|
||||
$resolved = $this->resolveArguments($this->arguments);
|
||||
$reflection = new ReflectionClass($concrete);
|
||||
|
||||
return $reflection->newInstanceArgs($resolved);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke methods on resolved instance.
|
||||
*
|
||||
* @param object $instance
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function invokeMethods($instance)
|
||||
{
|
||||
foreach ($this->methods as $method) {
|
||||
$args = $this->resolveArguments($method['arguments']);
|
||||
|
||||
/** @var callable $callable */
|
||||
$callable = [$instance, $method['method']];
|
||||
call_user_func_array($callable, $args);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Definition;
|
||||
|
||||
use Generator;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
|
||||
use Imagify\Dependencies\League\Container\Exception\NotFoundException;
|
||||
|
||||
class DefinitionAggregate implements DefinitionAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var DefinitionInterface[]
|
||||
*/
|
||||
protected $definitions = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param DefinitionInterface[] $definitions
|
||||
*/
|
||||
public function __construct(array $definitions = [])
|
||||
{
|
||||
$this->definitions = array_filter($definitions, function ($definition) {
|
||||
return ($definition instanceof DefinitionInterface);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(string $id, $definition, bool $shared = false) : DefinitionInterface
|
||||
{
|
||||
if (!$definition instanceof DefinitionInterface) {
|
||||
$definition = new Definition($id, $definition);
|
||||
}
|
||||
|
||||
$this->definitions[] = $definition
|
||||
->setAlias($id)
|
||||
->setShared($shared)
|
||||
;
|
||||
|
||||
return $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has(string $id) : bool
|
||||
{
|
||||
foreach ($this->getIterator() as $definition) {
|
||||
if ($id === $definition->getAlias()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasTag(string $tag) : bool
|
||||
{
|
||||
foreach ($this->getIterator() as $definition) {
|
||||
if ($definition->hasTag($tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getDefinition(string $id) : DefinitionInterface
|
||||
{
|
||||
foreach ($this->getIterator() as $definition) {
|
||||
if ($id === $definition->getAlias()) {
|
||||
return $definition->setLeagueContainer($this->getLeagueContainer());
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundException(sprintf('Alias (%s) is not being handled as a definition.', $id));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve(string $id, bool $new = false)
|
||||
{
|
||||
return $this->getDefinition($id)->resolve($new);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolveTagged(string $tag, bool $new = false) : array
|
||||
{
|
||||
$arrayOf = [];
|
||||
|
||||
foreach ($this->getIterator() as $definition) {
|
||||
if ($definition->hasTag($tag)) {
|
||||
$arrayOf[] = $definition->setLeagueContainer($this->getLeagueContainer())->resolve($new);
|
||||
}
|
||||
}
|
||||
|
||||
return $arrayOf;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator() : Generator
|
||||
{
|
||||
$count = count($this->definitions);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
yield $this->definitions[$i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Definition;
|
||||
|
||||
use IteratorAggregate;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
|
||||
interface DefinitionAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Add a definition to the aggregate.
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $definition
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function add(string $id, $definition, bool $shared = false) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Checks whether alias exists as definition.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(string $id) : bool;
|
||||
|
||||
/**
|
||||
* Checks whether tag exists as definition.
|
||||
*
|
||||
* @param string $tag
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTag(string $tag) : bool;
|
||||
|
||||
/**
|
||||
* Get the definition to be extended.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function getDefinition(string $id) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Resolve and build a concrete value from an id/alias.
|
||||
*
|
||||
* @param string $id
|
||||
* @param boolean $new
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolve(string $id, bool $new = false);
|
||||
|
||||
/**
|
||||
* Resolve and build an array of concrete values from a tag.
|
||||
*
|
||||
* @param string $tag
|
||||
* @param boolean $new
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolveTagged(string $tag, bool $new = false);
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Definition;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
|
||||
interface DefinitionInterface extends ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Add a tag to the definition.
|
||||
*
|
||||
* @param string $tag
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addTag(string $tag) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Does the definition have a tag?
|
||||
*
|
||||
* @param string $tag
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasTag(string $tag) : bool;
|
||||
|
||||
/**
|
||||
* Set the alias of the definition.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function setAlias(string $id) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Get the alias of the definition.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAlias() : string;
|
||||
|
||||
/**
|
||||
* Set whether this is a shared definition.
|
||||
*
|
||||
* @param boolean $shared
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setShared(bool $shared) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Is this a shared definition?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isShared() : bool;
|
||||
|
||||
/**
|
||||
* Get the concrete of the definition.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConcrete();
|
||||
|
||||
/**
|
||||
* Set the concrete of the definition.
|
||||
*
|
||||
* @param mixed $concrete
|
||||
*
|
||||
* @return DefinitionInterface
|
||||
*/
|
||||
public function setConcrete($concrete) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Add an argument to be injected.
|
||||
*
|
||||
* @param mixed $arg
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addArgument($arg) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Add multiple arguments to be injected.
|
||||
*
|
||||
* @param array $args
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addArguments(array $args) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Add a method to be invoked
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addMethodCall(string $method, array $args = []) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Add multiple methods to be invoked
|
||||
*
|
||||
* @param array $methods
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function addMethodCalls(array $methods = []) : DefinitionInterface;
|
||||
|
||||
/**
|
||||
* Handle instantiation and manipulation of value and return.
|
||||
*
|
||||
* @param boolean $new
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function resolve(bool $new = false);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Exception;
|
||||
|
||||
use Imagify\Dependencies\Psr\Container\ContainerExceptionInterface;
|
||||
use RuntimeException;
|
||||
|
||||
class ContainerException extends RuntimeException implements ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Exception;
|
||||
|
||||
use Imagify\Dependencies\Psr\Container\NotFoundExceptionInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class NotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Inflector;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Argument\ArgumentResolverInterface;
|
||||
use Imagify\Dependencies\League\Container\Argument\ArgumentResolverTrait;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
|
||||
|
||||
class Inflector implements ArgumentResolverInterface, InflectorInterface
|
||||
{
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var callable|null
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $methods = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $properties = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param string $type
|
||||
* @param callable|null $callback
|
||||
*/
|
||||
public function __construct(string $type, callable $callback = null)
|
||||
{
|
||||
$this->type = $type;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getType() : string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invokeMethod(string $name, array $args) : InflectorInterface
|
||||
{
|
||||
$this->methods[$name] = $args;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invokeMethods(array $methods) : InflectorInterface
|
||||
{
|
||||
foreach ($methods as $name => $args) {
|
||||
$this->invokeMethod($name, $args);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProperty(string $property, $value) : InflectorInterface
|
||||
{
|
||||
$this->properties[$property] = $this->resolveArguments([$value])[0];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setProperties(array $properties) : InflectorInterface
|
||||
{
|
||||
foreach ($properties as $property => $value) {
|
||||
$this->setProperty($property, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function inflect($object)
|
||||
{
|
||||
$properties = $this->resolveArguments(array_values($this->properties));
|
||||
$properties = array_combine(array_keys($this->properties), $properties);
|
||||
|
||||
// array_combine() can technically return false
|
||||
foreach ($properties ?: [] as $property => $value) {
|
||||
$object->{$property} = $value;
|
||||
}
|
||||
|
||||
foreach ($this->methods as $method => $args) {
|
||||
$args = $this->resolveArguments($args);
|
||||
|
||||
/** @var callable $callable */
|
||||
$callable = [$object, $method];
|
||||
call_user_func_array($callable, $args);
|
||||
}
|
||||
|
||||
if ($this->callback !== null) {
|
||||
call_user_func($this->callback, $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Inflector;
|
||||
|
||||
use Generator;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
|
||||
|
||||
class InflectorAggregate implements InflectorAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var Inflector[]
|
||||
*/
|
||||
protected $inflectors = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add(string $type, callable $callback = null) : Inflector
|
||||
{
|
||||
$inflector = new Inflector($type, $callback);
|
||||
$this->inflectors[] = $inflector;
|
||||
|
||||
return $inflector;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator() : Generator
|
||||
{
|
||||
$count = count($this->inflectors);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
yield $this->inflectors[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function inflect($object)
|
||||
{
|
||||
foreach ($this->getIterator() as $inflector) {
|
||||
$type = $inflector->getType();
|
||||
|
||||
if (! $object instanceof $type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$inflector->setLeagueContainer($this->getLeagueContainer());
|
||||
$inflector->inflect($object);
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Inflector;
|
||||
|
||||
use IteratorAggregate;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
|
||||
interface InflectorAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Add an inflector to the aggregate.
|
||||
*
|
||||
* @param string $type
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return Inflector
|
||||
*/
|
||||
public function add(string $type, callable $callback = null) : Inflector;
|
||||
|
||||
/**
|
||||
* Applies all inflectors to an object.
|
||||
*
|
||||
* @param object $object
|
||||
* @return object
|
||||
*/
|
||||
public function inflect($object);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\Inflector;
|
||||
|
||||
interface InflectorInterface
|
||||
{
|
||||
/**
|
||||
* Get the type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType() : string;
|
||||
|
||||
/**
|
||||
* Defines a method to be invoked on the subject object.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $args
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function invokeMethod(string $name, array $args) : InflectorInterface;
|
||||
|
||||
/**
|
||||
* Defines multiple methods to be invoked on the subject object.
|
||||
*
|
||||
* @param array $methods
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function invokeMethods(array $methods) : InflectorInterface;
|
||||
|
||||
/**
|
||||
* Defines a property to be set on the subject object.
|
||||
*
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setProperty(string $property, $value) : InflectorInterface;
|
||||
|
||||
/**
|
||||
* Defines multiple properties to be set on the subject object.
|
||||
*
|
||||
* @param array $properties
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setProperties(array $properties) : InflectorInterface;
|
||||
|
||||
/**
|
||||
* Apply inflections to an object.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function inflect($object);
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container;
|
||||
|
||||
use Imagify\Dependencies\League\Container\Argument\{ArgumentResolverInterface, ArgumentResolverTrait};
|
||||
use Imagify\Dependencies\League\Container\Exception\NotFoundException;
|
||||
use Imagify\Dependencies\Psr\Container\ContainerInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
|
||||
class ReflectionContainer implements ArgumentResolverInterface, ContainerInterface
|
||||
{
|
||||
use ArgumentResolverTrait;
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $cacheResolutions = false;
|
||||
|
||||
/**
|
||||
* Cache of resolutions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $cache = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function get($id, array $args = [])
|
||||
{
|
||||
if ($this->cacheResolutions === true && array_key_exists($id, $this->cache)) {
|
||||
return $this->cache[$id];
|
||||
}
|
||||
|
||||
if (! $this->has($id)) {
|
||||
throw new NotFoundException(
|
||||
sprintf('Alias (%s) is not an existing class and therefore cannot be resolved', $id)
|
||||
);
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass($id);
|
||||
$construct = $reflector->getConstructor();
|
||||
|
||||
if ($construct && !$construct->isPublic()) {
|
||||
throw new NotFoundException(
|
||||
sprintf('Alias (%s) has a non-public constructor and therefore cannot be instantiated', $id)
|
||||
);
|
||||
}
|
||||
|
||||
$resolution = $construct === null
|
||||
? new $id
|
||||
: $resolution = $reflector->newInstanceArgs($this->reflectArguments($construct, $args))
|
||||
;
|
||||
|
||||
if ($this->cacheResolutions === true) {
|
||||
$this->cache[$id] = $resolution;
|
||||
}
|
||||
|
||||
return $resolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($id)
|
||||
{
|
||||
return class_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a callable via the container.
|
||||
*
|
||||
* @param callable $callable
|
||||
* @param array $args
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function call(callable $callable, array $args = [])
|
||||
{
|
||||
if (is_string($callable) && strpos($callable, '::') !== false) {
|
||||
$callable = explode('::', $callable);
|
||||
}
|
||||
|
||||
if (is_array($callable)) {
|
||||
if (is_string($callable[0])) {
|
||||
$callable[0] = $this->getContainer()->get($callable[0]);
|
||||
}
|
||||
|
||||
$reflection = new ReflectionMethod($callable[0], $callable[1]);
|
||||
|
||||
if ($reflection->isStatic()) {
|
||||
$callable[0] = null;
|
||||
}
|
||||
|
||||
return $reflection->invokeArgs($callable[0], $this->reflectArguments($reflection, $args));
|
||||
}
|
||||
|
||||
if (is_object($callable)) {
|
||||
$reflection = new ReflectionMethod($callable, '__invoke');
|
||||
|
||||
return $reflection->invokeArgs($callable, $this->reflectArguments($reflection, $args));
|
||||
}
|
||||
|
||||
$reflection = new ReflectionFunction(\Closure::fromCallable($callable));
|
||||
|
||||
return $reflection->invokeArgs($this->reflectArguments($reflection, $args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the container should default to caching resolutions and returning
|
||||
* the cache on following calls.
|
||||
*
|
||||
* @param boolean $option
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function cacheResolutions(bool $option = true) : ContainerInterface
|
||||
{
|
||||
$this->cacheResolutions = $option;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\ServiceProvider;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareTrait;
|
||||
|
||||
abstract class AbstractServiceProvider implements ServiceProviderInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $identifier;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function provides(string $alias) : bool
|
||||
{
|
||||
return in_array($alias, $this->provides, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setIdentifier(string $id) : ServiceProviderInterface
|
||||
{
|
||||
$this->identifier = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIdentifier() : string
|
||||
{
|
||||
return $this->identifier ?? get_class($this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\ServiceProvider;
|
||||
|
||||
interface BootableServiceProviderInterface extends ServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* Method will be invoked on registration of a service provider implementing
|
||||
* this interface. Provides ability for eager loading of Service Providers.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot();
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\ServiceProvider;
|
||||
|
||||
use Generator;
|
||||
use Imagify\Dependencies\League\Container\{ContainerAwareInterface, ContainerAwareTrait};
|
||||
use Imagify\Dependencies\League\Container\Exception\ContainerException;
|
||||
|
||||
class ServiceProviderAggregate implements ServiceProviderAggregateInterface
|
||||
{
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* @var ServiceProviderInterface[]
|
||||
*/
|
||||
protected $providers = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $registered = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($provider) : ServiceProviderAggregateInterface
|
||||
{
|
||||
if (is_string($provider) && $this->getContainer()->has($provider)) {
|
||||
$provider = $this->getContainer()->get($provider);
|
||||
} elseif (is_string($provider) && class_exists($provider)) {
|
||||
$provider = new $provider;
|
||||
}
|
||||
|
||||
if (in_array($provider, $this->providers, true)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($provider instanceof ContainerAwareInterface) {
|
||||
$provider->setLeagueContainer($this->getLeagueContainer());
|
||||
}
|
||||
|
||||
if ($provider instanceof BootableServiceProviderInterface) {
|
||||
$provider->boot();
|
||||
}
|
||||
|
||||
if ($provider instanceof ServiceProviderInterface) {
|
||||
$this->providers[] = $provider;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new ContainerException(
|
||||
'A service provider must be a fully qualified class name or instance ' .
|
||||
'of (\Imagify\Dependencies\League\Container\ServiceProvider\ServiceProviderInterface)'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function provides(string $service) : bool
|
||||
{
|
||||
foreach ($this->getIterator() as $provider) {
|
||||
if ($provider->provides($service)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator() : Generator
|
||||
{
|
||||
$count = count($this->providers);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
yield $this->providers[$i];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function register(string $service)
|
||||
{
|
||||
if (false === $this->provides($service)) {
|
||||
throw new ContainerException(
|
||||
sprintf('(%s) is not provided by a service provider', $service)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->getIterator() as $provider) {
|
||||
if (in_array($provider->getIdentifier(), $this->registered, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($provider->provides($service)) {
|
||||
$this->registered[] = $provider->getIdentifier();
|
||||
$provider->register();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\ServiceProvider;
|
||||
|
||||
use IteratorAggregate;
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
|
||||
interface ServiceProviderAggregateInterface extends ContainerAwareInterface, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Add a service provider to the aggregate.
|
||||
*
|
||||
* @param string|ServiceProviderInterface $provider
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function add($provider) : ServiceProviderAggregateInterface;
|
||||
|
||||
/**
|
||||
* Determines whether a service is provided by the aggregate.
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function provides(string $service) : bool;
|
||||
|
||||
/**
|
||||
* Invokes the register method of a provider that provides a specific service.
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(string $service);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\League\Container\ServiceProvider;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ContainerAwareInterface;
|
||||
|
||||
interface ServiceProviderInterface extends ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* Returns a boolean if checking whether this provider provides a specific
|
||||
* service or returns an array of provided services if no argument passed.
|
||||
*
|
||||
* @param string $service
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function provides(string $service) : bool;
|
||||
|
||||
/**
|
||||
* Use the register method to register items with the container via the
|
||||
* protected $this->leagueContainer property or the `getLeagueContainer` method
|
||||
* from the ContainerAwareTrait.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register();
|
||||
|
||||
/**
|
||||
* Set a custom id for the service provider. This enables
|
||||
* registering the same service provider multiple times.
|
||||
*
|
||||
* @param string $id
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function setIdentifier(string $id) : ServiceProviderInterface;
|
||||
|
||||
/**
|
||||
* The id of the service provider uniquely identifies it, so
|
||||
* that we can quickly determine if it has already been registered.
|
||||
* Defaults to get_class($provider).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getIdentifier() : string;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Dependencies\Psr\Container;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Base interface representing a generic exception in a container.
|
||||
*/
|
||||
interface ContainerExceptionInterface extends Throwable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Dependencies\Psr\Container;
|
||||
|
||||
/**
|
||||
* Describes the interface of a container that exposes methods to read its entries.
|
||||
*/
|
||||
interface ContainerInterface
|
||||
{
|
||||
/**
|
||||
* Finds an entry of the container by its identifier and returns it.
|
||||
*
|
||||
* @param string $id Identifier of the entry to look for.
|
||||
*
|
||||
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
|
||||
* @throws ContainerExceptionInterface Error while retrieving the entry.
|
||||
*
|
||||
* @return mixed Entry.
|
||||
*/
|
||||
public function get(string $id);
|
||||
|
||||
/**
|
||||
* Returns true if the container can return an entry for the given identifier.
|
||||
* Returns false otherwise.
|
||||
*
|
||||
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
|
||||
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
|
||||
*
|
||||
* @param string $id Identifier of the entry to look for.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $id);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Imagify\Dependencies\Psr\Container;
|
||||
|
||||
/**
|
||||
* No entry was found in the container.
|
||||
*/
|
||||
interface NotFoundExceptionInterface extends ContainerExceptionInterface
|
||||
{
|
||||
}
|
||||
@@ -174,7 +174,7 @@ class Actions {
|
||||
}
|
||||
|
||||
$remaining = 0;
|
||||
$total = get_transient( 'imagify_missing_webp_total' );
|
||||
$total = get_transient( 'imagify_missing_next_gen_total' );
|
||||
|
||||
if ( false === $total ) {
|
||||
return $response;
|
||||
@@ -182,8 +182,14 @@ class Actions {
|
||||
|
||||
$bulk = Bulk::get_instance();
|
||||
|
||||
$format = 'webp';
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$format = 'avif';
|
||||
}
|
||||
|
||||
foreach ( $data[ $imagifybeat_id ] as $context ) {
|
||||
$media = $bulk->get_bulk_instance( $context )->get_optimized_media_ids_without_webp();
|
||||
$media = $bulk->get_bulk_instance( $context )->get_optimized_media_ids_without_format( $format );
|
||||
$remaining += count( $media['ids'] );
|
||||
}
|
||||
|
||||
|
||||
@@ -187,8 +187,8 @@ class MediaOptimization extends \Imagify_Abstract_Background_Process {
|
||||
$item['error'] = $data;
|
||||
|
||||
} elseif ( 'already_optimized' === $data['status'] ) {
|
||||
// Status is "already_optimized", try to create WebP versions only.
|
||||
$item['sizes'] = array_filter( $item['sizes'], [ $this->optimization_process, 'is_size_webp' ] );
|
||||
// Status is "already_optimized", try to create next-gen versions only.
|
||||
$item['sizes'] = array_filter( $item['sizes'], [ $this->optimization_process, 'is_size_next_gen' ] );
|
||||
|
||||
} elseif ( 'success' !== $data['status'] ) {
|
||||
// Don't go further if the full size has not the "success" status.
|
||||
|
||||
@@ -113,7 +113,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'not_exists',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to exist.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
@@ -124,7 +124,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'not_a_file',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'This does not seem to be a file: %s.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
@@ -135,7 +135,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
@@ -148,7 +148,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'folder_not_writable',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The folder %s does not seem to be writable.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $parent_folder ) ) . '</code>'
|
||||
)
|
||||
@@ -197,7 +197,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'not_an_image',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
@@ -321,7 +321,7 @@ class File {
|
||||
return new \WP_Error(
|
||||
'not_an_image',
|
||||
sprintf(
|
||||
/* translators: %s is a file path. */
|
||||
/* translators: %s is a file path. */
|
||||
__( 'The file %s does not seem to be an image, and cannot be resized.', 'imagify' ),
|
||||
'<code>' . esc_html( $this->filesystem->make_path_relative( $this->path ) ) . '</code>'
|
||||
)
|
||||
@@ -423,6 +423,22 @@ class File {
|
||||
) );
|
||||
}
|
||||
|
||||
// Check if a '-scaled' version of the image exists.
|
||||
$scaled_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_source );
|
||||
if ( $this->filesystem->exists( $scaled_path ) ) {
|
||||
// Create a backup path for the scaled image.
|
||||
$scaled_backup_path = preg_replace( '/(\.)([^\.]+)$/', '-scaled.$2', $backup_path );
|
||||
// Copy the '-scaled' version to the backup.
|
||||
$this->filesystem->copy( $scaled_path, $scaled_backup_path, $overwrite, FS_CHMOD_FILE );
|
||||
|
||||
if ( ! $this->filesystem->exists( $scaled_backup_path ) ) {
|
||||
return new \WP_Error( 'backup_doesnt_exist', __( 'The file could not be saved.', 'imagify' ), array(
|
||||
'file_path' => $this->filesystem->make_path_relative( $scaled_path ),
|
||||
'backup_path' => $this->filesystem->make_path_relative( $scaled_backup_path ),
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -438,7 +454,7 @@ class File {
|
||||
* @type bool $backup False to prevent backup. True to follow the user's setting. A backup can't be forced.
|
||||
* @type string $backup_path If a backup must be done, this is the path to use. Default is the backup path used for the WP Media Library.
|
||||
* @type int $optimization_level The optimization level (2=ultra, 1=aggressive, 0=normal).
|
||||
* @type string $convert Set to 'webp' to convert the image to WebP.
|
||||
* @type string $convert Set to 'webp' to convert the image to WebP, 'avif' to convert image to AVIF.
|
||||
* @type string $context The context.
|
||||
* @type int $original_size The file size, sent to the API.
|
||||
* }
|
||||
@@ -474,7 +490,7 @@ class File {
|
||||
*
|
||||
* @param string $path Absolute path to the media file.
|
||||
* @param array $args Arguments passed to the method.
|
||||
*/
|
||||
*/
|
||||
do_action( 'imagify_before_optimize_file', $this->path, $args );
|
||||
|
||||
/**
|
||||
@@ -485,7 +501,7 @@ class File {
|
||||
*
|
||||
* @param string $path Absolute path to the image file.
|
||||
* @param bool $backup True if a backup will be make.
|
||||
*/
|
||||
*/
|
||||
do_action_deprecated( 'before_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
|
||||
|
||||
if ( $args['backup'] ) {
|
||||
@@ -509,6 +525,7 @@ class File {
|
||||
|
||||
if ( $args['convert'] ) {
|
||||
$data['convert'] = $args['convert'];
|
||||
$format = $args['convert'];
|
||||
}
|
||||
|
||||
$response = upload_imagify_image( [
|
||||
@@ -534,8 +551,12 @@ class File {
|
||||
$args['convert'] = '';
|
||||
}
|
||||
|
||||
if ( 'webp' === $args['convert'] ) {
|
||||
$destination_path = $this->get_path_to_webp();
|
||||
$formats = [
|
||||
'webp',
|
||||
'avif',
|
||||
];
|
||||
if ( in_array( $args['convert'], $formats, true ) ) {
|
||||
$destination_path = $this->get_path_to_nextgen( $args['convert'] );
|
||||
$this->path = $destination_path;
|
||||
$this->file_type = null;
|
||||
$this->editor = null;
|
||||
@@ -557,7 +578,7 @@ class File {
|
||||
*
|
||||
* @param string $path Absolute path to the image file.
|
||||
* @param bool $backup True if a backup has been made.
|
||||
*/
|
||||
*/
|
||||
do_action_deprecated( 'after_do_imagify', [ $this->path, $args['backup'] ], '1.9', 'imagify_before_optimize_file' );
|
||||
|
||||
/**
|
||||
@@ -568,7 +589,7 @@ class File {
|
||||
*
|
||||
* @param string $path Absolute path to the media file.
|
||||
* @param array $args Arguments passed to the method.
|
||||
*/
|
||||
*/
|
||||
do_action( 'imagify_after_optimize_file', $this->path, $args );
|
||||
|
||||
return $response;
|
||||
@@ -603,7 +624,7 @@ class File {
|
||||
$this->editor = new \WP_Error(
|
||||
'image_editor',
|
||||
sprintf(
|
||||
/* translators: %1$s is an error message, %2$s is a "More info?" link. */
|
||||
/* translators: %1$s is an error message, %2$s is a "More info?" link. */
|
||||
__( 'No php extensions are available to edit images on the server. ImageMagick or GD is required. The internal error is: %1$s. %2$s', 'imagify' ),
|
||||
$this->editor->get_error_message(),
|
||||
'<a href="' . esc_url( imagify_get_external_url( 'documentation-imagick-gd' ) ) . '" target="_blank">' . __( 'More info?', 'imagify' ) . '</a>'
|
||||
@@ -765,6 +786,26 @@ class File {
|
||||
return imagify_path_to_webp( $this->path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the file extension by its next-gen format extension.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format the format we are targeting.
|
||||
* @return string|bool The file path on success. False if not an image or on failure.
|
||||
*/
|
||||
public function get_path_to_nextgen( string $format ) {
|
||||
if ( ! $this->is_image() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $this->is_webp() || $this->is_avif() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return imagify_path_to_nextgen( $this->path, $format );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is a WebP image.
|
||||
* Rejects "path/to/.webp" files.
|
||||
@@ -778,6 +819,18 @@ class File {
|
||||
return preg_match( '@(?!^|/|\\\)\.webp$@i', $this->path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if the file is an AVIF image.
|
||||
* Rejects "path/to/.avif" files.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_avif() {
|
||||
return preg_match( '@(?!^|/|\\\)\.avif$@i', $this->path );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mime type + file extension.
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,31 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use WP_Error;
|
||||
|
||||
/**
|
||||
* Fallback class to optimize medias.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Noop implements ProcessInterface {
|
||||
|
||||
/**
|
||||
* The suffix used in the thumbnail size name.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const WEBP_SUFFIX = '@imagify-webp';
|
||||
|
||||
/**
|
||||
* The suffix used in the thumbnail size name.
|
||||
*
|
||||
* @var string
|
||||
* @since 2.2
|
||||
*/
|
||||
const AVIF_SUFFIX = '@imagify-avif';
|
||||
|
||||
/**
|
||||
* The suffix used in file name to create a temporary copy of the full size.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TMP_SUFFIX = '@imagify-tmp';
|
||||
|
||||
@@ -33,9 +37,8 @@ class Noop implements ProcessInterface {
|
||||
* Used for the name of the transient telling if a media is locked.
|
||||
* %1$s is the context, %2$s is the media ID.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const LOCK_NAME = 'imagify_%1$s_%2$s_process_locked';
|
||||
|
||||
@@ -43,11 +46,10 @@ class Noop implements ProcessInterface {
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id ) {
|
||||
@@ -57,9 +59,7 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Get the data instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return DataInterface|false
|
||||
*/
|
||||
@@ -70,9 +70,7 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
@@ -83,9 +81,7 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Get the File instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
@@ -96,9 +92,7 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -109,106 +103,87 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Optimize a media files by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize( $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-optimize a media files with a different level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A WP_Error instance on failure.
|
||||
*/
|
||||
public function reoptimize( $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize several file sizes by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_sizes( $sizes, $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize one file with Imagify directly.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array|WP_Error The optimization data. A \WP_Error instance on failure.
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return array|WP_Error The optimization data. A WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_size( $size, $optimization_level = null ) {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the media files from the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
* @return bool|WP_Error True on success. A WP_Error instance on failure.
|
||||
*/
|
||||
public function restore() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* No sizes are returned if the file is not optimized, has no backup, or is not an image.
|
||||
* The 'full' size os never returned.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* A WP_Error object on failure.
|
||||
@@ -229,45 +204,30 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @return bool|WP_Error True if successfully launched. A WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Delete the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function delete_backup() {}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** RESIZE FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe resize an image.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
* @return array|WP_Error A \WP_Error instance on failure, an array on success as follow: {
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
*
|
||||
* @return array|WP_Error A WP_Error instance on failure, an array on success as follow: {
|
||||
* @type bool $resized True when the image has been resized.
|
||||
* @type bool $backuped True when the image has been backuped.
|
||||
* @type int $file_size The file size in bytes.
|
||||
@@ -281,58 +241,47 @@ class Noop implements ProcessInterface {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** WEBP ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate Nextgen images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @return bool|WP_Error True if successfully launched. A WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions() {
|
||||
return new \WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
public function generate_nextgen_versions() {
|
||||
return new WP_Error( 'invalid_media', __( 'This media is not valid.', 'imagify' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the WebP images.
|
||||
* Delete the next gen format images.
|
||||
* This doesn't delete the related optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 2.2
|
||||
*
|
||||
* @param bool $keep_full Set to true to keep the full size.
|
||||
* @return bool|WP_Error True on success. A WP_Error object on failure.
|
||||
*/
|
||||
public function delete_webp_files() {}
|
||||
|
||||
/**
|
||||
* Tell if a thumbnail size is an "Imagify WebP" size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $size_name The size name.
|
||||
* @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
|
||||
*/
|
||||
public function is_size_webp( $size_name ) {
|
||||
public function delete_nextgen_files( $keep_full = false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PROCESS STATUS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Tell if a thumbnail size is an "Imagify Next-Gen" size.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $size_name The size name.
|
||||
*
|
||||
* @return string|bool The unsuffixed name of the size if Next-Gen. False if not a Next-Gen.
|
||||
*/
|
||||
public function is_size_next_gen( $size_name ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a process is running for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -343,34 +292,24 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Set the running status to "running" for a period of time.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function lock() {}
|
||||
|
||||
/**
|
||||
* Delete the running status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function unlock() {}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** DATA ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a size already has optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The size name.
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @return bool
|
||||
*/
|
||||
public function size_has_optimization_data( $size ) {
|
||||
@@ -380,13 +319,12 @@ class Noop implements ProcessInterface {
|
||||
/**
|
||||
* Update the optimization data for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array {
|
||||
* The optimization data.
|
||||
*
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<?php
|
||||
namespace Imagify\Optimization\Process;
|
||||
declare(strict_types=1);
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
namespace Imagify\Optimization\Process;
|
||||
|
||||
/**
|
||||
* Interface to use to optimize medias.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
interface ProcessInterface {
|
||||
|
||||
@@ -15,11 +14,10 @@ interface ProcessInterface {
|
||||
* Tell if the given entry can be accepted in the constructor.
|
||||
* For example it can include `is_numeric( $id )` if the constructor accepts integers.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
*
|
||||
* @param mixed $id Whatever.
|
||||
* @return bool
|
||||
*/
|
||||
public static function constructor_accepts( $id );
|
||||
@@ -27,9 +25,7 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Get the data instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return DataInterface|false
|
||||
*/
|
||||
@@ -38,9 +34,7 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Get the media instance.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return MediaInterface|false
|
||||
*/
|
||||
@@ -49,9 +43,7 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Get the File instance of the original file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
@@ -60,9 +52,7 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Get the File instance of the full size file.
|
||||
*
|
||||
* @since 1.9.8
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9.8
|
||||
*
|
||||
* @return File|false
|
||||
*/
|
||||
@@ -71,9 +61,7 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Tell if the current media is valid.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -82,94 +70,75 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Tell if the current user is allowed to operate Imagify in this context.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
*
|
||||
* @param string $describer Capacity describer. See \Imagify\Context\ContextInterface->get_capacity() for possible values. Can also be a "real" user capacity.
|
||||
* @return bool
|
||||
*/
|
||||
public function current_user_can( $describer );
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** OPTIMIZATION ============================================================================ */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Optimize a media files by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize( $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Re-optimize a media files with a different level.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function reoptimize( $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Optimize several file sizes by pushing tasks into the queue.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param array $sizes An array of media sizes (strings). Use "full" for the size of the main file.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_sizes( $sizes, $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Optimize one file with Imagify directly.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array|WP_Error The optimization data. A \WP_Error instance on failure.
|
||||
* @param string $size The media size.
|
||||
* @param int $optimization_level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @return array|WP_Error The optimization data. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_size( $size, $optimization_level = null );
|
||||
|
||||
/**
|
||||
* Restore the media files from the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function restore();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** MISSING THUMBNAILS ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the sizes for this media that have not get through optimization.
|
||||
* No sizes are returned if the file is not optimized, has no backup, or is not an image.
|
||||
* The 'full' size os never returned.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array|WP_Error {
|
||||
* A WP_Error object on failure.
|
||||
@@ -188,42 +157,27 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Optimize missing thumbnail sizes.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function optimize_missing_thumbnails();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** BACKUP FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Delete the backup file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function delete_backup();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** RESIZE FILE ============================================================================= */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Maybe resize an image.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @param File $file A File instance.
|
||||
* @return array|WP_Error A \WP_Error instance on failure, an array on success as follow: {
|
||||
* @type bool $resized True when the image has been resized.
|
||||
* @type bool $backuped True when the image has been backuped.
|
||||
@@ -232,77 +186,58 @@ interface ProcessInterface {
|
||||
*/
|
||||
public function maybe_resize( $size, $file );
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** WEBP ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Generate WebP images if they are missing.
|
||||
* Generate next-gen images if they are missing.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool|WP_Error True if successfully launched. A \WP_Error instance on failure.
|
||||
*/
|
||||
public function generate_webp_versions();
|
||||
public function generate_nextgen_versions();
|
||||
|
||||
/**
|
||||
* Delete the WebP images.
|
||||
* Delete the next gen format images.
|
||||
* This doesn't delete the related optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.6 Return WP_Error or true.
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 2.2
|
||||
*
|
||||
* @param bool $keep_full Set to true to keep the full size.
|
||||
* @return bool|\WP_Error True on success. A \WP_Error object on failure.
|
||||
* @param bool $keep_full Set to true to keep the full size.
|
||||
*
|
||||
* @return bool|WP_Error True on success. A \WP_Error object on failure.
|
||||
*/
|
||||
public function delete_webp_files( $keep_full = false );
|
||||
public function delete_nextgen_files( $keep_full = false );
|
||||
|
||||
/**
|
||||
* Tell if a thumbnail size is an "Imagify WebP" size.
|
||||
* Tell if a thumbnail size is an "Imagify Next-Gen" size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $size_name The size name.
|
||||
* @return string|bool The unsuffixed name of the size if WebP. False if not WebP.
|
||||
* @param string $size_name The size name.
|
||||
*
|
||||
* @return string|bool The unsuffixed name of the size if next-gen. False if not next-gen.
|
||||
*/
|
||||
public function is_size_webp( $size_name );
|
||||
public function is_size_next_gen( $size_name );
|
||||
|
||||
/**
|
||||
* Tell if the media has all WebP versions.
|
||||
* Tell if the media has all next-gen versions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function is_full_webp();
|
||||
public function is_full_next_gen();
|
||||
|
||||
/**
|
||||
* Tell if the media has WebP versions.
|
||||
* Tell if the media has a next-gen format.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 2.2
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_webp();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** PROCESS STATUS ========================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
public function has_next_gen();
|
||||
|
||||
/**
|
||||
* Tell if a process is running for this media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
@@ -311,34 +246,24 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Set the running status to "running" for a period of time.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function lock();
|
||||
|
||||
/**
|
||||
* Delete the running status.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
public function unlock();
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** DATA ==================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Tell if a size already has optimization data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $size The size name.
|
||||
*
|
||||
* @param string $size The size name.
|
||||
* @return bool
|
||||
*/
|
||||
public function size_has_optimization_data( $size );
|
||||
@@ -346,13 +271,12 @@ interface ProcessInterface {
|
||||
/**
|
||||
* Update the optimization data for a size.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
*
|
||||
* @param object $response The API response.
|
||||
* @param string $size The size name.
|
||||
* @param int $level The optimization level (0=normal, 1=aggressive, 2=ultra).
|
||||
* @return array {
|
||||
* The optimization data.
|
||||
*
|
||||
|
||||
@@ -1,57 +1,50 @@
|
||||
<?php
|
||||
namespace Imagify\Webp\Picture;
|
||||
declare(strict_types=1);
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
namespace Imagify\Picture;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify_Filesystem;
|
||||
|
||||
/**
|
||||
* Display WebP images on the site with <picture> tags.
|
||||
* Display Next-gen images on the site with <picture> tags.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
*/
|
||||
class Display {
|
||||
use \Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
class Display implements SubscriberInterface {
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_VALUE = 'picture';
|
||||
|
||||
/**
|
||||
* Filesystem object.
|
||||
*
|
||||
* @var \Imagify_Filesystem
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @var Imagify_Filesystem
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @param Imagify_Filesystem $filesystem Filesystem instance.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->filesystem = \Imagify_Filesystem::get_instance();
|
||||
public function __construct( Imagify_Filesystem $filesystem ) {
|
||||
$this->filesystem = $filesystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init.
|
||||
* Array of events this subscriber listens to
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @return array
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'template_redirect', [ $this, 'start_content_process' ], -1000 );
|
||||
add_filter( 'imagify_process_webp_content', [ $this, 'process_content' ] );
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'template_redirect' => [ 'start_content_process', -1000 ],
|
||||
'imagify_process_webp_content' => 'process_content',
|
||||
];
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
@@ -61,28 +54,29 @@ class Display {
|
||||
/**
|
||||
* Start buffering the page content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start_content_process() {
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$allow = apply_filters_deprecated( 'imagify_allow_picture_tags_for_webp', [ true ], '2.2', 'imagify_allow_picture_tags_for_nextgen' );
|
||||
|
||||
/**
|
||||
* Prevent the replacement of <img> tags into <picture> tags.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param bool $allow True to allow the use of <picture> tags (default). False to prevent their use.
|
||||
*/
|
||||
$allow = apply_filters( 'imagify_allow_picture_tags_for_webp', true );
|
||||
$allow = apply_filters( 'imagify_allow_picture_tags_for_nextgen', true );
|
||||
|
||||
if ( ! $allow ) {
|
||||
return;
|
||||
@@ -94,11 +88,10 @@ class Display {
|
||||
/**
|
||||
* Maybe process the page content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
*
|
||||
* @param string $buffer The buffer content.
|
||||
* @return string
|
||||
*/
|
||||
public function maybe_process_buffer( $buffer ) {
|
||||
@@ -116,8 +109,7 @@ class Display {
|
||||
/**
|
||||
* Filter the page content after Imagify.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $buffer The page content.
|
||||
*/
|
||||
@@ -129,11 +121,10 @@ class Display {
|
||||
/**
|
||||
* Process the content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $content The content.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return string
|
||||
*/
|
||||
public function process_content( $content ) {
|
||||
@@ -181,13 +172,12 @@ class Display {
|
||||
/**
|
||||
* Build the <picture> tag to insert.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <picture> tag.
|
||||
* @param array $image An array of data.
|
||||
*
|
||||
* @return string A <picture> tag.
|
||||
*/
|
||||
protected function build_picture_tag( $image ) {
|
||||
$to_remove = [
|
||||
@@ -210,8 +200,7 @@ class Display {
|
||||
/**
|
||||
* Filter the attributes to be added to the <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <picture> tag.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
@@ -233,8 +222,7 @@ class Display {
|
||||
/**
|
||||
* Allow to add more <source> tags to the <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $more_source_tags Additional <source> tags.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
@@ -250,33 +238,74 @@ class Display {
|
||||
/**
|
||||
* Build the <source> tag to insert in the <picture>.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <source> tag.
|
||||
* @param array $image An array of data.
|
||||
*
|
||||
* @return string A <source> tag.
|
||||
*/
|
||||
protected function build_source_tag( $image ) {
|
||||
$source = '';
|
||||
|
||||
foreach ( [ 'avif', 'webp' ] as $image_type ) {
|
||||
$attributes = $this->build_source_attributes( $image, $image_type );
|
||||
|
||||
if ( empty( $attributes ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$source .= '<source' . $this->build_attributes( $attributes ) . "/>\n";
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the attribute for the source tag.
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @param string $image_type Type of image.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function build_source_attributes( array $image, string $image_type ): array {
|
||||
$mime_type = '';
|
||||
$url = '';
|
||||
|
||||
switch ( $image_type ) {
|
||||
case 'webp':
|
||||
$mime_type = 'image/webp';
|
||||
$url = 'webp_url';
|
||||
break;
|
||||
case 'avif':
|
||||
$mime_type = 'image/avif';
|
||||
$url = 'avif_url';
|
||||
break;
|
||||
}
|
||||
|
||||
$srcset_source = ! empty( $image['srcset_attribute'] ) ? $image['srcset_attribute'] : $image['src_attribute'] . 'set';
|
||||
$attributes = [
|
||||
'type' => 'image/webp',
|
||||
'type' => $mime_type,
|
||||
$srcset_source => [],
|
||||
];
|
||||
|
||||
if ( ! empty( $image['srcset'] ) ) {
|
||||
foreach ( $image['srcset'] as $srcset ) {
|
||||
if ( empty( $srcset['webp_url'] ) ) {
|
||||
if ( empty( $srcset[ $url ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[ $srcset_source ][] = $srcset['webp_url'] . ' ' . $srcset['descriptor'];
|
||||
$attributes[ $srcset_source ][] = $srcset[ $url ] . ' ' . $srcset['descriptor'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $attributes[ $srcset_source ] ) && empty( $image['src'][ $url ] ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ( empty( $attributes[ $srcset_source ] ) ) {
|
||||
$attributes[ $srcset_source ][] = $image['src']['webp_url'];
|
||||
$attributes[ $srcset_source ][] = $image['src'][ $url ];
|
||||
}
|
||||
|
||||
$attributes[ $srcset_source ] = implode( ', ', $attributes[ $srcset_source ] );
|
||||
@@ -301,27 +330,25 @@ class Display {
|
||||
/**
|
||||
* Filter the attributes to be added to the <source> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <source> tag.
|
||||
* @param array $data Data built from the original <img> tag. See $this->process_image().
|
||||
*/
|
||||
$attributes = apply_filters( 'imagify_picture_source_attributes', $attributes, $image );
|
||||
|
||||
return '<source' . $this->build_attributes( $attributes ) . "/>\n";
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the <img> tag to insert in the <picture>.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
*
|
||||
* @param array $image An array of data.
|
||||
* @return string A <img> tag.
|
||||
* @param array $image An array of data.
|
||||
*
|
||||
* @return string A <img> tag.
|
||||
*/
|
||||
protected function build_img_tag( $image ) {
|
||||
/**
|
||||
@@ -349,8 +376,7 @@ class Display {
|
||||
/**
|
||||
* Filter the attributes to be added to the <img> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $attributes A list of attributes to be added to the <img> tag.
|
||||
* @param array $data Data built from the originale <img> tag. See $this->process_image().
|
||||
@@ -363,12 +389,11 @@ class Display {
|
||||
/**
|
||||
* Create HTML attributes from an array.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $attributes A list of attribute pairs.
|
||||
* @return string HTML attributes.
|
||||
* @param array $attributes A list of attribute pairs.
|
||||
*
|
||||
* @return string HTML attributes.
|
||||
*/
|
||||
protected function build_attributes( $attributes ) {
|
||||
if ( ! $attributes || ! is_array( $attributes ) ) {
|
||||
@@ -391,11 +416,10 @@ class Display {
|
||||
/**
|
||||
* Get a list of images in a content.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $content The content.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return array
|
||||
*/
|
||||
protected function get_images( $content ) {
|
||||
@@ -412,9 +436,8 @@ class Display {
|
||||
/**
|
||||
* Filter the images to display with a <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
* @see $this->process_image()
|
||||
*
|
||||
* @param array $images A list of arrays.
|
||||
* @param string $content The page content.
|
||||
@@ -426,12 +449,23 @@ class Display {
|
||||
}
|
||||
|
||||
foreach ( $images as $i => $image ) {
|
||||
if ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) {
|
||||
if ( ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) &&
|
||||
( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) ) {
|
||||
|
||||
unset( $images[ $i ] );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) {
|
||||
unset( $images[ $i ]['src']['webp_url'] );
|
||||
}
|
||||
|
||||
if ( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) {
|
||||
unset( $images[ $i ]['src']['avif_url'] );
|
||||
}
|
||||
|
||||
unset( $images[ $i ]['src']['webp_path'], $images[ $i ]['src']['webp_exists'] );
|
||||
unset( $images[ $i ]['src']['avif_path'], $images[ $i ]['src']['avif_exists'] );
|
||||
|
||||
if ( empty( $image['srcset'] ) || ! is_array( $image['srcset'] ) ) {
|
||||
unset( $images[ $i ]['srcset'] );
|
||||
@@ -443,11 +477,22 @@ class Display {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) &&
|
||||
( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) ) {
|
||||
unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
|
||||
unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
|
||||
}
|
||||
|
||||
if ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) {
|
||||
unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
|
||||
}
|
||||
|
||||
if ( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) {
|
||||
unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
|
||||
}
|
||||
|
||||
unset( $images[ $i ]['srcset'][ $j ]['webp_path'], $images[ $i ]['srcset'][ $j ]['webp_exists'] );
|
||||
unset( $images[ $i ]['srcset'][ $j ]['avif_path'], $images[ $i ]['srcset'][ $j ]['avif_exists'] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,9 +502,7 @@ class Display {
|
||||
/**
|
||||
* Process an image tag and get an array containing some data.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $image An image html tag.
|
||||
* @return array|false {
|
||||
@@ -527,24 +570,21 @@ class Display {
|
||||
return false;
|
||||
}
|
||||
|
||||
$webp_url = imagify_path_to_webp( $src['src'] );
|
||||
$webp_path = $this->url_to_path( $webp_url );
|
||||
$webp_url .= ! empty( $src['query'] ) ? $src['query'] : '';
|
||||
|
||||
$data = [
|
||||
'tag' => $image,
|
||||
'attributes' => $attributes,
|
||||
'src_attribute' => $src_source,
|
||||
'src' => [
|
||||
'url' => $attributes[ $src_source ],
|
||||
'webp_url' => $webp_url,
|
||||
'webp_path' => $webp_path,
|
||||
'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
|
||||
],
|
||||
'srcset_attribute' => false,
|
||||
'srcset' => [],
|
||||
];
|
||||
|
||||
foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
|
||||
$data['src'][ $key ] = $value;
|
||||
}
|
||||
|
||||
// Deal with the srcset attribute.
|
||||
$srcset_source = false;
|
||||
|
||||
@@ -556,6 +596,8 @@ class Display {
|
||||
}
|
||||
|
||||
if ( $srcset_source ) {
|
||||
$srcset_data = [];
|
||||
|
||||
$data['srcset_attribute'] = $srcset_source;
|
||||
|
||||
$srcset = explode( ',', $attributes[ $srcset_source ] );
|
||||
@@ -582,25 +624,23 @@ class Display {
|
||||
continue;
|
||||
}
|
||||
|
||||
$webp_url = imagify_path_to_webp( $src['src'] );
|
||||
$webp_path = $this->url_to_path( $webp_url );
|
||||
$webp_url .= ! empty( $src['query'] ) ? $src['query'] : '';
|
||||
|
||||
$data['srcset'][] = [
|
||||
$srcset_data = [
|
||||
'url' => $srcs[0],
|
||||
'descriptor' => $srcs[1],
|
||||
'webp_url' => $webp_url,
|
||||
'webp_path' => $webp_path,
|
||||
'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
|
||||
];
|
||||
|
||||
foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
|
||||
$srcset_data[ $key ] = $value;
|
||||
}
|
||||
|
||||
$data['srcset'][] = $srcset_data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter a processed image tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $data An array of data for this image.
|
||||
* @param string $image An image html tag.
|
||||
@@ -618,14 +658,41 @@ class Display {
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next-gen image(webp & avif) data set.
|
||||
*
|
||||
* @param array $src Array of url/path segments.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function get_nextgen_image_data_set( array $src ): array {
|
||||
$webp_url = imagify_path_to_nextgen( $src['src'], 'webp' );
|
||||
$webp_path = $this->url_to_path( $webp_url );
|
||||
|
||||
$avif_url = imagify_path_to_nextgen( $src['src'], 'avif' );
|
||||
$avif_path = $this->url_to_path( $avif_url );
|
||||
$query_string = ! empty( $src['query'] ) ? $src['query'] : '';
|
||||
|
||||
return [
|
||||
// WebP data set.
|
||||
'webp_url' => $webp_url . $query_string,
|
||||
'webp_path' => $webp_path,
|
||||
'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
|
||||
|
||||
// Avif data set.
|
||||
'avif_url' => $avif_url . $query_string,
|
||||
'avif_path' => $avif_path,
|
||||
'avif_exists' => $avif_path && $this->filesystem->exists( $avif_path ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if a content is HTML.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $content The content.
|
||||
*
|
||||
* @param string $content The content.
|
||||
* @return bool
|
||||
*/
|
||||
protected function is_html( $content ) {
|
||||
@@ -635,11 +702,10 @@ class Display {
|
||||
/**
|
||||
* Convert a file URL to an absolute path.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @param string $url A file URL.
|
||||
*
|
||||
* @param string $url A file URL.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
protected function url_to_path( $url ) {
|
||||
@@ -658,7 +724,7 @@ class Display {
|
||||
$uploads_dir = $this->filesystem->get_upload_basedir( true );
|
||||
$root_url = set_url_scheme( $this->filesystem->get_site_root_url() );
|
||||
$root_dir = $this->filesystem->get_site_root();
|
||||
$cdn_url = $this->get_cdn_source();
|
||||
$cdn_url = apply_filters( 'imagify_cdn_source_url', '' );
|
||||
$cdn_url = $cdn_url['url'] ? set_url_scheme( $cdn_url['url'] ) : false;
|
||||
$domain_url = wp_parse_url( $root_url );
|
||||
|
||||
@@ -693,108 +759,4 @@ class Display {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CDN "source".
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $option_url An URL to use instead of the one stored in the option. It is used only if no constant/filter.
|
||||
* @return array {
|
||||
* @type string $source Where does it come from? Possible values are 'constant', 'filter', or 'option'.
|
||||
* @type string $name Who? Can be a constant name, a plugin name, or an empty string.
|
||||
* @type string $url The CDN URL, with a trailing slash. An empty string if no URL is set.
|
||||
* }
|
||||
*/
|
||||
public function get_cdn_source( $option_url = '' ) {
|
||||
if ( defined( 'IMAGIFY_CDN_URL' ) && IMAGIFY_CDN_URL && is_string( IMAGIFY_CDN_URL ) ) {
|
||||
// Use a constant.
|
||||
$source = [
|
||||
'source' => 'constant',
|
||||
'name' => 'IMAGIFY_CDN_URL',
|
||||
'url' => IMAGIFY_CDN_URL,
|
||||
];
|
||||
} else {
|
||||
// Maybe use a filter.
|
||||
$filter_source = [
|
||||
'name' => null,
|
||||
'url' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Provide a custom CDN source.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $filter_source {
|
||||
* @type $name string The name of which provides the URL (plugin name, etc).
|
||||
* @type $url string The CDN URL.
|
||||
* }
|
||||
*/
|
||||
$filter_source = apply_filters( 'imagify_cdn_source', $filter_source );
|
||||
|
||||
if ( ! empty( $filter_source['url'] ) ) {
|
||||
$source = [
|
||||
'source' => 'filter',
|
||||
'name' => ! empty( $filter_source['name'] ) ? $filter_source['name'] : '',
|
||||
'url' => $filter_source['url'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// No constant, no filter: use the option.
|
||||
$source = [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => $option_url && is_string( $option_url ) ? $option_url : get_imagify_option( 'cdn_url' ),
|
||||
];
|
||||
}
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Nothing set.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
$source['url'] = $this->sanitize_cdn_url( $source['url'] );
|
||||
|
||||
if ( empty( $source['url'] ) ) {
|
||||
// Not an URL.
|
||||
return [
|
||||
'source' => 'option',
|
||||
'name' => '',
|
||||
'url' => '',
|
||||
];
|
||||
}
|
||||
|
||||
return $source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize the CDN URL value.
|
||||
*
|
||||
* @since 1.9.3
|
||||
* @access public
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param string $url The URL to sanitize.
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize_cdn_url( $url ) {
|
||||
$url = sanitize_text_field( $url );
|
||||
|
||||
if ( ! $url || ! preg_match( '@^https?://.+\.[^.]+@i', $url ) ) {
|
||||
// Not an URL.
|
||||
return '';
|
||||
}
|
||||
|
||||
return trailingslashit( $url );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Picture;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
|
||||
/**
|
||||
* Service provider for Picture display
|
||||
*/
|
||||
class ServiceProvider extends AbstractServiceProvider {
|
||||
/**
|
||||
* Services provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [
|
||||
'picture_display',
|
||||
];
|
||||
|
||||
/**
|
||||
* Subscribers provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $subscribers = [
|
||||
'picture_display',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers the provided classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->getContainer()->share( 'picture_display', Display::class )
|
||||
->addArgument( $this->getContainer()->get( 'filesystem' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscribers array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribers() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
}
|
||||
@@ -3,16 +3,33 @@ declare(strict_types=1);
|
||||
|
||||
namespace Imagify;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
use Imagify\CLI\BulkOptimizeCommand;
|
||||
use Imagify\CLI\GenerateMissingWebpCommand;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Admin\AdminBar;
|
||||
use Imagify\Bulk\Bulk;
|
||||
use Imagify\CLI\{BulkOptimizeCommand, GenerateMissingNextgenCommand};
|
||||
use Imagify\Dependencies\League\Container\Container;
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\ServiceProviderInterface;
|
||||
use Imagify\EventManagement\{EventManager, SubscriberInterface};
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify_Filesystem;
|
||||
|
||||
/**
|
||||
* Main plugin class.
|
||||
*/
|
||||
class Plugin {
|
||||
/**
|
||||
* Container instance.
|
||||
*
|
||||
* @var Container
|
||||
*/
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Is the plugin loaded
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
private $loaded = false;
|
||||
|
||||
/**
|
||||
* Absolute path to the plugin (with trailing slash).
|
||||
*
|
||||
@@ -25,22 +42,64 @@ class Plugin {
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $plugin_args {
|
||||
* @param Container $container Instance of the container.
|
||||
* @param array $plugin_args {
|
||||
* An array of arguments.
|
||||
*
|
||||
* @type string $plugin_path Absolute path to the plugin (with trailing slash).
|
||||
* }
|
||||
*/
|
||||
public function __construct( $plugin_args ) {
|
||||
public function __construct( Container $container, $plugin_args ) {
|
||||
$this->container = $container;
|
||||
$this->plugin_path = $plugin_args['plugin_path'];
|
||||
|
||||
add_filter( 'imagify_container', [ $this, 'get_container' ] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the container instance.
|
||||
*
|
||||
* @return Container
|
||||
*/
|
||||
public function get_container() {
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the plugin is loaded
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function is_loaded(): bool {
|
||||
return $this->loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin init.
|
||||
*
|
||||
* @param array $providers Array of service providers.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
public function init() {
|
||||
public function init( $providers ) {
|
||||
if ( $this->is_loaded() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->container->share(
|
||||
'event_manager',
|
||||
function () {
|
||||
return new EventManager();
|
||||
}
|
||||
);
|
||||
|
||||
$this->container->share(
|
||||
'filesystem',
|
||||
function() {
|
||||
return new Imagify_Filesystem();
|
||||
}
|
||||
);
|
||||
|
||||
$this->include_files();
|
||||
|
||||
class_alias( '\\Imagify\\Traits\\InstanceGetterTrait', '\\Imagify\\Traits\\FakeSingletonTrait' );
|
||||
@@ -55,7 +114,6 @@ class Plugin {
|
||||
\Imagify_Cron_Sync_Files::get_instance()->init();
|
||||
\Imagify\Auth\Basic::get_instance()->init();
|
||||
\Imagify\Job\MediaOptimization::get_instance()->init();
|
||||
\Imagify\Stats\OptimizedMediaWithoutWebp::get_instance()->init();
|
||||
Bulk::get_instance()->init();
|
||||
AdminBar::get_instance()->init();
|
||||
|
||||
@@ -72,15 +130,21 @@ class Plugin {
|
||||
\Imagify_Assets::get_instance()->init();
|
||||
}
|
||||
|
||||
\Imagify\Webp\Display::get_instance()->init();
|
||||
|
||||
add_action( 'init', [ $this, 'maybe_activate' ] );
|
||||
|
||||
// Load plugin translations.
|
||||
imagify_load_translations();
|
||||
|
||||
imagify_add_command( new BulkOptimizeCommand() );
|
||||
imagify_add_command( new GenerateMissingWebpCommand() );
|
||||
imagify_add_command( new GenerateMissingNextgenCommand() );
|
||||
|
||||
foreach ( $providers as $service_provider ) {
|
||||
$provider_instance = new $service_provider();
|
||||
$this->container->addServiceProvider( $provider_instance );
|
||||
|
||||
// Load each service provider's subscribers if found.
|
||||
$this->load_subscribers( $provider_instance );
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires when Imagify is fully loaded.
|
||||
@@ -91,6 +155,8 @@ class Plugin {
|
||||
* @param \Imagify_Plugin $plugin Instance of this class.
|
||||
*/
|
||||
do_action( 'imagify_loaded', $this );
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,4 +237,25 @@ class Plugin {
|
||||
*/
|
||||
do_action( 'imagify_activation', (int) $user_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Load list of event subscribers from service provider.
|
||||
*
|
||||
* @param ServiceProviderInterface $service_provider Instance of service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function load_subscribers( ServiceProviderInterface $service_provider ) {
|
||||
if ( empty( $service_provider->get_subscribers() ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $service_provider->get_subscribers() as $subscriber ) {
|
||||
$subscriber_object = $this->container->get( $subscriber );
|
||||
|
||||
if ( $subscriber_object instanceof SubscriberInterface ) {
|
||||
$this->container->get( 'event_manager' )->add_subscriber( $subscriber_object );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Stats;
|
||||
|
||||
use Imagify\Bulk\Bulk;
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Class to get and cache the number of optimized media without WebP versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* Class to get and cache the number of optimized media without next-gen versions.
|
||||
*/
|
||||
class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
class OptimizedMediaWithoutNextGen implements StatInterface, SubscriberInterface {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
@@ -17,28 +18,26 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NAME = 'imagify_stat_without_webp';
|
||||
const NAME = 'imagify_stat_without_next_gen';
|
||||
|
||||
/**
|
||||
* Launch hooks.
|
||||
* Array of events this subscriber listens to
|
||||
*
|
||||
* @since 1.9
|
||||
* @return array
|
||||
*/
|
||||
public function init() {
|
||||
add_action( 'imagify_after_optimize', [ $this, 'maybe_clear_cache_after_optimization' ], 10, 2 );
|
||||
add_action( 'imagify_after_restore_media', [ $this, 'maybe_clear_cache_after_restoration' ], 10, 4 );
|
||||
add_action( 'imagify_delete_media', [ $this, 'maybe_clear_cache_on_deletion' ] );
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_after_optimize' => [ 'maybe_clear_cache_after_optimization', 10, 2 ],
|
||||
'imagify_after_restore_media' => [ 'maybe_clear_cache_after_restoration', 10, 4 ],
|
||||
'imagify_delete_media' => 'maybe_clear_cache_on_deletion',
|
||||
'update_option_imagify_settings' => [ 'maybe_clear_stat_cache', 9, 2 ],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** GET/CACHE THE STAT ====================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the number of optimized media without WebP versions.
|
||||
* Get the number of optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -48,16 +47,16 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
|
||||
// Sum the counts of each context.
|
||||
foreach ( imagify_get_context_names() as $context ) {
|
||||
$stat += $bulk->get_bulk_instance( $context )->has_optimized_media_without_webp();
|
||||
$stat += $bulk->get_bulk_instance( $context )->has_optimized_media_without_nextgen();
|
||||
}
|
||||
|
||||
return $stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and cache the number of optimized media without WebP versions.
|
||||
* Get and cache the number of optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
@@ -83,21 +82,16 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
/**
|
||||
* Clear the stat cache.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*/
|
||||
public function clear_cache() {
|
||||
delete_transient( static::NAME );
|
||||
}
|
||||
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Clear cache after optimizing a media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param array $item The item being processed.
|
||||
@@ -113,11 +107,15 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
$new_sizes = array_intersect_key( $sizes, $new_sizes );
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$size_name = 'full' . $process::AVIF_SUFFIX;
|
||||
}
|
||||
|
||||
if ( ! isset( $new_sizes['full'] ) && ! empty( $new_sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* We just successfully generated the WebP version of the full size.
|
||||
* We just successfully generated the next-gen version of the full size.
|
||||
* The full size was not optimized at the same time, that means it was optimized previously.
|
||||
* Meaning: we just added a WebP version to a media that was previously optimized, so there is one less optimized media without WebP.
|
||||
* Meaning: we just added a next-gen version to a media that was previously optimized, so there is one less optimized media without next-gen.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
return;
|
||||
@@ -125,7 +123,7 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
|
||||
if ( ! empty( $new_sizes['full']['success'] ) && empty( $new_sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* We now have a new optimized media without WebP.
|
||||
* We now have a new optimized media without next-gen.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
@@ -134,7 +132,7 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
/**
|
||||
* Clear cache after restoring a media.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @param ProcessInterface $process The optimization process.
|
||||
* @param bool|WP_Error $response The result of the operation: true on success, a WP_Error object on failure.
|
||||
@@ -149,9 +147,13 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
$sizes = isset( $data['sizes'] ) ? (array) $data['sizes'] : [];
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$size_name = 'full' . $process::AVIF_SUFFIX;
|
||||
}
|
||||
|
||||
if ( ! empty( $sizes['full']['success'] ) && empty( $sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* This media had no WebP versions.
|
||||
* This media had no next-gen versions.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
@@ -160,7 +162,7 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
/**
|
||||
* Clear cache on media deletion.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @param ProcessInterface $process An optimization process.
|
||||
*/
|
||||
@@ -173,11 +175,37 @@ class OptimizedMediaWithoutWebp implements StatInterface {
|
||||
$sizes = isset( $data['sizes'] ) ? (array) $data['sizes'] : [];
|
||||
$size_name = 'full' . $process::WEBP_SUFFIX;
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$size_name = 'full' . $process::AVIF_SUFFIX;
|
||||
}
|
||||
|
||||
if ( ! empty( $sizes['full']['success'] ) && empty( $sizes[ $size_name ]['success'] ) ) {
|
||||
/**
|
||||
* This media had no WebP versions.
|
||||
* This media had no next-gen versions.
|
||||
*/
|
||||
$this->clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maybe clear the stat cache on option change
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param array $old_value The old option value.
|
||||
* @param array $value The new option value.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function maybe_clear_stat_cache( $old_value, $value ) {
|
||||
if ( isset( $old_value['convert_to_avif'] ) && isset( $value['convert_to_avif'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $old_value['convert_to_avif'] ) && ! isset( $value['convert_to_avif'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->clear_cache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Stats;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
|
||||
/**
|
||||
* Service provider for Stats
|
||||
*/
|
||||
class ServiceProvider extends AbstractServiceProvider {
|
||||
/**
|
||||
* Services provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [
|
||||
'optimized_media_without_next_gen',
|
||||
];
|
||||
|
||||
/**
|
||||
* Subscribers provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $subscribers = [
|
||||
'optimized_media_without_next_gen',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers the provided classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->getContainer()->share( 'optimized_media_without_next_gen', OptimizedMediaWithoutNextGen::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscribers array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribers() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use Imagify\WriteFile\AbstractApacheDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove contents to the .htaccess file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
|
||||
class Apache extends AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: webp file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -1,70 +1,64 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\WriteFile\WriteFileInterface;
|
||||
|
||||
/**
|
||||
* Display WebP images on the site.
|
||||
* Display WebP images on the site using picture tag.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class Display {
|
||||
class Display implements SubscriberInterface {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
/**
|
||||
* Server conf object.
|
||||
*
|
||||
* @var \Imagify\WriteFile\WriteFileInterface
|
||||
* @var WriteFileInterface|null
|
||||
* @since 1.9
|
||||
*/
|
||||
protected $server_conf;
|
||||
protected $server_conf = null;
|
||||
|
||||
/**
|
||||
* Init.
|
||||
* Returns an array of events this subscriber listens to
|
||||
*
|
||||
* @since 1.9
|
||||
* @return array
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'imagify_settings_on_save', [ $this, 'maybe_add_rewrite_rules' ] );
|
||||
add_action( 'imagify_settings_webp_info', [ $this, 'maybe_add_webp_info' ] );
|
||||
add_action( 'imagify_activation', [ $this, 'activate' ] );
|
||||
add_action( 'imagify_deactivation', [ $this, 'deactivate' ] );
|
||||
|
||||
Picture\Display::get_instance()->init();
|
||||
RewriteRules\Display::get_instance()->init();
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 13 ],
|
||||
'imagify_settings_webp_info' => 'maybe_add_webp_info',
|
||||
'imagify_activation' => 'activate',
|
||||
'imagify_deactivation' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* If display WebP images, add the WebP type to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
$old_value = (bool) get_imagify_option( 'display_webp' );
|
||||
// See \Imagify_Options->validate_values_on_update() for why we use 'convert_to_webp' here.
|
||||
$new_value = ! empty( $values['display_webp'] ) && ! empty( $values['convert_to_webp'] );
|
||||
|
||||
if ( $old_value === $new_value ) {
|
||||
// No changes.
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( $new_value ) {
|
||||
$enabled = isset( $values['display_nextgen'] ) ? true : false;
|
||||
$result = false;
|
||||
|
||||
if ( $enabled ) {
|
||||
// Add the WebP file type.
|
||||
$result = $this->get_server_conf()->add();
|
||||
} else {
|
||||
} elseif ( ! $enabled ) {
|
||||
// Remove the WebP file type.
|
||||
$result = $this->get_server_conf()->remove();
|
||||
}
|
||||
@@ -76,10 +70,12 @@ class Display {
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
} else {
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
@@ -130,9 +126,11 @@ class Display {
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
return;
|
||||
}
|
||||
@@ -151,9 +149,6 @@ class Display {
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$file_path = $conf->get_file_path();
|
||||
$filesystem = \Imagify_Filesystem::get_instance();
|
||||
@@ -168,10 +163,6 @@ class Display {
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
@@ -194,30 +185,13 @@ class Display {
|
||||
return $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the WebP display method by validating the given value.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @return string 'picture' or 'rewrite'.
|
||||
*/
|
||||
public function get_display_webp_method( $values ) {
|
||||
$options = \Imagify_Options::get_instance();
|
||||
$default = $options->get_default_values();
|
||||
$default = $default['display_webp_method'];
|
||||
$method = ! empty( $values['display_webp_method'] ) ? $values['display_webp_method'] : '';
|
||||
|
||||
return $options->sanitize_and_validate( 'display_webp_method', $method, $default );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server conf instance.
|
||||
* Note: nothing needed for nginx.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return \Imagify\WriteFile\WriteFileInterface
|
||||
* @return WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7;
|
||||
@@ -230,8 +204,6 @@ class Display {
|
||||
$this->server_conf = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$this->server_conf = new IIS();
|
||||
} else {
|
||||
$this->server_conf = false;
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use Imagify\WriteFile\AbstractIISDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove contents to the web.config file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
class IIS extends \Imagify\WriteFile\AbstractIISDirConfFile {
|
||||
class IIS extends AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: webp file type';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use Imagify\WriteFile\AbstractApacheDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the .htaccess file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
|
||||
class Apache extends AbstractApacheDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
@@ -24,14 +24,13 @@ class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @source https://github.com/vincentorback/WebP-images-with-htaccess
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$extensions = str_replace( '|webp', '', $extensions );
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
|
||||
@@ -1,100 +1,80 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
use Imagify\EventManagement\SubscriberInterface;
|
||||
use Imagify\Notices\Notices;
|
||||
use Imagify\Traits\InstanceGetterTrait;
|
||||
use Imagify\WriteFile\AbstractWriteDirConfFile;
|
||||
use Imagify\WriteFile\WriteFileInterface;
|
||||
|
||||
/**
|
||||
* Display WebP images on the site with rewrite rules.
|
||||
*
|
||||
* @since 1.9
|
||||
*/
|
||||
class Display {
|
||||
use InstanceGetterTrait;
|
||||
|
||||
class Display implements SubscriberInterface {
|
||||
/**
|
||||
* Configuration file writer.
|
||||
*
|
||||
* @var AbstractWriteDirConfFile
|
||||
* @var WriteFileInterface|null
|
||||
*/
|
||||
protected $server_conf;
|
||||
protected $server_conf = null;
|
||||
|
||||
/**
|
||||
* Option value.
|
||||
*
|
||||
* @var string
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const OPTION_VALUE = 'rewrite';
|
||||
|
||||
/**
|
||||
* Init.
|
||||
* Returns an array of events this subscriber listens to
|
||||
*
|
||||
* @since 1.9
|
||||
* @return array
|
||||
*/
|
||||
public function init() {
|
||||
add_filter( 'imagify_settings_on_save', [ $this, 'maybe_add_rewrite_rules' ] );
|
||||
add_action( 'imagify_settings_webp_info', [ $this, 'maybe_add_webp_info' ] );
|
||||
add_action( 'imagify_activation', [ $this, 'activate' ] );
|
||||
add_action( 'imagify_deactivation', [ $this, 'deactivate' ] );
|
||||
public static function get_subscribed_events() {
|
||||
return [
|
||||
'imagify_settings_on_save' => [ 'maybe_add_rewrite_rules', 10 ],
|
||||
'imagify_settings_webp_info' => 'maybe_add_webp_info',
|
||||
'imagify_activation' => 'activate',
|
||||
'imagify_deactivation' => 'deactivate',
|
||||
];
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** HOOKS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* If display WebP images via rewrite rules, add the rules to the .htaccess/etc file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param array $values The option values.
|
||||
* @param array $values The option values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function maybe_add_rewrite_rules( $values ) {
|
||||
global $is_apache, $is_iis7, $is_nginx;
|
||||
|
||||
// Display WebP?
|
||||
$was_enabled = (bool) get_imagify_option( 'display_webp' );
|
||||
// See \Imagify_Options->validate_values_on_update() for why we use 'convert_to_webp' here.
|
||||
$is_enabled = ! empty( $values['display_webp'] ) && ! empty( $values['convert_to_webp'] );
|
||||
$was_enabled = (bool) get_imagify_option( 'display_nextgen' );
|
||||
$is_enabled = ! empty( $values['display_nextgen'] );
|
||||
|
||||
// Which method?
|
||||
$old_value = get_imagify_option( 'display_webp_method' );
|
||||
$new_value = ! empty( $values['display_webp_method'] ) ? $values['display_webp_method'] : '';
|
||||
$old_value = get_imagify_option( 'display_nextgen_method' );
|
||||
$new_value = ! empty( $values['display_nextgen_method'] ) ? $values['display_nextgen_method'] : '';
|
||||
|
||||
// Decide when to add or remove rules.
|
||||
$is_rewrite = self::OPTION_VALUE === $new_value;
|
||||
$was_rewrite = self::OPTION_VALUE === $old_value;
|
||||
$add_or_remove = false;
|
||||
$is_rewrite = self::OPTION_VALUE === $new_value;
|
||||
$was_rewrite = self::OPTION_VALUE === $old_value;
|
||||
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( $is_enabled && $is_rewrite && ( ! $was_enabled || ! $was_rewrite ) ) {
|
||||
// Display WebP & use rewrite method, but only if one of the values changed: add rules.
|
||||
$add_or_remove = 'add';
|
||||
} elseif ( $was_enabled && $was_rewrite && ( ! $is_enabled || ! $is_rewrite ) ) {
|
||||
// Was displaying WebP & was using rewrite method, but only if one of the values changed: remove rules.
|
||||
$add_or_remove = 'remove';
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( $is_apache ) {
|
||||
$rules = new Apache();
|
||||
} elseif ( $is_iis7 ) {
|
||||
$rules = new IIS();
|
||||
} elseif ( $is_nginx ) {
|
||||
$rules = new Nginx();
|
||||
} else {
|
||||
return $values;
|
||||
}
|
||||
|
||||
if ( 'add' === $add_or_remove ) {
|
||||
// Add the rewrite rules.
|
||||
$result = $rules->add();
|
||||
} else {
|
||||
$result = $this->get_server_conf()->add();
|
||||
} elseif ( $was_enabled && $was_rewrite && ( ! $is_enabled || ! $is_rewrite ) ) {
|
||||
// Remove the rewrite rules.
|
||||
$result = $rules->remove();
|
||||
$result = $this->get_server_conf()->remove();
|
||||
}
|
||||
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
@@ -104,10 +84,12 @@ class Display {
|
||||
// Display an error message.
|
||||
if ( is_multisite() && strpos( wp_get_referer(), network_admin_url( '/' ) ) === 0 ) {
|
||||
Notices::get_instance()->add_network_temporary_notice( $result->get_error_message() );
|
||||
} else {
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
Notices::get_instance()->add_site_temporary_notice( $result->get_error_message() );
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
@@ -162,10 +144,11 @@ class Display {
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( is_wp_error( $conf->is_file_writable() ) ) {
|
||||
@@ -186,10 +169,10 @@ class Display {
|
||||
if ( ! $conf ) {
|
||||
return;
|
||||
}
|
||||
if ( ! get_imagify_option( 'display_webp' ) ) {
|
||||
if ( ! get_imagify_option( 'display_nextgen' ) ) {
|
||||
return;
|
||||
}
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_webp_method' ) ) {
|
||||
if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -206,17 +189,14 @@ class Display {
|
||||
$conf->remove();
|
||||
}
|
||||
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
/** TOOLS =================================================================================== */
|
||||
/** ----------------------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Get the path to the directory conf file.
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @param bool $relative True to get a path relative to the site’s root.
|
||||
* @return string|bool The file path. False on failure.
|
||||
*
|
||||
* @return string|bool The file path. False on failure.
|
||||
*/
|
||||
public function get_file_path( $relative = false ) {
|
||||
if ( ! $this->get_server_conf() ) {
|
||||
@@ -237,7 +217,7 @@ class Display {
|
||||
*
|
||||
* @since 1.9
|
||||
*
|
||||
* @return \Imagify\WriteFile\WriteFileInterface
|
||||
* @return WriteFileInterface
|
||||
*/
|
||||
protected function get_server_conf() {
|
||||
global $is_apache, $is_iis7, $is_nginx;
|
||||
@@ -252,8 +232,6 @@ class Display {
|
||||
$this->server_conf = new IIS();
|
||||
} elseif ( $is_nginx ) {
|
||||
$this->server_conf = new Nginx();
|
||||
} else {
|
||||
$this->server_conf = false;
|
||||
}
|
||||
|
||||
return $this->server_conf;
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use Imagify\WriteFile\AbstractIISDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the web.config file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
class IIS extends \Imagify\WriteFile\AbstractIISDirConfFile {
|
||||
class IIS extends AbstractIISDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @since 1.9
|
||||
* @source https://github.com/igrigorik/webp-detect/blob/master/iis.config
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$extensions = str_replace( '|webp', '', $extensions );
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
|
||||
@@ -1,52 +1,70 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp\RewriteRules;
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
use Imagify\WriteFile\AbstractNginxDirConfFile;
|
||||
|
||||
/**
|
||||
* Add and remove rewrite rules to the imagify.conf file to display WebP images on the site.
|
||||
*
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*/
|
||||
class Nginx extends \Imagify\WriteFile\AbstractNginxDirConfFile {
|
||||
class Nginx extends AbstractNginxDirConfFile {
|
||||
|
||||
/**
|
||||
* Name of the tag used as block delemiter.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.9
|
||||
* @author Grégory Viguier
|
||||
* @var string
|
||||
* @since 1.9
|
||||
*/
|
||||
const TAG_NAME = 'Imagify: rewrite rules for webp';
|
||||
|
||||
/**
|
||||
* Get unfiltered new contents to write into the file.
|
||||
*
|
||||
* @since 1.9
|
||||
* @access protected
|
||||
* @author Grégory Viguier
|
||||
* @since 1.9
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_raw_new_contents() {
|
||||
$extensions = $this->get_extensions_pattern();
|
||||
$extensions = $this->get_extensions_pattern() . '|avif';
|
||||
$home_root = wp_parse_url( home_url( '/' ) );
|
||||
$home_root = $home_root['path'];
|
||||
|
||||
return trim( '
|
||||
location ~* ^(' . $home_root . '.+)\.(' . $extensions . ')$ {
|
||||
add_header Vary Accept;
|
||||
add_header Vary Accept;
|
||||
|
||||
if ($http_accept ~* "webp"){
|
||||
set $imwebp A;
|
||||
}
|
||||
if (-f $request_filename.webp) {
|
||||
set $imwebp "${imwebp}B";
|
||||
}
|
||||
if ($imwebp = AB) {
|
||||
rewrite ^(.*) $1.webp;
|
||||
}
|
||||
set $canavif 1;
|
||||
|
||||
if ($http_accept !~* "avif"){
|
||||
set $canavif 0;
|
||||
}
|
||||
|
||||
if (!-f $request_filename.avif) {
|
||||
set $canavif 0;
|
||||
|
||||
}
|
||||
if ($canavif = 1){
|
||||
rewrite ^(.*) $1.avif;
|
||||
break;
|
||||
}
|
||||
|
||||
set $canwebp 1;
|
||||
|
||||
if ($http_accept !~* "webp"){
|
||||
set $canwebp 0;
|
||||
}
|
||||
|
||||
if (!-f $request_filename.webp) {
|
||||
set $canwebp 0;
|
||||
|
||||
}
|
||||
if ($canwebp = 1){
|
||||
rewrite ^(.*) $1.webp;
|
||||
break;
|
||||
}
|
||||
}' );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Imagify\Webp;
|
||||
|
||||
use Imagify\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
use Imagify\Webp\RewriteRules\Display as RewriteRules;
|
||||
|
||||
/**
|
||||
* Service provider for WebP rewrite rules
|
||||
*/
|
||||
class ServiceProvider extends AbstractServiceProvider {
|
||||
/**
|
||||
* Services provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $provides = [
|
||||
'webp_display',
|
||||
'webp_rewrite_rules',
|
||||
];
|
||||
|
||||
/**
|
||||
* Subscribers provided by this provider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $subscribers = [
|
||||
'webp_display',
|
||||
'webp_rewrite_rules',
|
||||
];
|
||||
|
||||
/**
|
||||
* Registers the provided classes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register() {
|
||||
$this->getContainer()->share( 'webp_display', Display::class );
|
||||
$this->getContainer()->share( 'webp_rewrite_rules', RewriteRules::class );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscribers array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_subscribers() {
|
||||
return $this->subscribers;
|
||||
}
|
||||
}
|
||||
8
wp/wp-content/plugins/imagify/config/providers.php
Normal file
8
wp/wp-content/plugins/imagify/config/providers.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
return [
|
||||
'Imagify\Avif\ServiceProvider',
|
||||
'Imagify\CDN\ServiceProvider',
|
||||
'Imagify\Picture\ServiceProvider',
|
||||
'Imagify\Stats\ServiceProvider',
|
||||
'Imagify\Webp\ServiceProvider',
|
||||
];
|
||||
@@ -3,23 +3,23 @@
|
||||
* Plugin Name: Imagify
|
||||
* Plugin URI: https://wordpress.org/plugins/imagify/
|
||||
* Description: Dramatically reduce image file sizes without losing quality, make your website load faster, boost your SEO and save money on your bandwidth using Imagify, the new most advanced image optimization tool.
|
||||
* Version: 2.1.3.1
|
||||
* Version: 2.2.0.1
|
||||
* Requires at least: 5.3
|
||||
* Requires PHP: 7.0
|
||||
* Author: Imagify – Optimize Images & Convert WebP
|
||||
* Author: Imagify – Optimize Images & Convert WebP & Avif
|
||||
* Author URI: https://imagify.io
|
||||
* Licence: GPLv2
|
||||
*
|
||||
* Text Domain: imagify
|
||||
* Domain Path: languages
|
||||
*
|
||||
* Copyright 2023 WP Media
|
||||
* Copyright 2024 WP Media
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
|
||||
|
||||
// Imagify defines.
|
||||
define( 'IMAGIFY_VERSION', '2.1.3.1' );
|
||||
define( 'IMAGIFY_VERSION', '2.2.0.1' );
|
||||
define( 'IMAGIFY_SLUG', 'imagify' );
|
||||
define( 'IMAGIFY_FILE', __FILE__ );
|
||||
define( 'IMAGIFY_PATH', realpath( plugin_dir_path( IMAGIFY_FILE ) ) . '/' );
|
||||
|
||||
@@ -122,7 +122,7 @@ class Main extends \Imagify_AS3CF_Deprecated {
|
||||
* WebP images to display with a <picture> tag.
|
||||
*
|
||||
* @since 1.9
|
||||
* @see \Imagify\Webp\Picture\Display->process_image()
|
||||
* @see \Imagify\Picture\Display->process_image()
|
||||
* @author Grégory Viguier
|
||||
*
|
||||
* @param array $data An array of data for this image.
|
||||
@@ -443,7 +443,7 @@ class Main extends \Imagify_AS3CF_Deprecated {
|
||||
}
|
||||
|
||||
foreach ( $paths as $size_name => $file_path ) {
|
||||
if ( 'thumb' === $size_name || 'backup' === $size_name || $process->is_size_webp( $size_name ) ) {
|
||||
if ( 'thumb' === $size_name || 'backup' === $size_name || $process->is_size_next_gen( $size_name ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,10 +100,11 @@ class NGG extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ids of all optimized media without WebP versions.
|
||||
* Get ids of all optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 1.9.5 The method doesn't return the IDs directly anymore.
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $format Format we are looking for. (webp|avif).
|
||||
*
|
||||
* @return array {
|
||||
* @type array $ids A list of media IDs.
|
||||
@@ -113,7 +114,7 @@ class NGG extends AbstractBulk {
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public function get_optimized_media_ids_without_webp() {
|
||||
public function get_optimized_media_ids_without_format( $format ) {
|
||||
global $wpdb;
|
||||
|
||||
$this->set_no_time_limit();
|
||||
@@ -121,7 +122,12 @@ class NGG extends AbstractBulk {
|
||||
$storage = C_Gallery_Storage::get_instance();
|
||||
$ngg_table = $wpdb->prefix . 'ngg_pictures';
|
||||
$data_table = DB::get_instance()->get_table_name();
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
|
||||
}
|
||||
|
||||
$files = $wpdb->get_col( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
SELECT ngg.pid
|
||||
@@ -132,7 +138,7 @@ class NGG extends AbstractBulk {
|
||||
( data.status = 'success' OR data.status = 'already_optimized' )
|
||||
AND data.data NOT LIKE %s
|
||||
ORDER BY ngg.pid DESC",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
'%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
|
||||
$wpdb->flush();
|
||||
@@ -175,18 +181,22 @@ class NGG extends AbstractBulk {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell if there are optimized media without WebP versions.
|
||||
* Tell if there are optimized media without next-gen versions.
|
||||
*
|
||||
* @since 1.9
|
||||
* @since 2.2
|
||||
*
|
||||
* @return int The number of media.
|
||||
*/
|
||||
public function has_optimized_media_without_webp() {
|
||||
public function has_optimized_media_without_nextgen() {
|
||||
global $wpdb;
|
||||
|
||||
$ngg_table = $wpdb->prefix . 'ngg_pictures';
|
||||
$data_table = DB::get_instance()->get_table_name();
|
||||
$webp_suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::WEBP_SUFFIX' );
|
||||
|
||||
if ( get_imagify_option( 'convert_to_avif' ) ) {
|
||||
$suffix = constant( imagify_get_optimization_process_class_name( 'ngg' ) . '::AVIF_SUFFIX' );
|
||||
}
|
||||
|
||||
return (int) $wpdb->get_var( $wpdb->prepare( // WPCS: unprepared SQL ok.
|
||||
"
|
||||
@@ -197,7 +207,7 @@ class NGG extends AbstractBulk {
|
||||
WHERE
|
||||
( data.status = 'success' OR data.status = 'already_optimized' )
|
||||
AND data.data NOT LIKE %s",
|
||||
'%' . $wpdb->esc_like( $webp_suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
'%' . $wpdb->esc_like( $suffix . '";a:4:{s:7:"success";b:1;' ) . '%'
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
@@ -284,7 +284,8 @@ function imagify_ngg_cleanup_after_media_deletion( $image_id, $image ) {
|
||||
* The backup file has already been deleted by NGG.
|
||||
* Delete the WebP versions and the optimization data.
|
||||
*/
|
||||
$process->delete_webp_files();
|
||||
$process->delete_nextgen_files();
|
||||
|
||||
$process->get_data()->delete_optimization_data();
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ class Main extends \Imagify_Regenerate_Thumbnails_Deprecated {
|
||||
|
||||
if ( ! empty( $optimization_data['sizes'] ) ) {
|
||||
foreach ( $optimization_data['sizes'] as $size_name => $size_data ) {
|
||||
$non_webp_size_name = $process->is_size_webp( $size_name );
|
||||
$non_webp_size_name = $process->is_size_next_gen( $size_name );
|
||||
|
||||
if ( ! $non_webp_size_name || ! isset( $sizes[ $non_webp_size_name ] ) ) {
|
||||
continue;
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
* Description: A robust scheduling library for use in WordPress plugins.
|
||||
* Author: Automattic
|
||||
* Author URI: https://automattic.com/
|
||||
* Version: 3.5.4
|
||||
* Version: 3.7.1
|
||||
* License: GPLv3
|
||||
* Requires at least: 6.2
|
||||
* Tested up to: 6.4
|
||||
* Requires PHP: 5.6
|
||||
*
|
||||
* Copyright 2019 Automattic, Inc. (https://automattic.com/contact/)
|
||||
*
|
||||
@@ -26,27 +29,29 @@
|
||||
* @package ActionScheduler
|
||||
*/
|
||||
|
||||
if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
|
||||
if ( ! function_exists( 'action_scheduler_register_3_dot_7_dot_1' ) && function_exists( 'add_action' ) ) { // WRCS: DEFINED_VERSION.
|
||||
|
||||
if ( ! class_exists( 'ActionScheduler_Versions', false ) ) {
|
||||
require_once __DIR__ . '/classes/ActionScheduler_Versions.php';
|
||||
add_action( 'plugins_loaded', array( 'ActionScheduler_Versions', 'initialize_latest_version' ), 1, 0 );
|
||||
}
|
||||
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_5_dot_4', 0, 0 ); // WRCS: DEFINED_VERSION.
|
||||
add_action( 'plugins_loaded', 'action_scheduler_register_3_dot_7_dot_1', 0, 0 ); // WRCS: DEFINED_VERSION.
|
||||
|
||||
// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
|
||||
/**
|
||||
* Registers this version of Action Scheduler.
|
||||
*/
|
||||
function action_scheduler_register_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
|
||||
function action_scheduler_register_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
|
||||
$versions = ActionScheduler_Versions::instance();
|
||||
$versions->register( '3.5.4', 'action_scheduler_initialize_3_dot_5_dot_4' ); // WRCS: DEFINED_VERSION.
|
||||
$versions->register( '3.7.1', 'action_scheduler_initialize_3_dot_7_dot_1' ); // WRCS: DEFINED_VERSION.
|
||||
}
|
||||
|
||||
// phpcs:disable Generic.Functions.OpeningFunctionBraceKernighanRitchie.ContentAfterBrace
|
||||
/**
|
||||
* Initializes this version of Action Scheduler.
|
||||
*/
|
||||
function action_scheduler_initialize_3_dot_5_dot_4() { // WRCS: DEFINED_VERSION.
|
||||
function action_scheduler_initialize_3_dot_7_dot_1() { // WRCS: DEFINED_VERSION.
|
||||
// A final safety check is required even here, because historic versions of Action Scheduler
|
||||
// followed a different pattern (in some unusual cases, we could reach this point and the
|
||||
// ActionScheduler class is already defined—so we need to guard against that).
|
||||
@@ -58,7 +63,7 @@ if ( ! function_exists( 'action_scheduler_register_3_dot_5_dot_4' ) && function_
|
||||
|
||||
// Support usage in themes - load this version if no plugin has loaded a version yet.
|
||||
if ( did_action( 'plugins_loaded' ) && ! doing_action( 'plugins_loaded' ) && ! class_exists( 'ActionScheduler', false ) ) {
|
||||
action_scheduler_initialize_3_dot_5_dot_4(); // WRCS: DEFINED_VERSION.
|
||||
action_scheduler_initialize_3_dot_7_dot_1(); // WRCS: DEFINED_VERSION.
|
||||
do_action( 'action_scheduler_pre_theme_init' );
|
||||
ActionScheduler_Versions::initialize_latest_version();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,65 @@
|
||||
*** Changelog ***
|
||||
|
||||
= 3.7.1 - 2023-12-13 =
|
||||
* Release/3.7.0.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.
|
||||
|
||||
= 3.7.0 - 2023-11-20 =
|
||||
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
|
||||
* Add extended indexes for hook_status_scheduled_date_gmt and status_sheduled_date_gmt.
|
||||
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
|
||||
* Release/3.6.4.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* Update unit tests for upcoming dependency version policy.
|
||||
* make sure hook action_scheduler_failed_execution can access original exception object.
|
||||
* mention dependency version policy in usage.md.
|
||||
|
||||
= 3.6.4 - 2023-10-11 =
|
||||
* Performance improvements when bulk cancelling actions.
|
||||
* Dev-related fixes.
|
||||
|
||||
= 3.6.3 - 2023-09-13 =
|
||||
* Use `_doing_it_wrong` in initialization check.
|
||||
|
||||
= 3.6.2 - 2023-08-09 =
|
||||
* Add guidance about passing arguments.
|
||||
* Atomic option locking.
|
||||
* Improve bulk delete handling.
|
||||
* Include database error in the exception message.
|
||||
* Tweak - WP 6.3 compatibility.
|
||||
|
||||
= 3.6.1 - 2023-06-14 =
|
||||
* Document new optional `$priority` arg for various API functions.
|
||||
* Document the new `--exclude-groups` WP CLI option.
|
||||
* Document the new `action_scheduler_init` hook.
|
||||
* Ensure actions within each claim are executed in the expected order.
|
||||
* Fix incorrect text domain.
|
||||
* Remove SHOW TABLES usage when checking if tables exist.
|
||||
|
||||
= 3.6.0 - 2023-05-10 =
|
||||
* Add $unique parameter to function signatures.
|
||||
* Add a cast-to-int for extra safety before forming new DateTime object.
|
||||
* Add a hook allowing exceptions for consistently failing recurring actions.
|
||||
* Add action priorities.
|
||||
* Add init hook.
|
||||
* Always raise the time limit.
|
||||
* Bump minimatch from 3.0.4 to 3.0.8.
|
||||
* Bump yaml from 2.2.1 to 2.2.2.
|
||||
* Defensive coding relating to gaps in declared schedule types.
|
||||
* Do not process an action if it cannot be set to `in-progress`.
|
||||
* Filter view labels (status names) should be translatable | #919.
|
||||
* Fix WPCLI progress messages.
|
||||
* Improve data-store initialization flow.
|
||||
* Improve error handling across all supported PHP versions.
|
||||
* Improve logic for flushing the runtime cache.
|
||||
* Support exclusion of multiple groups.
|
||||
* Update lint-staged and Node/NPM requirements.
|
||||
* add CLI clean command.
|
||||
* add CLI exclude-group filter.
|
||||
* exclude past-due from list table all filter count.
|
||||
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.
|
||||
|
||||
= 3.5.4 - 2023-01-17 =
|
||||
* Add pre filters during action registration.
|
||||
* Async scheduling.
|
||||
|
||||
@@ -13,10 +13,15 @@ class ActionScheduler_ActionFactory {
|
||||
* @param array $args Args to pass to callbacks when the hook is triggered.
|
||||
* @param ActionScheduler_Schedule $schedule The action's schedule.
|
||||
* @param string $group A group to put the action in.
|
||||
* phpcs:ignore Squiz.Commenting.FunctionComment.ExtraParamComment
|
||||
* @param int $priority The action priority.
|
||||
*
|
||||
* @return ActionScheduler_Action An instance of the stored action.
|
||||
*/
|
||||
public function get_stored_action( $status, $hook, array $args = array(), ActionScheduler_Schedule $schedule = null, $group = '' ) {
|
||||
// The 6th parameter ($priority) is not formally declared in the method signature to maintain compatibility with
|
||||
// third-party subclasses created before this param was added.
|
||||
$priority = func_num_args() >= 6 ? (int) func_get_arg( 5 ) : 10;
|
||||
|
||||
switch ( $status ) {
|
||||
case ActionScheduler_Store::STATUS_PENDING:
|
||||
@@ -36,17 +41,19 @@ class ActionScheduler_ActionFactory {
|
||||
$action_class = apply_filters( 'action_scheduler_stored_action_class', $action_class, $status, $hook, $args, $schedule, $group );
|
||||
|
||||
$action = new $action_class( $hook, $args, $schedule, $group );
|
||||
$action->set_priority( $priority );
|
||||
|
||||
/**
|
||||
* Allow 3rd party code to change the instantiated action for a given hook, args, schedule and group.
|
||||
*
|
||||
* @param ActionScheduler_Action $action The instantiated action.
|
||||
* @param string $hook The instantiated action's hook.
|
||||
* @param array $args The instantiated action's args.
|
||||
* @param ActionScheduler_Action $action The instantiated action.
|
||||
* @param string $hook The instantiated action's hook.
|
||||
* @param array $args The instantiated action's args.
|
||||
* @param ActionScheduler_Schedule $schedule The instantiated action's schedule.
|
||||
* @param string $group The instantiated action's group.
|
||||
* @param string $group The instantiated action's group.
|
||||
* @param int $priority The action priority.
|
||||
*/
|
||||
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group );
|
||||
return apply_filters( 'action_scheduler_stored_action_instance', $action, $hook, $args, $schedule, $group, $priority );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,9 +236,100 @@ class ActionScheduler_ActionFactory {
|
||||
$schedule_class = get_class( $schedule );
|
||||
$new_schedule = new $schedule( $next, $schedule->get_recurrence(), $schedule->get_first_date() );
|
||||
$new_action = new ActionScheduler_Action( $action->get_hook(), $action->get_args(), $new_schedule, $action->get_group() );
|
||||
$new_action->set_priority( $action->get_priority() );
|
||||
return $this->store( $new_action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a scheduled action.
|
||||
*
|
||||
* This general purpose method can be used in place of specific methods such as async(),
|
||||
* async_unique(), single() or single_unique(), etc.
|
||||
*
|
||||
* @internal Not intended for public use, should not be overriden by subclasses.
|
||||
*
|
||||
* @param array $options {
|
||||
* Describes the action we wish to schedule.
|
||||
*
|
||||
* @type string $type Must be one of 'async', 'cron', 'recurring', or 'single'.
|
||||
* @type string $hook The hook to be executed.
|
||||
* @type array $arguments Arguments to be passed to the callback.
|
||||
* @type string $group The action group.
|
||||
* @type bool $unique If the action should be unique.
|
||||
* @type int $when Timestamp. Indicates when the action, or first instance of the action in the case
|
||||
* of recurring or cron actions, becomes due.
|
||||
* @type int|string $pattern Recurrence pattern. This is either an interval in seconds for recurring actions
|
||||
* or a cron expression for cron actions.
|
||||
* @type int $priority Lower values means higher priority. Should be in the range 0-255.
|
||||
* }
|
||||
*
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
public function create( array $options = array() ) {
|
||||
$defaults = array(
|
||||
'type' => 'single',
|
||||
'hook' => '',
|
||||
'arguments' => array(),
|
||||
'group' => '',
|
||||
'unique' => false,
|
||||
'when' => time(),
|
||||
'pattern' => null,
|
||||
'priority' => 10,
|
||||
);
|
||||
|
||||
$options = array_merge( $defaults, $options );
|
||||
|
||||
// Cron/recurring actions without a pattern are treated as single actions (this gives calling code the ability
|
||||
// to use functions like as_schedule_recurring_action() to schedule recurring as well as single actions).
|
||||
if ( ( 'cron' === $options['type'] || 'recurring' === $options['type'] ) && empty( $options['pattern'] ) ) {
|
||||
$options['type'] = 'single';
|
||||
}
|
||||
|
||||
switch ( $options['type'] ) {
|
||||
case 'async':
|
||||
$schedule = new ActionScheduler_NullSchedule();
|
||||
break;
|
||||
|
||||
case 'cron':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$cron = CronExpression::factory( $options['pattern'] );
|
||||
$schedule = new ActionScheduler_CronSchedule( $date, $cron );
|
||||
break;
|
||||
|
||||
case 'recurring':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$schedule = new ActionScheduler_IntervalSchedule( $date, $options['pattern'] );
|
||||
break;
|
||||
|
||||
case 'single':
|
||||
$date = as_get_datetime_object( $options['when'] );
|
||||
$schedule = new ActionScheduler_SimpleSchedule( $date );
|
||||
break;
|
||||
|
||||
default:
|
||||
error_log( "Unknown action type '{$options['type']}' specified when trying to create an action for '{$options['hook']}'." );
|
||||
return 0;
|
||||
}
|
||||
|
||||
$action = new ActionScheduler_Action( $options['hook'], $options['arguments'], $schedule, $options['group'] );
|
||||
$action->set_priority( $options['priority'] );
|
||||
|
||||
$action_id = 0;
|
||||
try {
|
||||
$action_id = $options['unique'] ? $this->store_unique_action( $action ) : $this->store( $action );
|
||||
} catch ( Exception $e ) {
|
||||
error_log(
|
||||
sprintf(
|
||||
/* translators: %1$s is the name of the hook to be enqueued, %2$s is the exception message. */
|
||||
__( 'Caught exception while enqueuing action "%1$s": %2$s', 'action-scheduler' ),
|
||||
$options['hook'],
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
return $action_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save action to database.
|
||||
*
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* Class ActionScheduler_Compatibility
|
||||
*/
|
||||
class ActionScheduler_Compatibility {
|
||||
|
||||
/**
|
||||
* Converts a shorthand byte value to an integer byte value.
|
||||
*
|
||||
@@ -89,21 +88,18 @@ class ActionScheduler_Compatibility {
|
||||
$limit = (int) $limit;
|
||||
$max_execution_time = (int) ini_get( 'max_execution_time' );
|
||||
|
||||
/*
|
||||
* If the max execution time is already unlimited (zero), or if it exceeds or is equal to the proposed
|
||||
* limit, there is no reason for us to make further changes (we never want to lower it).
|
||||
*/
|
||||
if (
|
||||
0 === $max_execution_time
|
||||
|| ( $max_execution_time >= $limit && $limit !== 0 )
|
||||
) {
|
||||
// If the max execution time is already set to zero (unlimited), there is no reason to make a further change.
|
||||
if ( 0 === $max_execution_time ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Whichever of $max_execution_time or $limit is higher is the amount by which we raise the time limit.
|
||||
$raise_by = 0 === $limit || $limit > $max_execution_time ? $limit : $max_execution_time;
|
||||
|
||||
if ( function_exists( 'wc_set_time_limit' ) ) {
|
||||
wc_set_time_limit( $limit );
|
||||
wc_set_time_limit( $raise_by );
|
||||
} elseif ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
|
||||
@set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
@set_time_limit( $raise_by ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
*/
|
||||
protected function get_recurrence( $action ) {
|
||||
$schedule = $action->get_schedule();
|
||||
if ( $schedule->is_recurring() ) {
|
||||
if ( $schedule->is_recurring() && method_exists( $schedule, 'get_recurrence' ) ) {
|
||||
$recurrence = $schedule->get_recurrence();
|
||||
|
||||
if ( is_numeric( $recurrence ) ) {
|
||||
@@ -471,7 +471,7 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
return __( 'async', 'action-scheduler' );
|
||||
}
|
||||
|
||||
if ( ! $schedule->get_date() ) {
|
||||
if ( ! method_exists( $schedule, 'get_date' ) || ! $schedule->get_date() ) {
|
||||
return '0000-00-00 00:00:00';
|
||||
}
|
||||
|
||||
@@ -502,7 +502,20 @@ class ActionScheduler_ListTable extends ActionScheduler_Abstract_ListTable {
|
||||
*/
|
||||
protected function bulk_delete( array $ids, $ids_sql ) {
|
||||
foreach ( $ids as $id ) {
|
||||
$this->store->delete_action( $id );
|
||||
try {
|
||||
$this->store->delete_action( $id );
|
||||
} catch ( Exception $e ) {
|
||||
// A possible reason for an exception would include a scenario where the same action is deleted by a
|
||||
// concurrent request.
|
||||
error_log(
|
||||
sprintf(
|
||||
/* translators: 1: action ID 2: exception message. */
|
||||
__( 'Action Scheduler was unable to delete action %1$d. Reason: %2$s', 'action-scheduler' ),
|
||||
$id,
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,37 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
* @bool True if lock value has changed, false if not or if set failed.
|
||||
*/
|
||||
public function set( $lock_type ) {
|
||||
return update_option( $this->get_key( $lock_type ), time() + $this->get_duration( $lock_type ) );
|
||||
global $wpdb;
|
||||
|
||||
$lock_key = $this->get_key( $lock_type );
|
||||
$existing_lock_value = $this->get_existing_lock( $lock_type );
|
||||
$new_lock_value = $this->new_lock_value( $lock_type );
|
||||
|
||||
// The lock may not exist yet, or may have been deleted.
|
||||
if ( empty( $existing_lock_value ) ) {
|
||||
return (bool) $wpdb->insert(
|
||||
$wpdb->options,
|
||||
array(
|
||||
'option_name' => $lock_key,
|
||||
'option_value' => $new_lock_value,
|
||||
'autoload' => 'no',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->get_expiration_from( $existing_lock_value ) >= time() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, try to obtain the lock.
|
||||
return (bool) $wpdb->update(
|
||||
$wpdb->options,
|
||||
array( 'option_value' => $new_lock_value ),
|
||||
array(
|
||||
'option_name' => $lock_key,
|
||||
'option_value' => $existing_lock_value,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +64,30 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
* @return bool|int False if no lock is set, otherwise the timestamp for when the lock is set to expire.
|
||||
*/
|
||||
public function get_expiration( $lock_type ) {
|
||||
return get_option( $this->get_key( $lock_type ) );
|
||||
return $this->get_expiration_from( $this->get_existing_lock( $lock_type ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the lock string, derives the lock expiration timestamp (or false if it cannot be determined).
|
||||
*
|
||||
* @param string $lock_value String containing a timestamp, or pipe-separated combination of unique value and timestamp.
|
||||
*
|
||||
* @return false|int
|
||||
*/
|
||||
private function get_expiration_from( $lock_value ) {
|
||||
$lock_string = explode( '|', $lock_value );
|
||||
|
||||
// Old style lock?
|
||||
if ( count( $lock_string ) === 1 && is_numeric( $lock_string[0] ) ) {
|
||||
return (int) $lock_string[0];
|
||||
}
|
||||
|
||||
// New style lock?
|
||||
if ( count( $lock_string ) === 2 && is_numeric( $lock_string[1] ) ) {
|
||||
return (int) $lock_string[1];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,4 +99,37 @@ class ActionScheduler_OptionLock extends ActionScheduler_Lock {
|
||||
protected function get_key( $lock_type ) {
|
||||
return sprintf( 'action_scheduler_lock_%s', $lock_type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies the existing lock value, or an empty string if not set.
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_existing_lock( $lock_type ) {
|
||||
global $wpdb;
|
||||
|
||||
// Now grab the existing lock value, if there is one.
|
||||
return (string) $wpdb->get_var(
|
||||
$wpdb->prepare(
|
||||
"SELECT option_value FROM $wpdb->options WHERE option_name = %s",
|
||||
$this->get_key( $lock_type )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Supplies a lock value consisting of a unique value and the current timestamp, which are separated by a pipe
|
||||
* character.
|
||||
*
|
||||
* Example: (string) "649de012e6b262.09774912|1688068114"
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function new_lock_value( $lock_type ) {
|
||||
return uniqid( '', true ) . '|' . ( time() + $this->get_duration( $lock_type ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@ class ActionScheduler_QueueCleaner {
|
||||
*/
|
||||
private $month_in_seconds = 2678400;
|
||||
|
||||
/**
|
||||
* @var string[] Default list of statuses purged by the cleaner process.
|
||||
*/
|
||||
private $default_statuses_to_purge = [
|
||||
ActionScheduler_Store::STATUS_COMPLETE,
|
||||
ActionScheduler_Store::STATUS_CANCELED,
|
||||
];
|
||||
|
||||
/**
|
||||
* ActionScheduler_QueueCleaner constructor.
|
||||
*
|
||||
@@ -29,46 +37,113 @@ class ActionScheduler_QueueCleaner {
|
||||
$this->batch_size = $batch_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default queue cleaner process used by queue runner.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function delete_old_actions() {
|
||||
/**
|
||||
* Filter the minimum scheduled date age for action deletion.
|
||||
*
|
||||
* @param int $retention_period Minimum scheduled age in seconds of the actions to be deleted.
|
||||
*/
|
||||
$lifespan = apply_filters( 'action_scheduler_retention_period', $this->month_in_seconds );
|
||||
$cutoff = as_get_datetime_object($lifespan.' seconds ago');
|
||||
|
||||
$statuses_to_purge = array(
|
||||
ActionScheduler_Store::STATUS_COMPLETE,
|
||||
ActionScheduler_Store::STATUS_CANCELED,
|
||||
);
|
||||
try {
|
||||
$cutoff = as_get_datetime_object( $lifespan . ' seconds ago' );
|
||||
} catch ( Exception $e ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* Translators: %s is the exception message. */
|
||||
esc_html__( 'It was not possible to determine a valid cut-off time: %s.', 'action-scheduler' ),
|
||||
esc_html( $e->getMessage() )
|
||||
),
|
||||
'3.5.5'
|
||||
);
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter the statuses when cleaning the queue.
|
||||
*
|
||||
* @param string[] $default_statuses_to_purge Action statuses to clean.
|
||||
*/
|
||||
$statuses_to_purge = (array) apply_filters( 'action_scheduler_default_cleaner_statuses', $this->default_statuses_to_purge );
|
||||
|
||||
return $this->clean_actions( $statuses_to_purge, $cutoff, $this->get_batch_size() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete selected actions limited by status and date.
|
||||
*
|
||||
* @param string[] $statuses_to_purge List of action statuses to purge. Defaults to canceled, complete.
|
||||
* @param DateTime $cutoff_date Date limit for selecting actions. Defaults to 31 days ago.
|
||||
* @param int|null $batch_size Maximum number of actions per status to delete. Defaults to 20.
|
||||
* @param string $context Calling process context. Defaults to `old`.
|
||||
* @return array Actions deleted.
|
||||
*/
|
||||
public function clean_actions( array $statuses_to_purge, DateTime $cutoff_date, $batch_size = null, $context = 'old' ) {
|
||||
$batch_size = $batch_size !== null ? $batch_size : $this->batch_size;
|
||||
$cutoff = $cutoff_date !== null ? $cutoff_date : as_get_datetime_object( $this->month_in_seconds . ' seconds ago' );
|
||||
$lifespan = time() - $cutoff->getTimestamp();
|
||||
if ( empty( $statuses_to_purge ) ) {
|
||||
$statuses_to_purge = $this->default_statuses_to_purge;
|
||||
}
|
||||
|
||||
$deleted_actions = [];
|
||||
foreach ( $statuses_to_purge as $status ) {
|
||||
$actions_to_delete = $this->store->query_actions( array(
|
||||
'status' => $status,
|
||||
'modified' => $cutoff,
|
||||
'modified_compare' => '<=',
|
||||
'per_page' => $this->get_batch_size(),
|
||||
'per_page' => $batch_size,
|
||||
'orderby' => 'none',
|
||||
) );
|
||||
|
||||
foreach ( $actions_to_delete as $action_id ) {
|
||||
try {
|
||||
$this->store->delete_action( $action_id );
|
||||
} catch ( Exception $e ) {
|
||||
$deleted_actions = array_merge( $deleted_actions, $this->delete_actions( $actions_to_delete, $lifespan, $context ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify 3rd party code of exceptions when deleting a completed action older than the retention period
|
||||
*
|
||||
* This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their
|
||||
* actions.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param int $action_id The scheduled actions ID in the data store
|
||||
* @param Exception $e The exception thrown when attempting to delete the action from the data store
|
||||
* @param int $lifespan The retention period, in seconds, for old actions
|
||||
* @param int $count_of_actions_to_delete The number of old actions being deleted in this batch
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_old_action_deletion', $action_id, $e, $lifespan, count( $actions_to_delete ) );
|
||||
}
|
||||
return $deleted_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $actions_to_delete List of action IDs to delete.
|
||||
* @param int $lifespan Minimum scheduled age in seconds of the actions being deleted.
|
||||
* @param string $context Context of the delete request.
|
||||
* @return array Deleted action IDs.
|
||||
*/
|
||||
private function delete_actions( array $actions_to_delete, $lifespan = null, $context = 'old' ) {
|
||||
$deleted_actions = [];
|
||||
if ( $lifespan === null ) {
|
||||
$lifespan = $this->month_in_seconds;
|
||||
}
|
||||
|
||||
foreach ( $actions_to_delete as $action_id ) {
|
||||
try {
|
||||
$this->store->delete_action( $action_id );
|
||||
$deleted_actions[] = $action_id;
|
||||
} catch ( Exception $e ) {
|
||||
/**
|
||||
* Notify 3rd party code of exceptions when deleting a completed action older than the retention period
|
||||
*
|
||||
* This hook provides a way for 3rd party code to log or otherwise handle exceptions relating to their
|
||||
* actions.
|
||||
*
|
||||
* @param int $action_id The scheduled actions ID in the data store
|
||||
* @param Exception $e The exception thrown when attempting to delete the action from the data store
|
||||
* @param int $lifespan The retention period, in seconds, for old actions
|
||||
* @param int $count_of_actions_to_delete The number of old actions being deleted in this batch
|
||||
* @since 2.0.0
|
||||
*
|
||||
*/
|
||||
do_action( "action_scheduler_failed_{$context}_action_deletion", $action_id, $e, $lifespan, count( $actions_to_delete ) );
|
||||
}
|
||||
}
|
||||
return $deleted_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,9 +103,12 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
||||
* should dispatch a request to process pending actions.
|
||||
*/
|
||||
public function maybe_dispatch_async_request() {
|
||||
if ( is_admin() && ! ActionScheduler::lock()->is_locked( 'async-request-runner' ) ) {
|
||||
// Only start an async queue at most once every 60 seconds
|
||||
ActionScheduler::lock()->set( 'async-request-runner' );
|
||||
// Only start an async queue at most once every 60 seconds.
|
||||
if (
|
||||
is_admin()
|
||||
&& ! ActionScheduler::lock()->is_locked( 'async-request-runner' )
|
||||
&& ActionScheduler::lock()->set( 'async-request-runner' )
|
||||
) {
|
||||
$this->async_request->maybe_dispatch();
|
||||
}
|
||||
}
|
||||
@@ -185,9 +188,15 @@ class ActionScheduler_QueueRunner extends ActionScheduler_Abstract_QueueRunner {
|
||||
protected function clear_caches() {
|
||||
/*
|
||||
* Calling wp_cache_flush_runtime() lets us clear the runtime cache without invalidating the external object
|
||||
* cache, so we will always prefer this when it is available (but it was only introduced in WordPress 6.0).
|
||||
* cache, so we will always prefer this method (as compared to calling wp_cache_flush()) when it is available.
|
||||
*
|
||||
* However, this function was only introduced in WordPress 6.0. Additionally, the preferred way of detecting if
|
||||
* it is supported changed in WordPress 6.1 so we use two different methods to decide if we should utilize it.
|
||||
*/
|
||||
if ( function_exists( 'wp_cache_flush_runtime' ) ) {
|
||||
$flushing_runtime_cache_explicitly_supported = function_exists( 'wp_cache_supports' ) && wp_cache_supports( 'flush_runtime' );
|
||||
$flushing_runtime_cache_implicitly_supported = ! function_exists( 'wp_cache_supports' ) && function_exists( 'wp_cache_flush_runtime' );
|
||||
|
||||
if ( $flushing_runtime_cache_explicitly_supported || $flushing_runtime_cache_implicitly_supported ) {
|
||||
wp_cache_flush_runtime();
|
||||
} elseif (
|
||||
! wp_using_ext_object_cache()
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Commands for Action Scheduler.
|
||||
*/
|
||||
class ActionScheduler_WPCLI_Clean_Command extends WP_CLI_Command {
|
||||
/**
|
||||
* Run the Action Scheduler Queue Cleaner
|
||||
*
|
||||
* ## OPTIONS
|
||||
*
|
||||
* [--batch-size=<size>]
|
||||
* : The maximum number of actions to delete per batch. Defaults to 20.
|
||||
*
|
||||
* [--batches=<size>]
|
||||
* : Limit execution to a number of batches. Defaults to 0, meaning batches will continue all eligible actions are deleted.
|
||||
*
|
||||
* [--status=<status>]
|
||||
* : Only clean actions with the specified status. Defaults to Canceled, Completed. Define multiple statuses as a comma separated string (without spaces), e.g. `--status=complete,failed,canceled`
|
||||
*
|
||||
* [--before=<datestring>]
|
||||
* : Only delete actions with scheduled date older than this. Defaults to 31 days. e.g `--before='7 days ago'`, `--before='02-Feb-2020 20:20:20'`
|
||||
*
|
||||
* [--pause=<seconds>]
|
||||
* : The number of seconds to pause between batches. Default no pause.
|
||||
*
|
||||
* @param array $args Positional arguments.
|
||||
* @param array $assoc_args Keyed arguments.
|
||||
* @throws \WP_CLI\ExitException When an error occurs.
|
||||
*
|
||||
* @subcommand clean
|
||||
*/
|
||||
public function clean( $args, $assoc_args ) {
|
||||
// Handle passed arguments.
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 20 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$status = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'status', '' ) );
|
||||
$status = array_filter( array_map( 'trim', $status ) );
|
||||
$before = \WP_CLI\Utils\get_flag_value( $assoc_args, 'before', '' );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
|
||||
$batches_completed = 0;
|
||||
$actions_deleted = 0;
|
||||
$unlimited = $batches === 0;
|
||||
try {
|
||||
$lifespan = as_get_datetime_object( $before );
|
||||
} catch ( Exception $e ) {
|
||||
$lifespan = null;
|
||||
}
|
||||
|
||||
try {
|
||||
// Custom queue cleaner instance.
|
||||
$cleaner = new ActionScheduler_QueueCleaner( null, $batch );
|
||||
|
||||
// Clean actions for as long as possible.
|
||||
while ( $unlimited || $batches_completed < $batches ) {
|
||||
if ( $sleep && $batches_completed > 0 ) {
|
||||
sleep( $sleep );
|
||||
}
|
||||
|
||||
$deleted = count( $cleaner->clean_actions( $status, $lifespan, null,'CLI' ) );
|
||||
if ( $deleted <= 0 ) {
|
||||
break;
|
||||
}
|
||||
$actions_deleted += $deleted;
|
||||
$batches_completed++;
|
||||
$this->print_success( $deleted );
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$this->print_error( $e );
|
||||
}
|
||||
|
||||
$this->print_total_batches( $batches_completed );
|
||||
if ( $batches_completed > 1 ) {
|
||||
$this->print_success( $actions_deleted );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WP CLI message about how many batches of actions were processed.
|
||||
*
|
||||
* @param int $batches_processed
|
||||
*/
|
||||
protected function print_total_batches( int $batches_processed ) {
|
||||
WP_CLI::log(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of batches processed */
|
||||
_n( '%d batch processed.', '%d batches processed.', $batches_processed, 'action-scheduler' ),
|
||||
$batches_processed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an exception into a WP CLI error.
|
||||
*
|
||||
* @param Exception $e The error object.
|
||||
*
|
||||
* @throws \WP_CLI\ExitException
|
||||
*/
|
||||
protected function print_error( Exception $e ) {
|
||||
WP_CLI::error(
|
||||
sprintf(
|
||||
/* translators: %s refers to the exception error message */
|
||||
__( 'There was an error deleting an action: %s', 'action-scheduler' ),
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a success message with the number of completed actions.
|
||||
*
|
||||
* @param int $actions_deleted
|
||||
*/
|
||||
protected function print_success( int $actions_deleted ) {
|
||||
WP_CLI::success(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of actions deleted */
|
||||
_n( '%d action deleted.', '%d actions deleted.', $actions_deleted, 'action-scheduler' ),
|
||||
$actions_deleted
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ class ActionScheduler_WPCLI_QueueRunner extends ActionScheduler_Abstract_QueueRu
|
||||
$count = count( $this->actions );
|
||||
$this->progress_bar = new ProgressBar(
|
||||
/* translators: %d: amount of actions */
|
||||
sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), number_format_i18n( $count ) ),
|
||||
sprintf( _n( 'Running %d action', 'Running %d actions', $count, 'action-scheduler' ), $count ),
|
||||
$count
|
||||
);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
* [--group=<group>]
|
||||
* : Only run actions from the specified group. Omitting this option runs actions from all groups.
|
||||
*
|
||||
* [--exclude-groups=<groups>]
|
||||
* : Run actions from all groups except the specified group(s). Define multiple groups as a comma separated string (without spaces), e.g. '--group_a,group_b'. This option is ignored when `--group` is used.
|
||||
*
|
||||
* [--free-memory-on=<count>]
|
||||
* : The number of actions to process between freeing memory. 0 disables freeing memory. Default 50.
|
||||
*
|
||||
@@ -72,15 +75,16 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
*/
|
||||
public function run( $args, $assoc_args ) {
|
||||
// Handle passed arguments.
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) );
|
||||
$hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) );
|
||||
$hooks = array_filter( array_map( 'trim', $hooks ) );
|
||||
$group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' );
|
||||
$free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
$force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
|
||||
$batch = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batch-size', 100 ) );
|
||||
$batches = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'batches', 0 ) );
|
||||
$clean = absint( \WP_CLI\Utils\get_flag_value( $assoc_args, 'cleanup-batch-size', $batch ) );
|
||||
$hooks = explode( ',', WP_CLI\Utils\get_flag_value( $assoc_args, 'hooks', '' ) );
|
||||
$hooks = array_filter( array_map( 'trim', $hooks ) );
|
||||
$group = \WP_CLI\Utils\get_flag_value( $assoc_args, 'group', '' );
|
||||
$exclude_groups = \WP_CLI\Utils\get_flag_value( $assoc_args, 'exclude-groups', '' );
|
||||
$free_on = \WP_CLI\Utils\get_flag_value( $assoc_args, 'free-memory-on', 50 );
|
||||
$sleep = \WP_CLI\Utils\get_flag_value( $assoc_args, 'pause', 0 );
|
||||
$force = \WP_CLI\Utils\get_flag_value( $assoc_args, 'force', false );
|
||||
|
||||
ActionScheduler_DataController::set_free_ticks( $free_on );
|
||||
ActionScheduler_DataController::set_sleep_time( $sleep );
|
||||
@@ -88,6 +92,13 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
$batches_completed = 0;
|
||||
$actions_completed = 0;
|
||||
$unlimited = $batches === 0;
|
||||
if ( is_callable( [ ActionScheduler::store(), 'set_claim_filter' ] ) ) {
|
||||
$exclude_groups = $this->parse_comma_separated_string( $exclude_groups );
|
||||
|
||||
if ( ! empty( $exclude_groups ) ) {
|
||||
ActionScheduler::store()->set_claim_filter('exclude-groups', $exclude_groups );
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Custom queue cleaner instance.
|
||||
@@ -116,6 +127,17 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
$this->print_success( $actions_completed );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string of comma-separated values into an array of those same values.
|
||||
*
|
||||
* @param string $string The string of one or more comma separated values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function parse_comma_separated_string( $string ): array {
|
||||
return array_filter( str_getcsv( $string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WP CLI message about how many actions are about to be processed.
|
||||
*
|
||||
@@ -126,9 +148,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
protected function print_total_actions( $total ) {
|
||||
WP_CLI::log(
|
||||
sprintf(
|
||||
/* translators: %d refers to how many scheduled taks were found to run */
|
||||
/* translators: %d refers to how many scheduled tasks were found to run */
|
||||
_n( 'Found %d scheduled task', 'Found %d scheduled tasks', $total, 'action-scheduler' ),
|
||||
number_format_i18n( $total )
|
||||
$total
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -145,7 +167,7 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of batches executed */
|
||||
_n( '%d batch executed.', '%d batches executed.', $batches_completed, 'action-scheduler' ),
|
||||
number_format_i18n( $batches_completed )
|
||||
$batches_completed
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -179,9 +201,9 @@ class ActionScheduler_WPCLI_Scheduler_command extends WP_CLI_Command {
|
||||
protected function print_success( $actions_completed ) {
|
||||
WP_CLI::success(
|
||||
sprintf(
|
||||
/* translators: %d refers to the total number of taskes completed */
|
||||
/* translators: %d refers to the total number of tasks completed */
|
||||
_n( '%d scheduled task completed.', '%d scheduled tasks completed.', $actions_completed, 'action-scheduler' ),
|
||||
number_format_i18n( $actions_completed )
|
||||
$actions_completed
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -153,11 +153,41 @@ abstract class ActionScheduler {
|
||||
add_action( 'init', array( $store, 'init' ), 1, 0 );
|
||||
add_action( 'init', array( $logger, 'init' ), 1, 0 );
|
||||
add_action( 'init', array( $runner, 'init' ), 1, 0 );
|
||||
|
||||
add_action(
|
||||
'init',
|
||||
/**
|
||||
* Runs after the active store's init() method has been called.
|
||||
*
|
||||
* It would probably be preferable to have $store->init() (or it's parent method) set this itself,
|
||||
* once it has initialized, however that would cause problems in cases where a custom data store is in
|
||||
* use and it has not yet been updated to follow that same logic.
|
||||
*/
|
||||
function () {
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Fires when Action Scheduler is ready: it is safe to use the procedural API after this point.
|
||||
*
|
||||
* @since 3.5.5
|
||||
*/
|
||||
do_action( 'action_scheduler_init' );
|
||||
},
|
||||
1
|
||||
);
|
||||
} else {
|
||||
$admin_view->init();
|
||||
$store->init();
|
||||
$logger->init();
|
||||
$runner->init();
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Fires when Action Scheduler is ready: it is safe to use the procedural API after this point.
|
||||
*
|
||||
* @since 3.5.5
|
||||
*/
|
||||
do_action( 'action_scheduler_init' );
|
||||
}
|
||||
|
||||
if ( apply_filters( 'action_scheduler_load_deprecated_functions', true ) ) {
|
||||
@@ -166,14 +196,13 @@ abstract class ActionScheduler {
|
||||
|
||||
if ( defined( 'WP_CLI' ) && WP_CLI ) {
|
||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Scheduler_command' );
|
||||
WP_CLI::add_command( 'action-scheduler', 'ActionScheduler_WPCLI_Clean_Command' );
|
||||
if ( ! ActionScheduler_DataController::is_migration_complete() && Controller::instance()->allow_migration() ) {
|
||||
$command = new Migration_Command();
|
||||
$command->register();
|
||||
}
|
||||
}
|
||||
|
||||
self::$data_store_initialized = true;
|
||||
|
||||
/**
|
||||
* Handle WP comment cleanup after migration.
|
||||
*/
|
||||
@@ -192,8 +221,12 @@ abstract class ActionScheduler {
|
||||
*/
|
||||
public static function is_initialized( $function_name = null ) {
|
||||
if ( ! self::$data_store_initialized && ! empty( $function_name ) ) {
|
||||
$message = sprintf( __( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ), esc_attr( $function_name ) );
|
||||
error_log( $message, E_WARNING );
|
||||
$message = sprintf(
|
||||
/* translators: %s function name. */
|
||||
__( '%s() was called before the Action Scheduler data store was initialized', 'action-scheduler' ),
|
||||
esc_attr( $function_name )
|
||||
);
|
||||
_doing_it_wrong( $function_name, $message, '3.1.6' );
|
||||
}
|
||||
|
||||
return self::$data_store_initialized;
|
||||
|
||||
@@ -673,24 +673,34 @@ abstract class ActionScheduler_Abstract_ListTable extends WP_List_Table {
|
||||
|
||||
// Helper to set 'all' filter when not set on status counts passed in.
|
||||
if ( ! isset( $this->status_counts['all'] ) ) {
|
||||
$this->status_counts = array( 'all' => array_sum( $this->status_counts ) ) + $this->status_counts;
|
||||
$all_count = array_sum( $this->status_counts );
|
||||
if ( isset( $this->status_counts['past-due'] ) ) {
|
||||
$all_count -= $this->status_counts['past-due'];
|
||||
}
|
||||
$this->status_counts = array( 'all' => $all_count ) + $this->status_counts;
|
||||
}
|
||||
|
||||
foreach ( $this->status_counts as $status_name => $count ) {
|
||||
// Translated status labels.
|
||||
$status_labels = ActionScheduler_Store::instance()->get_status_labels();
|
||||
$status_labels['all'] = _x( 'All', 'status labels', 'action-scheduler' );
|
||||
$status_labels['past-due'] = _x( 'Past-due', 'status labels', 'action-scheduler' );
|
||||
|
||||
foreach ( $this->status_counts as $status_slug => $count ) {
|
||||
|
||||
if ( 0 === $count ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $status_name === $request_status || ( empty( $request_status ) && 'all' === $status_name ) ) {
|
||||
if ( $status_slug === $request_status || ( empty( $request_status ) && 'all' === $status_slug ) ) {
|
||||
$status_list_item = '<li class="%1$s"><a href="%2$s" class="current">%3$s</a> (%4$d)</li>';
|
||||
} else {
|
||||
$status_list_item = '<li class="%1$s"><a href="%2$s">%3$s</a> (%4$d)</li>';
|
||||
}
|
||||
|
||||
$status_filter_url = ( 'all' === $status_name ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_name );
|
||||
$status_name = isset( $status_labels[ $status_slug ] ) ? $status_labels[ $status_slug ] : ucfirst( $status_slug );
|
||||
$status_filter_url = ( 'all' === $status_slug ) ? remove_query_arg( 'status' ) : add_query_arg( 'status', $status_slug );
|
||||
$status_filter_url = remove_query_arg( array( 'paged', 's' ), $status_filter_url );
|
||||
$status_list_items[] = sprintf( $status_list_item, esc_attr( $status_name ), esc_url( $status_filter_url ), esc_html( ucfirst( $status_name ) ), absint( $count ) );
|
||||
$status_list_items[] = sprintf( $status_list_item, esc_attr( $status_slug ), esc_url( $status_filter_url ), esc_html( $status_name ), absint( $count ) );
|
||||
}
|
||||
|
||||
if ( $status_list_items ) {
|
||||
|
||||
@@ -48,30 +48,56 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
* Generally, this should be capitalised and not localised as it's a proper noun.
|
||||
*/
|
||||
public function process_action( $action_id, $context = '' ) {
|
||||
// Temporarily override the error handler while we process the current action.
|
||||
set_error_handler(
|
||||
/**
|
||||
* Temporary error handler which can catch errors and convert them into exceptions. This faciliates more
|
||||
* robust error handling across all supported PHP versions.
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @param int $type Error level expressed as an integer.
|
||||
* @param string $message Error message.
|
||||
*/
|
||||
function ( $type, $message ) {
|
||||
throw new Exception( $message );
|
||||
},
|
||||
E_USER_ERROR | E_RECOVERABLE_ERROR
|
||||
);
|
||||
|
||||
/*
|
||||
* The nested try/catch structure is required because we potentially need to convert thrown errors into
|
||||
* exceptions (and an exception thrown from a catch block cannot be caught by a later catch block in the *same*
|
||||
* structure).
|
||||
*/
|
||||
try {
|
||||
$valid_action = false;
|
||||
do_action( 'action_scheduler_before_execute', $action_id, $context );
|
||||
try {
|
||||
$valid_action = false;
|
||||
do_action( 'action_scheduler_before_execute', $action_id, $context );
|
||||
|
||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
||||
do_action( 'action_scheduler_execution_ignored', $action_id, $context );
|
||||
return;
|
||||
if ( ActionScheduler_Store::STATUS_PENDING !== $this->store->get_status( $action_id ) ) {
|
||||
do_action( 'action_scheduler_execution_ignored', $action_id, $context );
|
||||
return;
|
||||
}
|
||||
|
||||
$valid_action = true;
|
||||
do_action( 'action_scheduler_begin_execute', $action_id, $context );
|
||||
|
||||
$action = $this->store->fetch_action( $action_id );
|
||||
$this->store->log_execution( $action_id );
|
||||
$action->execute();
|
||||
do_action( 'action_scheduler_after_execute', $action_id, $action, $context );
|
||||
$this->store->mark_complete( $action_id );
|
||||
} catch ( Throwable $e ) {
|
||||
// Throwable is defined when executing under PHP 7.0 and up. We convert it to an exception, for
|
||||
// compatibility with ActionScheduler_Logger.
|
||||
throw new Exception( $e->getMessage(), $e->getCode(), $e );
|
||||
}
|
||||
|
||||
$valid_action = true;
|
||||
do_action( 'action_scheduler_begin_execute', $action_id, $context );
|
||||
|
||||
$action = $this->store->fetch_action( $action_id );
|
||||
$this->store->log_execution( $action_id );
|
||||
$action->execute();
|
||||
do_action( 'action_scheduler_after_execute', $action_id, $action, $context );
|
||||
$this->store->mark_complete( $action_id );
|
||||
} catch ( Exception $e ) {
|
||||
if ( $valid_action ) {
|
||||
$this->store->mark_failure( $action_id );
|
||||
do_action( 'action_scheduler_failed_execution', $action_id, $e, $context );
|
||||
} else {
|
||||
do_action( 'action_scheduler_failed_validation', $action_id, $e, $context );
|
||||
}
|
||||
// This catch block exists for compatibility with PHP 5.6.
|
||||
$this->handle_action_error( $action_id, $e, $context, $valid_action );
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ( isset( $action ) && is_a( $action, 'ActionScheduler_Action' ) && $action->get_schedule()->is_recurring() ) {
|
||||
@@ -79,6 +105,39 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks actions as either having failed execution or failed validation, as appropriate.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
* @param bool $valid_action If the action is valid.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function handle_action_error( $action_id, $e, $context, $valid_action ) {
|
||||
if ( $valid_action ) {
|
||||
$this->store->mark_failure( $action_id );
|
||||
/**
|
||||
* Runs when action execution fails.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_execution', $action_id, $e, $context );
|
||||
} else {
|
||||
/**
|
||||
* Runs when action validation fails.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
* @param Exception $e Exception instance.
|
||||
* @param string $context Execution context.
|
||||
*/
|
||||
do_action( 'action_scheduler_failed_validation', $action_id, $e, $context );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the next instance of the action if necessary.
|
||||
*
|
||||
@@ -143,12 +202,22 @@ abstract class ActionScheduler_Abstract_QueueRunner extends ActionScheduler_Abst
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now let's fetch the first action (having the same hook) of *any status*ithin the same window.
|
||||
// Now let's fetch the first action (having the same hook) of *any status* within the same window.
|
||||
unset( $query_args['status'] );
|
||||
$first_action_id_with_the_same_hook = $this->store->query_actions( $query_args );
|
||||
|
||||
// If the IDs match, then actions for this hook must be consistently failing.
|
||||
return $first_action_id_with_the_same_hook === $first_failing_action_id;
|
||||
/**
|
||||
* If a recurring action is assessed as consistently failing, it will not be rescheduled. This hook provides a
|
||||
* way to observe and optionally override that assessment.
|
||||
*
|
||||
* @param bool $is_consistently_failing If the action is considered to be consistently failing.
|
||||
* @param ActionScheduler_Action $action The action being assessed.
|
||||
*/
|
||||
return (bool) apply_filters(
|
||||
'action_scheduler_recurring_action_is_consistently_failing',
|
||||
$first_action_id_with_the_same_hook === $first_failing_action_id,
|
||||
$action
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
/**
|
||||
* @var array Names of tables that will be registered by this class.
|
||||
*/
|
||||
protected $tables = [];
|
||||
protected $tables = array();
|
||||
|
||||
/**
|
||||
* Can optionally be used by concrete classes to carry out additional initialization work
|
||||
@@ -90,10 +90,10 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
$plugin_option_name = 'schema-';
|
||||
|
||||
switch ( static::class ) {
|
||||
case 'ActionScheduler_StoreSchema' :
|
||||
case 'ActionScheduler_StoreSchema':
|
||||
$plugin_option_name .= 'Action_Scheduler\Custom_Tables\DB_Store_Table_Maker';
|
||||
break;
|
||||
case 'ActionScheduler_LoggerSchema' :
|
||||
case 'ActionScheduler_LoggerSchema':
|
||||
$plugin_option_name .= 'Action_Scheduler\Custom_Tables\DB_Logger_Table_Maker';
|
||||
break;
|
||||
}
|
||||
@@ -129,7 +129,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
* @return void
|
||||
*/
|
||||
private function update_table( $table ) {
|
||||
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
$definition = $this->get_table_definition( $table );
|
||||
if ( $definition ) {
|
||||
$updated = dbDelta( $definition );
|
||||
@@ -148,7 +148,7 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
* table prefix for the current blog
|
||||
*/
|
||||
protected function get_full_table_name( $table ) {
|
||||
return $GLOBALS[ 'wpdb' ]->prefix . $table;
|
||||
return $GLOBALS['wpdb']->prefix . $table;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,14 +159,19 @@ abstract class ActionScheduler_Abstract_Schema {
|
||||
public function tables_exist() {
|
||||
global $wpdb;
|
||||
|
||||
$existing_tables = $wpdb->get_col( 'SHOW TABLES' );
|
||||
$expected_tables = array_map(
|
||||
function ( $table_name ) use ( $wpdb ) {
|
||||
return $wpdb->prefix . $table_name;
|
||||
},
|
||||
$this->tables
|
||||
);
|
||||
$tables_exist = true;
|
||||
|
||||
return count( array_intersect( $existing_tables, $expected_tables ) ) === count( $expected_tables );
|
||||
foreach ( $this->tables as $table_name ) {
|
||||
$table_name = $wpdb->prefix . $table_name;
|
||||
$pattern = str_replace( '_', '\\_', $table_name );
|
||||
$existing_table = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $pattern ) );
|
||||
|
||||
if ( $existing_table !== $table_name ) {
|
||||
$tables_exist = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $tables_exist;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ abstract class ActionScheduler_Lock {
|
||||
/**
|
||||
* Set a lock.
|
||||
*
|
||||
* To prevent race conditions, implementations should avoid setting the lock if the lock is already held.
|
||||
*
|
||||
* @param string $lock_type A string to identify different lock types.
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@@ -347,7 +347,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
|
||||
'hook' => $hook,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'per_page' => 1000,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -372,7 +372,7 @@ abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
|
||||
'group' => $group,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'per_page' => 1000,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ class ActionScheduler_Action {
|
||||
protected $schedule = NULL;
|
||||
protected $group = '';
|
||||
|
||||
/**
|
||||
* Priorities are conceptually similar to those used for regular WordPress actions.
|
||||
* Like those, a lower priority takes precedence over a higher priority and the default
|
||||
* is 10.
|
||||
*
|
||||
* Unlike regular WordPress actions, the priority of a scheduled action is strictly an
|
||||
* integer and should be kept within the bounds 0-255 (anything outside the bounds will
|
||||
* be brought back into the acceptable range).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $priority = 10;
|
||||
|
||||
public function __construct( $hook, array $args = array(), ActionScheduler_Schedule $schedule = NULL, $group = '' ) {
|
||||
$schedule = empty( $schedule ) ? new ActionScheduler_NullSchedule() : $schedule;
|
||||
$this->set_hook($hook);
|
||||
@@ -93,4 +106,30 @@ class ActionScheduler_Action {
|
||||
public function is_finished() {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of the action.
|
||||
*
|
||||
* @param int $priority Priority level (lower is higher priority). Should be in the range 0-255.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_priority( $priority ) {
|
||||
if ( $priority < 0 ) {
|
||||
$priority = 0;
|
||||
} elseif ( $priority > 255 ) {
|
||||
$priority = 255;
|
||||
}
|
||||
|
||||
$this->priority = (int) $priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the action priority.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function get_priority() {
|
||||
return $this->priority;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class ActionScheduler_DBLogger extends ActionScheduler_Logger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the an action's log entries from the database.
|
||||
* Retrieve an action's log entries from the database.
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
*
|
||||
|
||||
@@ -25,6 +25,13 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
|
||||
/** @var int */
|
||||
protected static $max_index_length = 191;
|
||||
|
||||
/** @var array List of claim filters. */
|
||||
protected $claim_filters = [
|
||||
'group' => '',
|
||||
'hooks' => '',
|
||||
'exclude-groups' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Initialize the data store
|
||||
*
|
||||
@@ -84,7 +91,8 @@ class ActionScheduler_DBStore extends ActionScheduler_Store {
|
||||
'scheduled_date_gmt' => $this->get_scheduled_date_string( $action, $date ),
|
||||
'scheduled_date_local' => $this->get_scheduled_date_string_local( $action, $date ),
|
||||
'schedule' => serialize( $action->get_schedule() ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
|
||||
'group_id' => $this->get_group_id( $action->get_group() ),
|
||||
'group_id' => current( $this->get_group_ids( $action->get_group() ) ),
|
||||
'priority' => $action->get_priority(),
|
||||
);
|
||||
|
||||
$args = wp_json_encode( $action->get_args() );
|
||||
@@ -172,6 +180,7 @@ WHERE ( $where_clause ) IS NULL",
|
||||
ActionScheduler_Store::STATUS_RUNNING,
|
||||
);
|
||||
$pending_status_placeholders = implode( ', ', array_fill( 0, count( $pending_statuses ), '%s' ) );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pending_status_placeholders is hardcoded.
|
||||
$where_clause = $wpdb->prepare(
|
||||
"
|
||||
@@ -242,23 +251,35 @@ AND `group_id` = %d
|
||||
/**
|
||||
* Get a group's ID based on its name/slug.
|
||||
*
|
||||
* @param string $slug The string name of a group.
|
||||
* @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.
|
||||
* @param string|array $slugs The string name of a group, or names for several groups.
|
||||
* @param bool $create_if_not_exists Whether to create the group if it does not already exist. Default, true - create the group.
|
||||
*
|
||||
* @return int The group's ID, if it exists or is created, or 0 if it does not exist and is not created.
|
||||
* @return array The group IDs, if they exist or were successfully created. May be empty.
|
||||
*/
|
||||
protected function get_group_id( $slug, $create_if_not_exists = true ) {
|
||||
if ( empty( $slug ) ) {
|
||||
return 0;
|
||||
}
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
$group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) );
|
||||
if ( empty( $group_id ) && $create_if_not_exists ) {
|
||||
$group_id = $this->create_group( $slug );
|
||||
protected function get_group_ids( $slugs, $create_if_not_exists = true ) {
|
||||
$slugs = (array) $slugs;
|
||||
$group_ids = array();
|
||||
|
||||
if ( empty( $slugs ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
foreach ( $slugs as $slug ) {
|
||||
$group_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT group_id FROM {$wpdb->actionscheduler_groups} WHERE slug=%s", $slug ) );
|
||||
|
||||
if ( empty( $group_id ) && $create_if_not_exists ) {
|
||||
$group_id = $this->create_group( $slug );
|
||||
}
|
||||
|
||||
if ( $group_id ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $group_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -355,7 +376,7 @@ AND `group_id` = %d
|
||||
}
|
||||
$group = $data->group ? $data->group : '';
|
||||
|
||||
return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group );
|
||||
return ActionScheduler::factory()->get_stored_action( $data->status, $data->hook, $args, $schedule, $group, $data->priority );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,7 +705,7 @@ AND `group_id` = %d
|
||||
array(
|
||||
'per_page' => 1000,
|
||||
'status' => self::STATUS_PENDING,
|
||||
'orderby' => 'action_id',
|
||||
'orderby' => 'none',
|
||||
)
|
||||
);
|
||||
|
||||
@@ -796,6 +817,33 @@ AND `group_id` = %d
|
||||
return $wpdb->insert_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a claim filter.
|
||||
*
|
||||
* @param string $filter_name Claim filter name.
|
||||
* @param mixed $filter_values Values to filter.
|
||||
* @return void
|
||||
*/
|
||||
public function set_claim_filter( $filter_name, $filter_values ) {
|
||||
if ( isset( $this->claim_filters[ $filter_name ] ) ) {
|
||||
$this->claim_filters[ $filter_name ] = $filter_values;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the claim filter value.
|
||||
*
|
||||
* @param string $filter_name Claim filter name.
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_claim_filter( $filter_name ) {
|
||||
if ( isset( $this->claim_filters[ $filter_name ] ) ) {
|
||||
return $this->claim_filters[ $filter_name ];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark actions claimed.
|
||||
*
|
||||
@@ -813,9 +861,8 @@ AND `group_id` = %d
|
||||
/** @var \wpdb $wpdb */
|
||||
global $wpdb;
|
||||
|
||||
$now = as_get_datetime_object();
|
||||
$date = is_null( $before_date ) ? $now : clone $before_date;
|
||||
|
||||
$now = as_get_datetime_object();
|
||||
$date = is_null( $before_date ) ? $now : clone $before_date;
|
||||
// can't use $wpdb->update() because of the <= condition.
|
||||
$update = "UPDATE {$wpdb->actionscheduler_actions} SET claim_id=%d, last_attempt_gmt=%s, last_attempt_local=%s";
|
||||
$params = array(
|
||||
@@ -824,6 +871,18 @@ AND `group_id` = %d
|
||||
current_time( 'mysql' ),
|
||||
);
|
||||
|
||||
// Set claim filters.
|
||||
if ( ! empty( $hooks ) ) {
|
||||
$this->set_claim_filter( 'hooks', $hooks );
|
||||
} else {
|
||||
$hooks = $this->get_claim_filter( 'hooks' );
|
||||
}
|
||||
if ( ! empty( $group ) ) {
|
||||
$this->set_claim_filter( 'group', $group );
|
||||
} else {
|
||||
$group = $this->get_claim_filter( 'group' );
|
||||
}
|
||||
|
||||
$where = 'WHERE claim_id = 0 AND scheduled_date_gmt <= %s AND status=%s';
|
||||
$params[] = $date->format( 'Y-m-d H:i:s' );
|
||||
$params[] = self::STATUS_PENDING;
|
||||
@@ -834,18 +893,33 @@ AND `group_id` = %d
|
||||
$params = array_merge( $params, array_values( $hooks ) );
|
||||
}
|
||||
|
||||
$group_operator = 'IN';
|
||||
if ( empty( $group ) ) {
|
||||
$group = $this->get_claim_filter( 'exclude-groups' );
|
||||
$group_operator = 'NOT IN';
|
||||
}
|
||||
|
||||
if ( ! empty( $group ) ) {
|
||||
$group_ids = $this->get_group_ids( $group, false );
|
||||
|
||||
$group_id = $this->get_group_id( $group, false );
|
||||
|
||||
// throw exception if no matching group found, this matches ActionScheduler_wpPostStore's behaviour.
|
||||
if ( empty( $group_id ) ) {
|
||||
/* translators: %s: group name */
|
||||
throw new InvalidArgumentException( sprintf( __( 'The group "%s" does not exist.', 'action-scheduler' ), $group ) );
|
||||
// throw exception if no matching group(s) found, this matches ActionScheduler_wpPostStore's behaviour.
|
||||
if ( empty( $group_ids ) ) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
/* translators: %s: group name(s) */
|
||||
_n(
|
||||
'The group "%s" does not exist.',
|
||||
'The groups "%s" do not exist.',
|
||||
is_array( $group ) ? count( $group ) : 1,
|
||||
'action-scheduler'
|
||||
),
|
||||
$group
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$where .= ' AND group_id = %d';
|
||||
$params[] = $group_id;
|
||||
$id_list = implode( ',', array_map( 'intval', $group_ids ) );
|
||||
$where .= " AND group_id {$group_operator} ( $id_list )";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,13 +929,23 @@ AND `group_id` = %d
|
||||
*
|
||||
* @param string $order_by_sql
|
||||
*/
|
||||
$order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY attempts ASC, scheduled_date_gmt ASC, action_id ASC' );
|
||||
$order = apply_filters( 'action_scheduler_claim_actions_order_by', 'ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC' );
|
||||
$params[] = $limit;
|
||||
|
||||
$sql = $wpdb->prepare( "{$update} {$where} {$order} LIMIT %d", $params ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders
|
||||
$rows_affected = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
if ( false === $rows_affected ) {
|
||||
throw new \RuntimeException( __( 'Unable to claim actions. Database error.', 'action-scheduler' ) );
|
||||
$error = empty( $wpdb->last_error )
|
||||
? _x( 'unknown', 'database error', 'action-scheduler' )
|
||||
: $wpdb->last_error;
|
||||
|
||||
throw new \RuntimeException(
|
||||
sprintf(
|
||||
/* translators: %s database error. */
|
||||
__( 'Unable to claim actions. Database error: %s.', 'action-scheduler' ),
|
||||
$error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $rows_affected;
|
||||
@@ -912,7 +996,7 @@ AND `group_id` = %d
|
||||
$cut_off = $before_date->format( 'Y-m-d H:i:s' );
|
||||
|
||||
$sql = $wpdb->prepare(
|
||||
"SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d",
|
||||
"SELECT action_id, scheduled_date_gmt FROM {$wpdb->actionscheduler_actions} WHERE claim_id = %d ORDER BY priority ASC, attempts ASC, scheduled_date_gmt ASC, action_id ASC",
|
||||
$claim_id
|
||||
);
|
||||
|
||||
@@ -955,7 +1039,7 @@ AND `group_id` = %d
|
||||
if ( $row_updates < count( $action_ids ) ) {
|
||||
throw new RuntimeException(
|
||||
sprintf(
|
||||
__( 'Unable to release actions from claim id %d.', 'woocommerce' ),
|
||||
__( 'Unable to release actions from claim id %d.', 'action-scheduler' ),
|
||||
$claim->get_id()
|
||||
)
|
||||
);
|
||||
@@ -1005,6 +1089,8 @@ AND `group_id` = %d
|
||||
/**
|
||||
* Add execution message to action log.
|
||||
*
|
||||
* @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress').
|
||||
*
|
||||
* @param int $action_id Action ID.
|
||||
*
|
||||
* @return void
|
||||
@@ -1015,7 +1101,20 @@ AND `group_id` = %d
|
||||
|
||||
$sql = "UPDATE {$wpdb->actionscheduler_actions} SET attempts = attempts+1, status=%s, last_attempt_gmt = %s, last_attempt_local = %s WHERE action_id = %d";
|
||||
$sql = $wpdb->prepare( $sql, self::STATUS_RUNNING, current_time( 'mysql', true ), current_time( 'mysql' ), $action_id ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
$status_updated = $wpdb->query( $sql );
|
||||
|
||||
if ( ! $status_updated ) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
/* translators: 1: action ID. 2: status slug. */
|
||||
__( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ),
|
||||
$action_id,
|
||||
self::STATUS_RUNNING
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -936,6 +936,8 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
/**
|
||||
* Log Execution.
|
||||
*
|
||||
* @throws Exception If the action status cannot be updated to self::STATUS_RUNNING ('in-progress').
|
||||
*
|
||||
* @param string $action_id Action ID.
|
||||
*/
|
||||
public function log_execution( $action_id ) {
|
||||
@@ -947,7 +949,7 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
global $wpdb;
|
||||
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||||
$wpdb->query(
|
||||
$status_updated = $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"UPDATE {$wpdb->posts} SET menu_order = menu_order+1, post_status=%s, post_modified_gmt = %s, post_modified = %s WHERE ID = %d AND post_type = %s",
|
||||
self::STATUS_RUNNING,
|
||||
@@ -957,6 +959,17 @@ class ActionScheduler_wpPostStore extends ActionScheduler_Store {
|
||||
self::POST_TYPE
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! $status_updated ) {
|
||||
throw new Exception(
|
||||
sprintf(
|
||||
/* translators: 1: action ID. 2: status slug. */
|
||||
__( 'Unable to update the status of action %1$d to %2$s.', 'action-scheduler' ),
|
||||
$action_id,
|
||||
self::STATUS_RUNNING
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,7 +79,7 @@ class Runner {
|
||||
|
||||
if ( $this->progress_bar ) {
|
||||
/* translators: %d: amount of actions */
|
||||
$this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), number_format_i18n( $batch_size ) ) );
|
||||
$this->progress_bar->set_message( sprintf( _n( 'Migrating %d action', 'Migrating %d actions', $batch_size, 'action-scheduler' ), $batch_size ) );
|
||||
$this->progress_bar->set_count( $batch_size );
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
/**
|
||||
* @var int Increment this value to trigger a schema update.
|
||||
*/
|
||||
protected $schema_version = 6;
|
||||
protected $schema_version = 7;
|
||||
|
||||
public function __construct() {
|
||||
$this->tables = [
|
||||
@@ -38,6 +38,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
$table_name = $wpdb->$table;
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
$max_index_length = 191; // @see wp_get_db_schema()
|
||||
$hook_status_scheduled_date_gmt_max_index_length = $max_index_length - 20 - 8; // - status, - scheduled_date_gmt
|
||||
$default_date = self::DEFAULT_DATE;
|
||||
switch ( $table ) {
|
||||
|
||||
@@ -49,6 +50,7 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
status varchar(20) NOT NULL,
|
||||
scheduled_date_gmt datetime NULL default '{$default_date}',
|
||||
scheduled_date_local datetime NULL default '{$default_date}',
|
||||
priority tinyint unsigned NOT NULL default '10',
|
||||
args varchar($max_index_length),
|
||||
schedule longtext,
|
||||
group_id bigint(20) unsigned NOT NULL default '0',
|
||||
@@ -58,8 +60,8 @@ class ActionScheduler_StoreSchema extends ActionScheduler_Abstract_Schema {
|
||||
claim_id bigint(20) unsigned NOT NULL default '0',
|
||||
extended_args varchar(8000) DEFAULT NULL,
|
||||
PRIMARY KEY (action_id),
|
||||
KEY hook (hook($max_index_length)),
|
||||
KEY status (status),
|
||||
KEY hook_status_scheduled_date_gmt (hook($hook_status_scheduled_date_gmt_max_index_length), status, scheduled_date_gmt),
|
||||
KEY status_scheduled_date_gmt (status, scheduled_date_gmt),
|
||||
KEY scheduled_date_gmt (scheduled_date_gmt),
|
||||
KEY args (args($max_index_length)),
|
||||
KEY group_id (group_id),
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -33,13 +34,23 @@ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique =
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_enqueue_async_action', null, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->async_unique( $hook, $args, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'async',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,10 +61,11 @@ function as_enqueue_async_action( $hook, $args = array(), $group = '', $unique =
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_single_action( $timestamp, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -72,13 +84,24 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priorities Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_single_action', null, $timestamp, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->single_unique( $hook, $args, $timestamp, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'single',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,14 +113,34 @@ function as_schedule_single_action( $timestamp, $hook, $args = array(), $group =
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$interval = (int) $interval_in_seconds;
|
||||
|
||||
// We expect an integer and allow it to be passed using float and string types, but otherwise
|
||||
// should reject unexpected values.
|
||||
if ( ! is_numeric( $interval_in_seconds ) || $interval_in_seconds != $interval ) {
|
||||
_doing_it_wrong(
|
||||
__METHOD__,
|
||||
sprintf(
|
||||
/* translators: 1: provided value 2: provided type. */
|
||||
esc_html__( 'An integer was expected but "%1$s" (%2$s) was received.', 'action-scheduler' ),
|
||||
esc_html( $interval_in_seconds ),
|
||||
esc_html( gettype( $interval_in_seconds ) )
|
||||
),
|
||||
'3.6.0'
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity to short-circuit the default process for enqueuing recurring
|
||||
* actions.
|
||||
@@ -113,13 +156,25 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_recurring_action', null, $timestamp, $interval_in_seconds, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->recurring_unique( $hook, $args, $timestamp, $interval_in_seconds, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'recurring',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'pattern' => $interval_in_seconds,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,10 +198,11 @@ function as_schedule_recurring_action( $timestamp, $interval_in_seconds, $hook,
|
||||
* @param array $args Arguments to pass when the hook triggers.
|
||||
* @param string $group The group to assign this job to.
|
||||
* @param bool $unique Whether the action should be unique.
|
||||
* @param int $priority Lower values take precedence over higher values. Defaults to 10, with acceptable values falling in the range 0-255.
|
||||
*
|
||||
* @return int The action ID.
|
||||
* @return int The action ID. Zero if there was an error scheduling the action.
|
||||
*/
|
||||
function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false ) {
|
||||
function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(), $group = '', $unique = false, $priority = 10 ) {
|
||||
if ( ! ActionScheduler::is_initialized( __FUNCTION__ ) ) {
|
||||
return 0;
|
||||
}
|
||||
@@ -166,13 +222,25 @@ function as_schedule_cron_action( $timestamp, $schedule, $hook, $args = array(),
|
||||
* @param string $hook Action hook.
|
||||
* @param array $args Action arguments.
|
||||
* @param string $group Action group.
|
||||
* @param int $priority Action priority.
|
||||
*/
|
||||
$pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group );
|
||||
$pre = apply_filters( 'pre_as_schedule_cron_action', null, $timestamp, $schedule, $hook, $args, $group, $priority );
|
||||
if ( null !== $pre ) {
|
||||
return is_int( $pre ) ? $pre : 0;
|
||||
}
|
||||
|
||||
return ActionScheduler::factory()->cron_unique( $hook, $args, $timestamp, $schedule, $group, $unique );
|
||||
return ActionScheduler::factory()->create(
|
||||
array(
|
||||
'type' => 'cron',
|
||||
'hook' => $hook,
|
||||
'arguments' => $args,
|
||||
'when' => $timestamp,
|
||||
'pattern' => $schedule,
|
||||
'group' => $group,
|
||||
'unique' => $unique,
|
||||
'priority' => $priority,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -215,9 +283,10 @@ function as_unschedule_action( $hook, $args = array(), $group = '' ) {
|
||||
ActionScheduler::logger()->log(
|
||||
$action_id,
|
||||
sprintf(
|
||||
/* translators: %s is the name of the hook to be cancelled. */
|
||||
__( 'Caught exception while cancelling action: %s', 'action-scheduler' ),
|
||||
esc_attr( $hook )
|
||||
/* translators: %1$s is the name of the hook to be cancelled, %2$s is the exception message. */
|
||||
__( 'Caught exception while cancelling action "%1$s": %2$s', 'action-scheduler' ),
|
||||
$hook,
|
||||
$exception->getMessage()
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
=== Action Scheduler ===
|
||||
Contributors: Automattic, wpmuguru, claudiosanches, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, royho, barryhughes-1
|
||||
Tags: scheduler, cron
|
||||
Requires at least: 5.2
|
||||
Tested up to: 6.0
|
||||
Stable tag: 3.5.4
|
||||
Stable tag: 3.7.1
|
||||
License: GPLv3
|
||||
Requires at least: 6.2
|
||||
Tested up to: 6.4
|
||||
Requires PHP: 5.6
|
||||
|
||||
Action Scheduler - Job Queue for WordPress
|
||||
@@ -47,6 +47,66 @@ Collaboration is cool. We'd love to work with you to improve Action Scheduler. [
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 3.7.1 - 2023-12-13 =
|
||||
* Release/3.7.0.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* update semver to 5.7.2 because of a security vulnerability in 5.7.1.
|
||||
|
||||
= 3.7.0 - 2023-11-20 =
|
||||
* Important: starting with this release, Action Scheduler follows an L-2 version policy (WordPress, and consequently PHP).
|
||||
* Add extended indexes for hook_status_scheduled_date_gmt and status_sheduled_date_gmt.
|
||||
* Catch and log exceptions thrown when actions can't be created, e.g. under a corrupt database schema.
|
||||
* Release/3.6.4.
|
||||
* Tweak - WP 6.4 compatibility.
|
||||
* Update unit tests for upcoming dependency version policy.
|
||||
* make sure hook action_scheduler_failed_execution can access original exception object.
|
||||
* mention dependency version policy in usage.md.
|
||||
|
||||
= 3.6.4 - 2023-10-11 =
|
||||
* Performance improvements when bulk cancelling actions.
|
||||
* Dev-related fixes.
|
||||
|
||||
= 3.6.3 - 2023-09-13 =
|
||||
* Use `_doing_it_wrong` in initialization check.
|
||||
|
||||
= 3.6.2 - 2023-08-09 =
|
||||
* Add guidance about passing arguments.
|
||||
* Atomic option locking.
|
||||
* Improve bulk delete handling.
|
||||
* Include database error in the exception message.
|
||||
* Tweak - WP 6.3 compatibility.
|
||||
|
||||
= 3.6.1 - 2023-06-14 =
|
||||
* Document new optional `$priority` arg for various API functions.
|
||||
* Document the new `--exclude-groups` WP CLI option.
|
||||
* Document the new `action_scheduler_init` hook.
|
||||
* Ensure actions within each claim are executed in the expected order.
|
||||
* Fix incorrect text domain.
|
||||
* Remove SHOW TABLES usage when checking if tables exist.
|
||||
|
||||
= 3.6.0 - 2023-05-10 =
|
||||
* Add $unique parameter to function signatures.
|
||||
* Add a cast-to-int for extra safety before forming new DateTime object.
|
||||
* Add a hook allowing exceptions for consistently failing recurring actions.
|
||||
* Add action priorities.
|
||||
* Add init hook.
|
||||
* Always raise the time limit.
|
||||
* Bump minimatch from 3.0.4 to 3.0.8.
|
||||
* Bump yaml from 2.2.1 to 2.2.2.
|
||||
* Defensive coding relating to gaps in declared schedule types.
|
||||
* Do not process an action if it cannot be set to `in-progress`.
|
||||
* Filter view labels (status names) should be translatable | #919.
|
||||
* Fix WPCLI progress messages.
|
||||
* Improve data-store initialization flow.
|
||||
* Improve error handling across all supported PHP versions.
|
||||
* Improve logic for flushing the runtime cache.
|
||||
* Support exclusion of multiple groups.
|
||||
* Update lint-staged and Node/NPM requirements.
|
||||
* add CLI clean command.
|
||||
* add CLI exclude-group filter.
|
||||
* exclude past-due from list table all filter count.
|
||||
* throwing an exception if as_schedule_recurring_action interval param is not of type integer.
|
||||
|
||||
= 3.5.4 - 2023-01-17 =
|
||||
* Add pre filters during action registration.
|
||||
* Async scheduling.
|
||||
|
||||
@@ -296,7 +296,7 @@ function _imagify_new_upgrade( $network_version, $site_version ) {
|
||||
|
||||
// 1.9.6
|
||||
if ( version_compare( $site_version, '1.9.6' ) < 0 ) {
|
||||
\Imagify\Stats\OptimizedMediaWithoutWebp::get_instance()->clear_cache();
|
||||
\Imagify\Stats\OptimizedMediaWithoutNextGen::get_instance()->clear_cache();
|
||||
}
|
||||
|
||||
// 1.9.11
|
||||
@@ -307,6 +307,11 @@ function _imagify_new_upgrade( $network_version, $site_version ) {
|
||||
if ( version_compare( $site_version, '2.0' ) < 0 ) {
|
||||
Imagify_Options::get_instance()->set( 'optimization_level', 2 );
|
||||
}
|
||||
|
||||
if ( version_compare( $site_version, '2.2' ) < 0 ) {
|
||||
Imagify_Options::get_instance()->set( 'display_nextgen', Imagify_Options::get_instance()->get( 'display_webp', 0 ) );
|
||||
Imagify_Options::get_instance()->set( 'display_nextgen_method', Imagify_Options::get_instance()->get( 'display_webp_method' ) );
|
||||
}
|
||||
}
|
||||
add_action( 'imagify_upgrade', '_imagify_new_upgrade', 10, 2 );
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user