Passed
Push — master ( e53b80...bdf164 )
by Brian
04:19
created

GetPaid_Graph_Downloader::prepare_handler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
/**
3
 * Contains the class that downloads a single report.
4
 *
5
 *
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * GetPaid_Graph_Downloader Class.
12
 */
13
class GetPaid_Graph_Downloader {
14
15
	/**
16
	 * @var GetPaid_Reports_Report
17
	 */
18
	public $handler;
19
20
	/**
21
	 * Class constructor.
22
	 *
23
	 */
24
	public function __construct() {
25
		$this->handler = new GetPaid_Reports_Report();
26
	}
27
28
	/**
29
	 * Prepares the datastore handler.
30
	 *
31
	 * @return GetPaid_Reports_Report_Items|GetPaid_Reports_Report_Gateways|GetPaid_Reports_Report_Discounts
32
	 */
33
	public function prepare_handler( $graph ) {
34
35
		if ( empty( $this->handler->views[ $graph ] ) ) {
36
			wp_die( __( 'Invalid Graph', 'invoicing' ), 400 );
37
		}
38
39
		return new $this->handler->views[ $graph ]['class']();
40
41
	}
42
43
	/**
44
	 * Prepares the output stream.
45
	 *
46
	 * @return resource
47
	 */
48
	public function prepare_output() {
49
50
		$output  = fopen( 'php://output', 'w' );
51
52
		if ( false === $output ) {
53
			wp_die( __( 'Unsupported server', 'invoicing' ), 500 );
54
		}
55
56
		return $output;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $output could also return false which is incompatible with the documented return type resource. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
57
	}
58
59
	/**
60
	 * Prepares the file type.
61
	 *
62
	 * @return string
63
	 */
64
	public function prepare_file_type( $graph ) {
65
66
		$file_type = empty( $_GET['file_type'] ) ? 'csv' : sanitize_text_field( $_GET['file_type'] );
67
		$file_name = "getpaid-$graph-" . current_time( 'Y-m-d' );
68
69
		header( "Content-Type:application/$file_type" );
70
		header( "Content-Disposition:attachment;filename=$file_name.$file_type" );
71
72
		return $file_type;
73
	}
74
75
	/**
76
	 * Handles the actual download.
77
	 *
78
	 */
79
	public function download( $graph ) {
80
		global $wpdb;
81
82
		$handler   = $this->prepare_handler( $graph );
83
		$stream    = $this->prepare_output();
84
		$stats     = $wpdb->get_results( $handler->get_sql( $handler->get_range() ) );
85
		$headers   = array( $handler->field, 'total', 'total_raw' );
86
		$file_type = $this->prepare_file_type( $graph );
87
88
		if ( 'csv' == $file_type ) {
89
			$this->download_csv( $stats, $stream, $headers );
90
		} else if( 'xml' == $file_type ) {
91
			$this->download_xml( $stats, $stream, $headers );
92
		} else {
93
			$this->download_json( $stats, $stream, $headers );
94
		}
95
96
		fclose( $stream );
97
		exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
98
	}
99
100
	/**
101
	 * Downloads graph as csv
102
	 *
103
	 * @param array $stats The stats being downloaded.
104
	 * @param resource $stream The stream to output to.
105
	 * @param array $headers The fields to stream.
106
	 * @since       1.0.19
107
	 */
108
	public function download_csv( $stats, $stream, $headers ) {
109
110
		// Output the csv column headers.
111
		fputcsv( $stream, $headers );
112
113
		// Loop through 
114
		foreach ( $stats as $stat ) {
115
			$row  = array_values( $this->prepare_row( $stat, $headers ) );
116
			fputcsv( $stream, $row );
117
		}
118
119
	}
120
121
	/**
122
	 * Downloads graph as json
123
	 *
124
	 * @param array $stats The stats being downloaded.
125
	 * @param resource $stream The stream to output to.
126
	 * @param array $headers The fields to stream.
127
	 * @since       1.0.19
128
	 */
129
	public function download_json( $stats, $stream, $headers ) {
130
131
		$prepared = array();
132
133
		// Loop through 
134
		foreach ( $stats as $stat ) {
135
			$prepared[] = $this->prepare_row( $stat, $headers );
136
		}
137
138
		fwrite( $stream, wp_json_encode( $prepared ) );
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($prepared) can also be of type false; however, parameter $string of fwrite() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

138
		fwrite( $stream, /** @scrutinizer ignore-type */ wp_json_encode( $prepared ) );
Loading history...
139
140
	}
141
142
	/**
143
	 * Downloads graph as xml
144
	 *
145
	 * @param array $stats The stats being downloaded.
146
	 * @param resource $stream The stream to output to.
147
	 * @param array $headers The fields to stream.
148
	 * @since       1.0.19
149
	 */
150
	public function download_xml( $stats, $stream, $headers ) {
151
152
		$prepared = array();
153
154
		// Loop through 
155
		foreach ( $stats as $stat ) {
156
			$prepared[] = $this->prepare_row( $stat, $headers );
157
		}
158
159
		$xml = new SimpleXMLElement('<?xml version="1.0"?><data></data>');
160
		$this->convert_array_xml( $prepared, $xml );
161
162
		fwrite( $stream, $xml->asXML() );
163
164
	}
165
166
	/**
167
	 * Converts stats array to xml
168
	 *
169
	 * @access      public
170
	 * @since      1.0.19
171
	 */
172
	public function convert_array_xml( $data, $xml ) {
173
174
		// Loop through 
175
		foreach ( $data as $key => $value ) {
176
177
			$key = preg_replace( "/[^A-Za-z0-9_\-]/", '', $key );
178
179
			if ( is_array( $value ) ) {
180
181
				if ( is_numeric( $key ) ){
182
					$key = 'item'.$key; //dealing with <0/>..<n/> issues
183
				}
184
185
				$subnode = $xml->addChild( $key );
186
				$this->convert_array_xml( $value, $subnode );
187
188
			} else {
189
				$xml->addChild( $key, htmlspecialchars( $value ) );
190
			}
191
192
		}
193
194
	}
195
196
	/**
197
	 * Prepares a single row for download.
198
	 *
199
	 * @param stdClass|array $row The row to prepare..
200
	 * @param array $fields The fields to stream.
201
	 * @since       1.0.19
202
	 * @return array
203
	 */
204
	public function prepare_row( $row, $fields ) {
205
206
		$prepared = array();
207
		$row      = (array) $row;
208
209
		foreach ( $fields as $field ) {
210
211
			if ( $field === 'total' ) {
212
				$prepared[ $field ] = html_entity_decode( strip_tags( wpinv_price( $row['total'] ) ), ENT_QUOTES );
213
				continue;
214
			}
215
216
			if ( $field === 'total_raw' ) {
217
				$prepared[ $field ] = wpinv_round_amount( wpinv_sanitize_amount( $row['total'] ) );
218
				continue;
219
			}
220
221
			$prepared[ $field ] = strip_tags( $row[ $field ] );
222
223
		}
224
225
		return $prepared;
226
	}
227
228
229
}
230