| @@ -21,9 +21,19 @@ | ||
| 21 | 21 | */ |
| 22 | 22 | class Export extends Controller{ |
| 23 | 23 | |
| 24 | 24 | use Helper; |
| 25 | + | |
| 25 | 26 | /** |
| 27 | + * CSV escape character, passed to fputcsv() explicitly on every call. | |
| 28 | + * | |
| 29 | + * PHP 8.4 deprecates relying on fputcsv()'s default $escape, because that | |
| 30 | + * default will change in a later release. Passing the current default (a | |
| 31 | + * backslash) keeps the output byte-identical on PHP 7.4 through 8.5. | |
| 32 | + */ | |
| 33 | + const CSV_ESCAPE = "\\"; | |
| 34 | + | |
| 35 | + /** | |
| 26 | 36 | * Class constructor |
| 27 | 37 | * |
| 28 | 38 | * @since 0.0.4 |
| 29 | 39 | */ |
| @@ -89,8 +99,21 @@ | ||
| 89 | 99 | |
| 90 | 100 | return $this->transactions_array_to_csv_download($all_transactions, $filename); |
| 91 | 101 | } |
| 92 | 102 | |
| 103 | + /** | |
| 104 | + * Write one CSV row. All CSV writes go through here so none can rely on the | |
| 105 | + * deprecated default $escape. | |
| 106 | + * | |
| 107 | + * @param resource $handle Open, writable stream. | |
| 108 | + * @param array $fields Row values. | |
| 109 | + * @param string $delimiter Field delimiter. | |
| 110 | + * @return int|false Bytes written, or false on failure. | |
| 111 | + */ | |
| 112 | + public static function put_csv_row($handle, array $fields, $delimiter = ";") { | |
| 113 | + return fputcsv($handle, $fields, $delimiter, "\"", self::CSV_ESCAPE); | |
| 114 | + } | |
| 115 | + | |
| 93 | 116 | public function transactions_array_to_csv_download($array, $filename = "export.csv", $delimiter=";") { |
| 94 | 117 | $wp_date_format = get_option('date_format'); |
| 95 | 118 | $wp_time_format = get_option('time_format'); |
| 96 | 119 | |
| @@ -95,9 +118,9 @@ | ||
| 95 | 118 | $wp_time_format = get_option('time_format'); |
| 96 | 119 | |
| 97 | 120 | $f = fopen('php://memory', 'w'); |
| 98 | 121 | |
| 99 | - fputcsv($f, $this->export_transactions_heading(), $delimiter); | |
| 122 | + self::put_csv_row($f, $this->export_transactions_heading(), $delimiter); | |
| 100 | 123 | |
| 101 | 124 | foreach ($array as $line) { |
| 102 | 125 | $line = (array)$line; |
| 103 | 126 | unset($line['id']); |
| @@ -141,9 +164,9 @@ | ||
| 141 | 164 | $line['payment_date'], |
| 142 | 165 | ! empty( $line['form_fields_info'] ) ? maybe_serialize( $line['form_fields_info'] ) : '', |
| 143 | 166 | ); |
| 144 | 167 | |
| 145 | - fputcsv($f, $line_formatted, $delimiter); | |
| 168 | + self::put_csv_row($f, $line_formatted, $delimiter); | |
| 146 | 169 | } |
| 147 | 170 | |
| 148 | 171 | // reset the file pointer to the start of the file |
| 149 | 172 | fseek($f, 0); |
| @@ -179,11 +202,10 @@ | ||
| 179 | 202 | /** |
| 180 | 203 | * The fields a transaction contributes to the CSV: customer name and email, plus |
| 181 | 204 | * whatever `better_payment/export/form_fields_info` adds. |
| 182 | 205 | * |
| 183 | - * Pro adds the subscription keys its recurring and split checkouts store. Lite's | |
| 184 | - * copy of that (ProSubscriptionTransactionData::export_fields()) serves only an | |
| 185 | - * active Pro that does not (<= 1.4.3); a free install exports name and email. | |
| 206 | + * Pro adds the subscription keys its recurring and split checkouts store; a free | |
| 207 | + * install exports name and email. | |
| 186 | 208 | * |
| 187 | 209 | * @param mixed $form_fields_info Serialized or unserialized form_fields_info. |
| 188 | 210 | * @return array |
| 189 | 211 | */ |
| @@ -212,12 +234,8 @@ | ||
| 212 | 234 | 'customer_email' => $bp_transaction_customer_email, |
| 213 | 235 | ]; |
| 214 | 236 | |
| 215 | 237 | $raw_fields = is_array( $bp_form_fields_info ) ? $bp_form_fields_info : []; |
| 216 | - | |
| 217 | - if ( ProSubscriptionTransactionData::lite_copy_applies() ) { | |
| 218 | - $formatted = ProSubscriptionTransactionData::export_fields( $formatted, $raw_fields ); | |
| 219 | - } | |
| 220 | 238 | |
| 221 | 239 | $filtered = apply_filters( 'better_payment/export/form_fields_info', $formatted, $raw_fields ); |
| 222 | 240 | |
| 223 | 241 | return is_array( $filtered ) ? $filtered : $formatted; |