Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AVIF Rewrite rules #774

Merged
merged 4 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions classes/Avif/RewriteRules/Apache.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
namespace Imagify\Avif\RewriteRules;

defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );
Miraeld marked this conversation as resolved.
Show resolved Hide resolved

/**
* Add and remove rewrite rules to the .htaccess file to display AVIF images on the site.
*
* @author Gael Robin
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
*/
class Apache extends \Imagify\WriteFile\AbstractApacheDirConfFile {
Miraeld marked this conversation as resolved.
Show resolved Hide resolved

/**
* Name of the tag used as block delimiter.
*
* @var string
* @author Gael Robin
*/
const TAG_NAME = 'Imagify: rewrite rules for avif';

/**
* Get unfiltered new contents to write into the file.
*
* @access protected
* @author Gael Robin
*
* @return string
*/
protected function get_raw_new_contents() {
$extensions = $this->get_extensions_pattern();
$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>' );
}
}
232 changes: 232 additions & 0 deletions classes/Avif/RewriteRules/Display.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<?php
namespace Imagify\Avif\RewriteRules;

use Imagify\Notices\Notices;
use Imagify\Traits\InstanceGetterTrait;
use Imagify\WriteFile\AbstractWriteDirConfFile;

/**
* Display AVIF images on the site with rewrite rules.
*/
class Display {
use InstanceGetterTrait;

/**
* Configuration file writer.
*
* @var AbstractWriteDirConfFile
*/
protected $server_conf;

/**
* Option value.
*
* @var string
*/
const OPTION_VALUE = 'rewrite_avif';

/**
* Init.
*/
public function init() {
add_filter( 'imagify_settings_on_save', [ $this, 'maybe_add_rewrite_rules' ] );
add_action( 'imagify_settings_avif_info', [ $this, 'maybe_add_avif_info' ] );
add_action( 'imagify_activation', [ $this, 'activate' ] );
add_action( 'imagify_deactivation', [ $this, 'deactivate' ] );
}

/**
* If display AVIF images via rewrite rules, add the rules to the .htaccess/etc file.
*
* @param array $values The option values.
* @return array
*/
public function maybe_add_rewrite_rules( $values ) {
global $is_apache, $is_nginx;

// Display AVIF?
$was_enabled = (bool) get_imagify_option( 'display_avif' );
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
// See \Imagify_Options->validate_values_on_update() for why we use 'convert_to_avif' here.
$is_enabled = ! empty( $values['display_avif'] ) && ! empty( $values['convert_to_avif'] );
Miraeld marked this conversation as resolved.
Show resolved Hide resolved

// Which method?
$old_value = get_imagify_option( 'display_avif_method' );
$new_value = ! empty( $values['display_avif_method'] ) ? $values['display_avif_method'] : '';
Miraeld marked this conversation as resolved.
Show resolved Hide resolved

// 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;

if ( $is_enabled && $is_rewrite && ( ! $was_enabled || ! $was_rewrite ) ) {
// Display AVIF & 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 AVIF & 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_nginx ) {
$rules = new Nginx();
} else {
return $values;
}

if ( 'add' === $add_or_remove ) {
// Add the rewrite rules.
$result = $rules->add();
} else {
// Remove the rewrite rules.
$result = $rules->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() );
} else {
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() {
global $is_nginx;

$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>';
} elseif ( $is_nginx ) {
printf(
/* translators: %s is a file name. */
esc_html__( 'If you choose to use rewrite rules, the file %s will be created and must be included into the server’s configuration file (then restart the server).', 'imagify' ),
'<code>' . $this->get_file_path( true ) . '</code>'
);
}
}

/**
* Add rules on plugin activation.
*/
public function activate() {
$conf = $this->get_server_conf();

if ( ! $conf ) {
return;
}
if ( ! get_imagify_option( 'display_avif' ) ) {
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if ( self::OPTION_VALUE !== get_imagify_option( 'display_avif_method' ) ) {
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if ( is_wp_error( $conf->is_file_writable() ) ) {
return;
}

$conf->add();
}

/**
* Remove rules on plugin deactivation.
*/
public function deactivate() {
$conf = $this->get_server_conf();

if ( ! $conf ) {
return;
}
if ( ! get_imagify_option( 'display_avif' ) ) {
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
return;
}
if ( self::OPTION_VALUE !== get_imagify_option( 'display_avif_method' ) ) {
Miraeld marked this conversation as resolved.
Show resolved Hide resolved
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 \Imagify\WriteFile\WriteFileInterface
*/
protected function get_server_conf() {
global $is_apache, $is_nginx;

if ( isset( $this->server_conf ) ) {
return $this->server_conf;
}

if ( $is_apache ) {
$this->server_conf = new Apache();
} elseif ( $is_nginx ) {
$this->server_conf = new Nginx();
} else {
$this->server_conf = false;
}

return $this->server_conf;
}
}
49 changes: 49 additions & 0 deletions classes/Avif/RewriteRules/Nginx.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
namespace Imagify\Avif\RewriteRules;

defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' );

/**
* Add and remove rewrite rules to the imagify.conf file to display AVIF images on the site.
*
* @author Gael Robin
*/
class Nginx extends \Imagify\WriteFile\AbstractNginxDirConfFile {

/**
* Name of the tag used as block delimiter.
*
* @var string
* @author Gael Robin
*/
const TAG_NAME = 'Imagify: rewrite rules for avif';

/**
* Get unfiltered new contents to write into the file.
*
* @access protected
* @author Gael Robin
*
* @return string
*/
protected function get_raw_new_contents() {
$extensions = $this->get_extensions_pattern();
$home_root = wp_parse_url( home_url( '/' ) );
$home_root = $home_root['path'];

return trim( '
location ~* ^(' . $home_root . '.+)\.(' . $extensions . ')$ {
add_header Vary Accept;

if ($http_accept ~* "avif"){
set $imavif A;
}
if (-f $request_filename.avif) {
set $imavif "${imavif}B";
}
if ($imavif = AB) {
rewrite ^(.*) $1.avif;
}
}' );
}
}