Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.33% covered (warning)
78.33%
47 / 60
80.00% covered (warning)
80.00%
8 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
Report_Registry
79.66% covered (warning)
79.66%
47 / 59
80.00% covered (warning)
80.00%
8 / 10
28.85
0.00% covered (danger)
0.00%
0 / 1
 register_controller
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 is_registered
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_registered_reports
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_data_endpoint
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 get_columns
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 get_row_formatter
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
2.02
 get_label
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 build_filename
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
42
 get_batch_limit
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 get_controller
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Report Registry
4 *
5 * Registry pattern to manage report type configurations for CSV exports.
6 *
7 * @package Automattic\Jetpack\PremiumAnalytics\Reports\Export
8 */
9
10declare( strict_types=1 );
11
12namespace Automattic\Jetpack\PremiumAnalytics\Reports\Export;
13
14defined( 'ABSPATH' ) || exit;
15
16/**
17 * Report Registry class for managing report configurations.
18 *
19 * @since $$next-version$$
20 */
21class Report_Registry {
22
23    /**
24     * Registered controller instances.
25     *
26     * @var array<string, Csv_Report_Controller_Interface>
27     */
28    private $controllers = array();
29
30    /**
31     * Register a controller instance.
32     *
33     * @param Csv_Report_Controller_Interface $controller The controller instance.
34     * @return bool True on success, false if already registered.
35     */
36    public function register_controller( Csv_Report_Controller_Interface $controller ): bool {
37        $report_key = $controller->get_report_key();
38
39        if ( isset( $this->controllers[ $report_key ] ) ) {
40            return false;
41        }
42
43        // Store the controller instance.
44        $this->controllers[ $report_key ] = $controller;
45
46        return true;
47    }
48
49    /**
50     * Check if a report type is registered.
51     *
52     * @param string $report_key The report key.
53     * @return bool True if registered, false otherwise.
54     */
55    public function is_registered( string $report_key ): bool {
56        return isset( $this->controllers[ $report_key ] );
57    }
58
59    /**
60     * Get all registered report keys.
61     *
62     * @return string[] Array of registered report keys.
63     */
64    public function get_registered_reports(): array {
65        return array_keys( $this->controllers );
66    }
67
68    /**
69     * Get data endpoint for a report type.
70     *
71     * @param string $report_key The report key.
72     * @return string|\WP_Error The data endpoint or error.
73     */
74    public function get_data_endpoint( string $report_key ) {
75        $controller = $this->get_controller( $report_key );
76        if ( \is_wp_error( $controller ) ) {
77            return $controller;
78        }
79
80        return $controller->get_data_endpoint();
81    }
82
83    /**
84     * Get columns for a report type.
85     *
86     * @param string      $report_key         The report key.
87     * @param bool        $include_comparison Whether to include comparison columns.
88     * @param string|null $interval           Optional time interval for dynamic headers.
89     * @return array|\WP_Error Column definitions or error.
90     */
91    public function get_columns( string $report_key, bool $include_comparison = false, ?string $interval = null ) {
92        $controller = $this->get_controller( $report_key );
93        if ( \is_wp_error( $controller ) ) {
94            return $controller;
95        }
96
97        $columns = $controller->get_column_headers( $interval );
98
99        if ( $include_comparison ) {
100            $comparison_columns = array();
101            foreach ( $columns as $key => $label ) {
102                $comparison_columns[ Report_Data_Fetcher::COMPARISON_INDEX_PREFIX . $key ] = sprintf(
103                    /* translators: %s: the column label, e.g. "Orders". */
104                    __( '%s (Previous Period)', 'jetpack-premium-analytics' ),
105                    $label
106                );
107            }
108            $columns = array_merge( $columns, $comparison_columns );
109        }
110
111        return $columns;
112    }
113
114    /**
115     * Get row formatter for a report type.
116     *
117     * @param string      $report_key The report key.
118     * @param string|null $interval   Optional time interval for formatting.
119     * @return callable|\WP_Error The row formatter callback or error.
120     */
121    public function get_row_formatter( string $report_key, ?string $interval = null ) {
122        $controller = $this->get_controller( $report_key );
123        if ( \is_wp_error( $controller ) ) {
124            return $controller;
125        }
126        // Return a closure that captures the interval to avoid race conditions.
127        return function ( $item ) use ( $controller, $interval ) {
128            return $controller->format_row_with_comparison( $item, $interval );
129        };
130    }
131
132    /**
133     * Get report label.
134     *
135     * @param string $report_key The report key.
136     * @return string|\WP_Error The report label or error.
137     */
138    public function get_label( string $report_key ) {
139        $controller = $this->get_controller( $report_key );
140        if ( \is_wp_error( $controller ) ) {
141            return $controller;
142        }
143        return $controller->get_report_label();
144    }
145
146    /**
147     * Build a filename base (no extension) for a report export: "<label>-<from>-to-<to>".
148     *
149     * @param string $report_key The report key.
150     * @param array  $params     Request parameters (reads 'from' and 'to').
151     * @return string The sanitized filename base.
152     */
153    public function build_filename( string $report_key, array $params ): string {
154        $label = $this->get_label( $report_key );
155        if ( \is_wp_error( $label ) ) {
156            $label = $report_key;
157        }
158
159        $from_ts = empty( $params['from'] ) ? false : strtotime( $params['from'] );
160        $to_ts   = empty( $params['to'] ) ? false : strtotime( $params['to'] );
161
162        return sprintf(
163            '%s-%s-to-%s',
164            sanitize_title( $label ),
165            false === $from_ts ? '' : gmdate( 'Y-m-d', $from_ts ),
166            false === $to_ts ? '' : gmdate( 'Y-m-d', $to_ts )
167        );
168    }
169
170    /**
171     * Get batch limit for a report type.
172     *
173     * @param string $report_key The report key.
174     * @return int|\WP_Error The batch limit or error.
175     */
176    public function get_batch_limit( string $report_key ) {
177        $controller = $this->get_controller( $report_key );
178        if ( \is_wp_error( $controller ) ) {
179            return $controller;
180        }
181        return $controller->get_batch_limit();
182    }
183
184    /**
185     * Get controller instance for a report type.
186     *
187     * @param string $report_key The report key.
188     * @return Csv_Report_Controller_Interface|\WP_Error The controller instance or error.
189     */
190    public function get_controller( string $report_key ) {
191        if ( ! isset( $this->controllers[ $report_key ] ) ) {
192            return new \WP_Error(
193                'invalid_report_type',
194                sprintf(
195                    /* translators: %s: Report type key. */
196                    __( 'Invalid report type: %s', 'jetpack-premium-analytics' ),
197                    $report_key
198                ),
199                array( 'status' => 400 )
200            );
201        }
202        return $this->controllers[ $report_key ];
203    }
204}