Skip to content

Commit b823bc6

Browse files
tijmenbruggemanwarp-agentSreini
authored
feat: select converted images via .htaccess
--------- Co-authored-by: Warp <agent@warp.dev> Co-authored-by: Sertii <36940685+Sreini@users.noreply.github.com>
1 parent 578c32c commit b823bc6

17 files changed

Lines changed: 722 additions & 113 deletions

src/class-tiny-apache-rewrite.php

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
<?php
2+
/*
3+
* Tiny Compress Images - WordPress plugin.
4+
* Copyright (C) 2015-2018 Tinify B.V.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License as published by the Free
8+
* Software Foundation; either version 2 of the License, or (at your option)
9+
* any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14+
* more details.
15+
*
16+
* You should have received a copy of the GNU General Public License along
17+
* with this program; if not, write to the Free Software Foundation, Inc., 51
18+
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n*/
19+
20+
/**
21+
* Tiny_Apache_Rewrite
22+
* class responsible for the apache rules for image delivery.
23+
* - toggling the rules when saving settings
24+
* - inserting/removing rules from htaccess
25+
*
26+
* We are only updating rules when:
27+
* - updating the option convert_format
28+
* - uninstalling the plug-in
29+
*/
30+
class Tiny_Apache_Rewrite extends Tiny_WP_Base {
31+
32+
/**
33+
* seperator when rules are inserted
34+
* @var string
35+
*/
36+
const MARKER = 'tiny-compress-images';
37+
38+
/**
39+
* Installs or uninstalls the htaccess rules
40+
* hooked into `update_option_tinypng_convert_format`
41+
* https://developer.wordpress.org/reference/hooks/update_option_option/
42+
*
43+
*
44+
* @param mixed $old_value
45+
* @param mixed $value
46+
* @param string $option
47+
* @return void
48+
*/
49+
public static function toggle_rules( $old_value, $value, $option ) {
50+
$old_delivery = isset( $old_value['delivery_method'] ) ?
51+
$old_value['delivery_method'] : null;
52+
$new_delivery = isset( $value['delivery_method'] ) ?
53+
$value['delivery_method'] : null;
54+
55+
if ( $old_delivery === $new_delivery ) {
56+
return;
57+
}
58+
59+
if ( 'htaccess' === $new_delivery ) {
60+
$installed = self::install_rules();
61+
Tiny_Logger::debug( 'htaccess rules installed: ' . $installed );
62+
return;
63+
}
64+
65+
// We only uninstall if we were previously using htaccess
66+
if ( 'htaccess' === $old_delivery ) {
67+
$uninstalled = self::uninstall_rules();
68+
Tiny_Logger::debug( 'htaccess rules uninstaled: ' . $uninstalled );
69+
}
70+
}
71+
72+
/**
73+
* Generate .htaccess rewrite rules for serving WebP and AVIF images.
74+
*
75+
* @return string The .htaccess rules
76+
*/
77+
private static function get_rewrite_rules() {
78+
$rules = array();
79+
$rules[] = '<IfModule mod_rewrite.c>';
80+
$rules[] = 'RewriteEngine On';
81+
$rules[] = 'RewriteOptions Inherit';
82+
83+
$rules = array_merge( $rules, self::get_avif_rules() );
84+
$rules = array_merge( $rules, self::get_webp_rules() );
85+
86+
$rules[] = '</IfModule>';
87+
88+
$rules[] = '<IfModule mod_headers.c>';
89+
$rules[] = '<FilesMatch "\.(jpe?g|png)$">';
90+
$rules[] = 'Header append Vary Accept';
91+
$rules[] = '</FilesMatch>';
92+
$rules[] = '</IfModule>';
93+
94+
$rules[] = '<IfModule mod_mime.c>';
95+
$rules[] = 'AddType image/webp .webp';
96+
$rules[] = 'AddType image/avif .avif';
97+
$rules[] = '</IfModule>';
98+
99+
return implode( "\n", $rules );
100+
}
101+
102+
/**
103+
* Generate AVIF rewrite rules.
104+
*
105+
* @return array[] AVIF rewrite rules
106+
*/
107+
private static function get_avif_rules() {
108+
$rules = array();
109+
$rules[] = 'RewriteCond %{HTTP_ACCEPT} image/avif';
110+
$rules[] = 'RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$';
111+
$rules[] = 'RewriteCond %{DOCUMENT_ROOT}/%1.avif -f';
112+
$rules[] = 'RewriteCond %{QUERY_STRING} !type=original';
113+
$rules[] = 'RewriteRule (.+)\.(?:jpe?g|png)$ $1.avif [T=image/avif,L]';
114+
return $rules;
115+
}
116+
117+
/**
118+
* Generate WebP rewrite rules.
119+
*
120+
* @return array[] WebP rewrite rules
121+
*/
122+
private static function get_webp_rules() {
123+
$rules = array();
124+
$rules[] = 'RewriteCond %{HTTP_ACCEPT} image/webp';
125+
$rules[] = 'RewriteCond %{REQUEST_URI} ^(.+)\.(?:jpe?g|png)$';
126+
$rules[] = 'RewriteCond %{DOCUMENT_ROOT}/%1.webp -f';
127+
$rules[] = 'RewriteCond %{QUERY_STRING} !type=original';
128+
$rules[] = 'RewriteRule (.+)\.(?:jpe?g|png)$ $1.webp [T=image/webp,L]';
129+
130+
return $rules;
131+
}
132+
133+
/**
134+
* Install rewrite rules to .htaccess files.
135+
*
136+
* @return bool True on success, false otherwise
137+
*/
138+
private static function install_rules() {
139+
$rules = self::get_rewrite_rules();
140+
$upload_dir = wp_upload_dir();
141+
if ( isset( $upload_dir['basedir'] ) && is_writable( $upload_dir['basedir'] ) ) {
142+
$htaccess_file = $upload_dir['basedir'] . '/.htaccess';
143+
return insert_with_markers( $htaccess_file, self::MARKER, $rules );
144+
}
145+
146+
return false;
147+
}
148+
149+
/**
150+
* Remove rewrite rules from .htaccess files.
151+
*
152+
* @return bool True on success, false otherwise
153+
*/
154+
public static function uninstall_rules() {
155+
$upload_dir = wp_upload_dir();
156+
if (
157+
file_exists( $upload_dir['basedir'] . '/.htaccess' )
158+
) {
159+
$htaccess_file = $upload_dir['basedir'] . '/.htaccess';
160+
return insert_with_markers( $htaccess_file, self::MARKER, '' );
161+
}
162+
163+
return false;
164+
}
165+
}

