$action, '_wpnonce' => wp_create_nonce( $action ), ], admin_url( 'admin-post.php' ) ); } public static function guard( string $action ): void { if ( ! current_user_can( 'manage_woocommerce' ) ) { wp_die( esc_html__( 'You do not have permission to export this.', 'advently' ), 403 ); } check_admin_referer( $action ); } /** * Sends download headers and returns the output stream, BOM written. * * @return resource */ public static function open( string $name ) { nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="' . sanitize_file_name( $name ) . '.csv"' ); $out = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- streaming a download. // UTF-8 byte order mark, so spreadsheet apps read accented text correctly. fwrite( $out, "\xEF\xBB\xBF" ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- streaming a download. return $out; } public static function download(): void { self::guard( self::ACTION ); $config = Option::get(); $out = self::open( 'advently-subscribers-' . gmdate( 'Y-m-d' ) ); self::row( $out, [ __( 'Email', 'advently' ), __( 'Signed up', 'advently' ), __( 'Agreed to offers', 'advently' ), __( 'Doors opened', 'advently' ), __( 'Customer ID', 'advently' ) ] ); ( new SubscriberList( $config['start'] ) )->each( static function ( array $row ) use ( $out ) { self::row( $out, array_map( [ self::class, 'cell' ], [ $row['email'], $row['joined'], $row['consent'], $row['opened'], $row['user_id'] ? $row['user_id'] : '' ] ) ); } ); fclose( $out ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- streaming a download. exit; } /** * One CSV line. RFC 4180 quoting only (no backslash escape), which PHP 8.4 asks * callers to choose explicitly. * * @param resource $out Output stream from open(). * @param array $fields Cells. */ public static function row( $out, array $fields ): void { fputcsv( $out, $fields, ',', '"', '' ); } /** * Cells starting with = + - @ would run as formulas in a spreadsheet. * * @param mixed $value Cell value. */ public static function cell( $value ): string { $value = (string) $value; return '' !== $value && false !== strpos( "=+-@\t\r", $value[0] ) ? "'" . $value : $value; } }