Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
64.18% covered (warning)
64.18%
43 / 67
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Quality_Settings
64.18% covered (warning)
64.18%
43 / 67
44.44% covered (danger)
44.44%
4 / 9
23.01
0.00% covered (danger)
0.00%
0 / 1
 setup
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_slug
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_available
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 register_data_sync
100.00% covered (success)
100.00%
38 / 38
100.00% covered (success)
100.00%
1 / 1
1
 get_change_output_action_names
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add_quality_args
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_quality_for_image
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 get_quality_for_type
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 get_parent_features
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Automattic\Jetpack_Boost\Modules\Optimizations\Image_CDN;
4
5use Automattic\Jetpack\Schema\Schema;
6use Automattic\Jetpack\WP_JS_Data_Sync\Data_Sync;
7use Automattic\Jetpack_Boost\Contracts\Changes_Output_After_Activation;
8use Automattic\Jetpack_Boost\Contracts\Has_Data_Sync;
9use Automattic\Jetpack_Boost\Contracts\Is_Always_On;
10use Automattic\Jetpack_Boost\Contracts\Sub_Feature;
11use Automattic\Jetpack_Boost\Lib\Premium_Features;
12
13class Quality_Settings implements Sub_Feature, Is_Always_On, Has_Data_Sync, Changes_Output_After_Activation {
14
15    public function setup() {
16        add_filter( 'jetpack_photon_pre_args', array( $this, 'add_quality_args' ), 10, 2 );
17    }
18
19    public static function get_slug() {
20        return 'image_cdn_quality';
21    }
22
23    public static function is_available() {
24        return Premium_Features::has_feature( Premium_Features::IMAGE_CDN_QUALITY );
25    }
26
27    public function register_data_sync( Data_Sync $instance ) {
28        $image_cdn_quality_schema = Schema::as_assoc_array(
29            array(
30                'jpg'  => Schema::as_assoc_array(
31                    array(
32                        'quality'  => Schema::as_number(),
33                        'lossless' => Schema::as_boolean(),
34                    )
35                ),
36                'png'  => Schema::as_assoc_array(
37                    array(
38                        'quality'  => Schema::as_number(),
39                        'lossless' => Schema::as_boolean(),
40                    )
41                ),
42                'webp' => Schema::as_assoc_array(
43                    array(
44                        'quality'  => Schema::as_number(),
45                        'lossless' => Schema::as_boolean(),
46                    )
47                ),
48            )
49        )->fallback(
50            array(
51                'jpg'  => array(
52                    'quality'  => 89,
53                    'lossless' => false,
54                ),
55                'png'  => array(
56                    'quality'  => 80,
57                    'lossless' => false,
58                ),
59                'webp' => array(
60                    'quality'  => 80,
61                    'lossless' => false,
62                ),
63            )
64        );
65
66        $instance->register( 'image_cdn_quality', $image_cdn_quality_schema );
67    }
68
69    public static function get_change_output_action_names() {
70        return array( 'update_option_' . JETPACK_BOOST_DATASYNC_NAMESPACE . '_image_cdn_quality' );
71    }
72
73    /**
74     * Add quality arg to existing photon args.
75     *
76     * @param array $args - Existing photon args.
77     *
78     * @return mixed
79     */
80    public function add_quality_args( $args, $image_url ) {
81        $quality = $this->get_quality_for_image( $image_url );
82
83        if ( $quality !== null ) {
84            $args['quality'] = $quality;
85        }
86
87        return $args;
88    }
89
90    /**
91     * Get the quality for an image based on the extension.
92     */
93    private function get_quality_for_image( $image_url ) {
94        static $quality_cache = array();
95
96        // Extract the file extension from the URL
97        $file_extension = strtolower( pathinfo( $image_url, PATHINFO_EXTENSION ) );
98
99        static $extension_to_type = array(
100            'jpg'  => 'jpg',
101            'jpeg' => 'jpg',
102            'webp' => 'webp',
103            'png'  => 'png',
104        );
105
106        if ( ! isset( $extension_to_type[ $file_extension ] ) ) {
107            return null;
108        }
109
110        $type = $extension_to_type[ $file_extension ];
111
112        if ( ! isset( $quality_cache[ $type ] ) ) {
113            $quality_cache[ $type ] = $this->get_quality_for_type( $type );
114        }
115
116        return $quality_cache[ $type ];
117    }
118
119    private function get_quality_for_type( $image_type ) {
120        $quality_settings = jetpack_boost_ds_get( 'image_cdn_quality' );
121
122        if ( ! isset( $quality_settings[ $image_type ] ) ) {
123            return null;
124        }
125
126        // Passing 100 to photon will result in a lossless image
127        return $quality_settings[ $image_type ]['lossless'] ? 100 : $quality_settings[ $image_type ]['quality'];
128    }
129
130    public static function get_parent_features(): array {
131        return array(
132            Image_CDN::class,
133        );
134    }
135}