src/class-tiny-conversion.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
/*
3+
* Tiny Compress Images - WordPress plugin.
4+
* Copyright (C) 2015-2018 Tinify B.V.
5+
*
6+
* This program is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License as published by the Free
8+
* Software Foundation; either version 2 of the License, or (at your option)
9+
* any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14+
* more details.
15+
*
16+
* You should have received a copy of the GNU General Public License along
17+
* with this program; if not, write to the Free Software Foundation, Inc., 51
18+
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19+
*/
20+
21+
/**
22+
* class managing conversion delivery method
23+
*/
24+
class Tiny_Conversion extends Tiny_WP_Base {
25+
26+
/**
27+
* @var Tiny_Settings plug-in settings
28+
*/
29+
private $settings;
30+
31+
/**
32+
* @param Tiny_Settings $settings
33+
*/
34+
public function __construct( $settings ) {
35+
parent::__construct();
36+
$this->settings = $settings;
37+
}
38+
39+
/**
40+
* will check if conversion is enabled,
41+
* if true:
42+
* - will enable the delivery method
43+
* - will add hook to toggle rules
44+
*
45+
* hooked into `init`
46+
*/
47+
public function init() {
48+
if ( ! $this->settings->get_conversion_enabled() ) {
49+
return;
50+
}
51+
52+
add_action(
53+
'update_option_tinypng_convert_format',
54+
'Tiny_Apache_Rewrite::toggle_rules',
55+
20,
56+
3
57+
);
58+
59+
$delivery_method = $this->settings->get_conversion_delivery_method();
60+
61+
$this->init_image_delivery( $delivery_method );
62+
}
63+
64+
/**
65+
* Initializes the method of delivery for optimised images
66+
*
67+
* @param string $delivery_method 'picture' or 'htaccess'
68+
* @return void
69+
*/
70+
private function init_image_delivery( $delivery_method ) {
71+
global $is_apache;
72+
/**
73+
* Controls wether the page should replace <img> with <picture> elements
74+
* converted sources.
75+
*
76+
* @since 3.7.0
77+
*/
78+
if ( 'htaccess' === $delivery_method && $is_apache ) {
79+
new Tiny_Apache_Rewrite();
80+
return;
81+
}
82+
83+
if ( apply_filters( 'tiny_replace_with_picture', 'picture' === $delivery_method ) ) {
84+
new Tiny_Picture( $this->settings, ABSPATH, array( get_site_url() ) );
85+
return;
86+
}
87+
}
88+
}

src/class-tiny-plugin.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public static function version() {
4040
public function __construct() {
4141
parent::__construct();
4242
$this->settings = new Tiny_Settings();
43+
new Tiny_Conversion( $this->settings );
4344
}
4445

4546
public function set_compressor( $compressor ) {
@@ -72,7 +73,6 @@ public function init() {
7273
dirname( plugin_basename( __FILE__ ) ) . '/languages'
7374
);
7475

75-
new Tiny_Picture( $this->settings, ABSPATH, array( get_site_url() ) );
7676
$this->tiny_compatibility();
7777
}
7878

0 commit comments

Comments
 (0)