Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageUploadListener
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 5
132
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 register_hooks
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 track_wp_image_resizes
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
2
 handle_media_upload
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 process_attachment_metadata
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace NewfoldLabs\WP\Module\Performance\Images;
4
5use NewfoldLabs\WP\Module\Performance\Data\Events;
6use NewfoldLabs\WP\Module\Performance\Services\EventService;
7
8/**
9 * Listens for media uploads and manages image optimization processing.
10 */
11class ImageUploadListener {
12    /**
13     * The service class for image optimization.
14     *
15     * @var ImageService
16     */
17    private $image_service;
18
19    /**
20     * Whether to delete the original uploaded file after optimization.
21     *
22     * @var bool
23     */
24    private $delete_original;
25
26    /**
27     * Constructor to initialize the listener.
28     *
29     * @param \NewfoldLabs\WP\Container\Container $container Dependency injection container.
30     * @param bool                                $delete_original Whether to delete the original file after optimization.
31     */
32    public function __construct( $container, $delete_original = false ) {
33        $this->image_service   = new ImageService( $container );
34        $this->delete_original = $delete_original;
35        $this->register_hooks();
36    }
37
38    /**
39     * Registers the WordPress hooks for listening to media uploads.
40     */
41    private function register_hooks() {
42        if ( ImageSettings::is_optimization_enabled() && ImageSettings::is_auto_optimization_enabled() ) {
43            add_filter( 'wp_handle_upload', array( $this, 'handle_media_upload' ), 10, 2 );
44            add_action( 'add_attachment', array( $this, 'process_attachment_metadata' ) );
45        }
46        add_filter( 'wp_generate_attachment_metadata', array( $this, 'track_wp_image_resizes' ), 10, 2 );
47    }
48
49    /**
50     * Tracks how many times WordPress generates image metadata.
51     *
52     * @param array $metadata The generated metadata for the image.
53     * @param int   $attachment_id The attachment ID.
54     * @return array The unchanged metadata.
55     */
56    public function track_wp_image_resizes( $metadata, $attachment_id ) {
57        EventService::send(
58            array(
59                'category' => Events::get_category()[0],
60                'action'   => 'image_resized',
61                'data'     => array(
62                    'attachment_id'   => $attachment_id,
63                    'generated_sizes' => count( $metadata['sizes'] ?? array() ),
64                ),
65            )
66        );
67
68        return $metadata;
69    }
70
71    /**
72     * Intercepts media uploads and optimizes images via the Cloudflare Worker.
73     *
74     * @param array $upload The upload array with file data.
75     * @return array The modified upload array or the original array on failure.
76     */
77    public function handle_media_upload( $upload ) {
78        $optimized_image_path = $this->image_service->optimize_image( $upload['url'], $upload['file'] );
79
80        if ( is_wp_error( $optimized_image_path ) ) {
81            return $upload;
82        }
83
84        if ( $this->delete_original ) {
85            $result = $this->image_service->replace_original_with_webp( $upload['file'], $optimized_image_path );
86            if ( is_wp_error( $result ) ) {
87                return $upload;
88            }
89
90            return $result;
91        } else {
92            $this->image_service->register_webp_as_new_media( $optimized_image_path );
93        }
94
95        return $upload;
96    }
97
98    /**
99     * Processes attachment metadata after the attachment is created.
100     *
101     * @param int $attachment_id The attachment ID.
102     */
103    public function process_attachment_metadata( $attachment_id ) {
104        $attachment_file = get_attached_file( $attachment_id );
105        $transient_key   = 'nfd_webp_metadata_' . md5( $attachment_file );
106        $metadata        = get_transient( $transient_key );
107
108        if ( $metadata ) {
109            // Update postmeta
110            update_post_meta( $attachment_id, '_nfd_performance_image_optimized', 1 );
111        }
112    }
113}