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 0.1.0
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        $this->controllers[ $report_key ] = $controller;
44
45        return true;
46    }
47
48    /**
49     * Check if a report type is registered.
50     *
51     * @param string $report_key The report key.
52     * @return bool True if registered, false otherwise.
53     */
54    public function is_registered( string $report_key ): bool {
55        return isset( $this->controllers[ $report_key ] );
56    }
57
58    /**
59     * Get all registered report keys.
60     *
61     * @return string[] Array of registered report keys.
62     */
63    public function get_registered_reports(): array {
64        return array_keys( $this->controllers );
65    }
66
67    /**
68     * Get data endpoint for a report type.
69     *
70     * @param string $report_key The report key.
71     * @return string|\WP_Error The data endpoint or error.
72     */
73    public function get_data_endpoint( string $report_key ) {
74        $controller = $this->get_controller( $report_key );
75        if ( \is_wp_error( $controller ) ) {
76            return $controller;
77        }
78
79        return $controller->get_data_endpoint();
80    }
81
82    /**
83     * Get columns for a report type.
84     *
85     * @param string      $report_key         The report key.
86     * @param bool        $include_comparison Whether to include comparison columns.
87     * @param string|null $interval           Optional time interval for dynamic headers.
88     * @return array|\WP_Error Column definitions or error.
89     */
90    public function get_columns( string $report_key, bool $include_comparison = false, ?string $interval = null ) {
91        $controller = $this->get_controller( $report_key );
92        if ( \is_wp_error( $controller ) ) {
93            return $controller;
94        }
95
96        $columns = $controller->get_column_headers( $interval );
97
98        if ( $include_comparison ) {
99            $comparison_columns = array();
100            foreach ( $columns as $key => $label ) {
101                $comparison_columns[ Report_Data_Fetcher::COMPARISON_INDEX_PREFIX . $key ] = sprintf(
102                    /* translators: %s: the column label, e.g. "Orders". */
103                    __( '%s (Previous Period)', 'jetpack-premium-analytics-pkg' ),
104                    $label
105                );
106            }
107            $columns = array_merge( $columns, $comparison_columns );
108        }
109
110        return $columns;
111    }
112
113    /**
114     * Get row formatter for a report type.
115     *
116     * @param string      $report_key The report key.
117     * @param string|null $interval   Optional time interval for formatting.
118     * @return callable|\WP_Error The row formatter callback or error.
119     */
120    public function get_row_formatter( string $report_key, ?string $interval = null ) {
121        $controller = $this->get_controller( $report_key );
122        if ( \is_wp_error( $controller ) ) {
123            return $controller;
124        }
125        // Return a closure that captures the interval to avoid race conditions.
126        return function ( $item ) use ( $controller, $interval ) {
127            return $controller->format_row_with_comparison( $item, $interval );
128        };
129    }
130
131    /**
132     * Get report label.
133     *
134     * @param string $report_key The report key.
135     * @return string|\WP_Error The report label or error.
136     */
137    public function get_label( string $report_key ) {
138        $controller = $this->get_controller( $report_key );
139        if ( \is_wp_error( $controller ) ) {
140            return $controller;
141        }
142        return $controller->get_report_label();
143    }
144
145    /**
146     * Build a filename base (no extension) for a report export: "<label>-<from>-to-<to>".
147     *
148     * @param string $report_key The report key.
149     * @param array  $params     Request parameters (reads 'from' and 'to').
150     * @return string The sanitized filename base.
151     */
152    public function build_filename( string $report_key, array $params ): string {
153        $label = $this->get_label( $report_key );
154        if ( \is_wp_error( $label ) ) {
155            $label = $report_key;
156        }
157
158        $from_ts = empty( $params['from'] ) ? false : strtotime( $params['from'] );
159        $to_ts   = empty( $params['to'] ) ? false : strtotime( $params['to'] );
160
161        return sprintf(
162            '%s-%s-to-%s',
163            sanitize_title( $label ),
164            false === $from_ts ? '' : gmdate( 'Y-m-d', $from_ts ),
165            false === $to_ts ? '' : gmdate( 'Y-m-d', $to_ts )
166        );
167    }
168
169    /**
170     * Get batch limit for a report type.
171     *
172     * @param string $report_key The report key.
173     * @return int|\WP_Error The batch limit or error.
174     */
175    public function get_batch_limit( string $report_key ) {
176        $controller = $this->get_controller( $report_key );
177        if ( \is_wp_error( $controller ) ) {
178            return $controller;
179        }
180        return $controller->get_batch_limit();
181    }
182
183    /**
184     * Get controller instance for a report type.
185     *
186     * @param string $report_key The report key.
187     * @return Csv_Report_Controller_Interface|\WP_Error The controller instance or error.
188     */
189    public function get_controller( string $report_key ) {
190        if ( ! isset( $this->controllers[ $report_key ] ) ) {
191            return new \WP_Error(
192                'invalid_report_type',
193                sprintf(
194                    /* translators: %s: Report type key. */
195                    __( 'Invalid report type: %s', 'jetpack-premium-analytics-pkg' ),
196                    $report_key
197                ),
198                array( 'status' => 400 )
199            );
200        }
201        return $this->controllers[ $report_key ];
202    }
203}