| @@ -7,8 +7,10 @@ | ||
| 7 | 7 | * @author Tobias Bäthge |
| 8 | 8 | * @since 1.0.0 |
| 9 | 9 | */ |
| 10 | 10 | |
| 11 | +declare(strict_types=1); | |
| 12 | + | |
| 11 | 13 | // Prohibit direct script loading. |
| 12 | 14 | defined( 'ABSPATH' ) || die( 'No direct script access allowed!' ); |
| 13 | 15 | |
| 14 | 16 | /** |
| @@ -23,34 +25,34 @@ | ||
| 23 | 25 | |
| 24 | 26 | /** |
| 25 | 27 | * File/Data Formats that are available for import. |
| 26 | 28 | * |
| 29 | + * The constructor also adds 'html', if the PHP prerequisites are met. | |
| 30 | + * | |
| 27 | 31 | * @since 1.0.0 |
| 28 | - * @var array | |
| 32 | + * @var string[] | |
| 29 | 33 | */ |
| 30 | - public $import_formats = array(); | |
| 34 | + public array $import_formats = array( 'csv', 'json', 'xls', 'xlsx' ); | |
| 31 | 35 | |
| 32 | 36 | /** |
| 33 | 37 | * Whether HTML import support is available in the PHP installation on the server. |
| 34 | 38 | * |
| 35 | 39 | * @since 1.0.0 |
| 36 | - * @var bool | |
| 37 | 40 | */ |
| 38 | - public $html_import_support_available = false; | |
| 41 | + public bool $html_import_support_available = false; | |
| 39 | 42 | |
| 40 | 43 | /** |
| 41 | 44 | * Data to be imported. |
| 42 | 45 | * |
| 43 | 46 | * @since 1.0.0 |
| 44 | - * @var string | |
| 45 | 47 | */ |
| 46 | - protected $import_data; | |
| 48 | + protected string $import_data; | |
| 47 | 49 | |
| 48 | 50 | /** |
| 49 | 51 | * Imported table. |
| 50 | 52 | * |
| 51 | 53 | * @since 1.0.0 |
| 52 | - * @var array|false | |
| 54 | + * @var array<string, mixed>|false | |
| 53 | 55 | */ |
| 54 | 56 | protected $imported_table = false; |
| 55 | 57 | |
| 56 | 58 | /** |
| @@ -62,17 +64,11 @@ | ||
| 62 | 64 | if ( class_exists( 'DOMDocument', false ) && function_exists( 'simplexml_import_dom' ) && function_exists( 'libxml_use_internal_errors' ) ) { |
| 63 | 65 | $this->html_import_support_available = true; |
| 64 | 66 | } |
| 65 | 67 | |
| 66 | - // Initiate here, because function call not possible outside a class method. | |
| 67 | - $this->import_formats = array(); | |
| 68 | - $this->import_formats['csv'] = __( 'CSV - Character-Separated Values', 'tablepress' ); | |
| 69 | 68 | if ( $this->html_import_support_available ) { |
| 70 | - $this->import_formats['html'] = __( 'HTML - Hypertext Markup Language', 'tablepress' ); | |
| 69 | + $this->import_formats[] = 'html'; | |
| 71 | 70 | } |
| 72 | - $this->import_formats['json'] = __( 'JSON - JavaScript Object Notation', 'tablepress' ); | |
| 73 | - $this->import_formats['xls'] = __( 'XLS - Microsoft Excel 97-2003 (experimental)', 'tablepress' ); | |
| 74 | - $this->import_formats['xlsx'] = __( 'XLSX - Microsoft Excel 2007-2019 (experimental)', 'tablepress' ); | |
| 75 | 71 | } |
| 76 | 72 | |
| 77 | 73 | /** |
| 78 | 74 | * Import a table. |
| @@ -80,11 +76,19 @@ | ||
| 80 | 76 | * @since 1.0.0 |
| 81 | 77 | * |
| 82 | 78 | * @param string $format Import format. |
| 83 | 79 | * @param string $data Data to import. |
| 84 | - * @return array|false Table array on success, false on error. | |
| 80 | + * @return array<string, mixed>|false Table array on success, false on error. | |
| 85 | 81 | */ |
| 86 | - public function import_table( $format, $data ) { | |
| 82 | + public function import_table( string $format, string $data ) /* : array|false */ { | |
| 83 | + /** | |
| 84 | + * Filters the data that is to be imported. | |
| 85 | + * | |
| 86 | + * @since 1.8.1 | |
| 87 | + * | |
| 88 | + * @param string $data Data to import. | |
| 89 | + * @param string $format Import format. | |
| 90 | + */ | |
| 87 | 91 | $this->import_data = apply_filters( 'tablepress_import_table_data', $data, $format ); |
| 88 | 92 | |
| 89 | 93 | if ( ! in_array( $format, array( 'xlsx', 'xls' ), true ) ) { |
| 90 | 94 | $this->fix_table_encoding(); |
| @@ -109,14 +113,18 @@ | ||
| 109 | 113 | default: |
| 110 | 114 | return false; |
| 111 | 115 | } |
| 112 | 116 | |
| 117 | + if ( false === $this->imported_table ) { | |
| 118 | + return false; | |
| 119 | + } | |
| 120 | + | |
| 113 | 121 | // Make sure that cells are stored as strings. |
| 114 | 122 | array_walk_recursive( |
| 115 | 123 | $this->imported_table['data'], |
| 116 | - static function( &$cell_content, $col_idx ) { | |
| 124 | + static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void { | |
| 117 | 125 | $cell_content = (string) $cell_content; |
| 118 | - } | |
| 126 | + }, | |
| 119 | 127 | ); |
| 120 | 128 | |
| 121 | 129 | return $this->imported_table; |
| 122 | 130 | } |
| @@ -125,9 +133,9 @@ | ||
| 125 | 133 | * Import CSV data. |
| 126 | 134 | * |
| 127 | 135 | * @since 1.0.0 |
| 128 | 136 | */ |
| 129 | - protected function import_csv() { | |
| 137 | + protected function import_csv(): void { | |
| 130 | 138 | $csv_parser = TablePress::load_class( 'CSV_Parser', 'csv-parser.class.php', 'libraries' ); |
| 131 | 139 | $csv_parser->load_data( $this->import_data ); |
| 132 | 140 | $delimiter = $csv_parser->find_delimiter(); |
| 133 | 141 | $data = $csv_parser->parse( $delimiter ); |
| @@ -140,9 +148,9 @@ | ||
| 140 | 148 | * Import HTML data. |
| 141 | 149 | * |
| 142 | 150 | * @since 1.0.0 |
| 143 | 151 | */ |
| 144 | - protected function import_html() { | |
| 152 | + protected function import_html(): void { | |
| 145 | 153 | if ( ! $this->html_import_support_available ) { |
| 146 | 154 | return; |
| 147 | 155 | } |
| 148 | 156 | |
| @@ -163,15 +171,15 @@ | ||
| 163 | 171 | * Import JSON data. |
| 164 | 172 | * |
| 165 | 173 | * @since 1.0.0 |
| 166 | 174 | */ |
| 167 | - protected function import_json() { | |
| 175 | + protected function import_json(): void { | |
| 168 | 176 | $json_table = json_decode( $this->import_data, true ); |
| 169 | 177 | |
| 170 | 178 | // Check if JSON could be decoded. |
| 171 | 179 | if ( is_null( $json_table ) ) { |
| 172 | 180 | $json_error = json_last_error_msg(); |
| 173 | - $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . "</strong><br /><br />JSON error: {$json_error}<br />"; | |
| 181 | + $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . "</strong><br><br>JSON error: {$json_error}<br>"; | |
| 174 | 182 | wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) ); |
| 175 | 183 | } |
| 176 | 184 | |
| 177 | 185 | // Specifically cast to an array again. |
| @@ -196,9 +204,9 @@ | ||
| 196 | 204 | * Import Microsoft Excel 97-2003 data. |
| 197 | 205 | * |
| 198 | 206 | * @since 1.1.0 |
| 199 | 207 | */ |
| 200 | - protected function import_xls() { | |
| 208 | + protected function import_xls(): void { | |
| 201 | 209 | $excel_reader = TablePress::load_class( 'Spreadsheet_Excel_Reader', 'excel-reader.class.php', 'libraries', $this->import_data ); |
| 202 | 210 | |
| 203 | 211 | // Loop through Excel file and retrieve value and colspan/rowspan properties for each cell. |
| 204 | 212 | $sheet = 0; // 0 means first sheet of the Workbook |
| @@ -249,11 +257,11 @@ | ||
| 249 | 257 | foreach ( $table as &$row ) { |
| 250 | 258 | foreach ( $row as &$cell ) { |
| 251 | 259 | $cell = $cell['val']; |
| 252 | 260 | } |
| 253 | - unset( $cell ); | |
| 261 | + unset( $cell ); // Unset use-by-reference parameter of foreach loop. | |
| 254 | 262 | } |
| 255 | - unset( $row ); | |
| 263 | + unset( $row ); // Unset use-by-reference parameter of foreach loop. | |
| 256 | 264 | |
| 257 | 265 | $this->imported_table = array( 'data' => $table ); |
| 258 | 266 | } |
| 259 | 267 | |
| @@ -261,14 +269,14 @@ | ||
| 261 | 269 | * Import Microsoft Excel 2007-2019 data. |
| 262 | 270 | * |
| 263 | 271 | * @since 1.1.0 |
| 264 | 272 | */ |
| 265 | - protected function import_xlsx() { | |
| 273 | + protected function import_xlsx(): void { | |
| 266 | 274 | TablePress::load_file( 'simplexlsx.class.php', 'libraries' ); |
| 267 | 275 | $xlsx_file = \Shuchkin\SimpleXLSX::parse( $this->import_data, true ); |
| 268 | 276 | |
| 269 | 277 | if ( ! $xlsx_file ) { |
| 270 | - $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . '</strong><br /><br />' . \Shuchkin\SimpleXLSX::parseError() . '<br />'; | |
| 278 | + $output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . '</strong><br><br>' . \Shuchkin\SimpleXLSX::parseError() . '<br>'; | |
| 271 | 279 | wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) ); |
| 272 | 280 | } |
| 273 | 281 | |
| 274 | 282 | $this->imported_table = array( 'data' => $xlsx_file->rows() ); |
| @@ -280,12 +288,12 @@ | ||
| 280 | 288 | * @since 1.0.0 |
| 281 | 289 | * |
| 282 | 290 | * @link http://stevephillips.me/blog/dealing-php-and-character-encoding |
| 283 | 291 | */ |
| 284 | - protected function fix_table_encoding() { | |
| 292 | + protected function fix_table_encoding(): void { | |
| 285 | 293 | // Check and remove possible UTF-8 Byte-Order Mark (BOM). |
| 286 | 294 | $bom = pack( 'CCC', 0xef, 0xbb, 0xbf ); |
| 287 | - if ( 0 === strncmp( $this->import_data, $bom, 3 ) ) { | |
| 295 | + if ( str_starts_with( $this->import_data, $bom ) ) { | |
| 288 | 296 | $this->import_data = substr( $this->import_data, 3 ); |
| 289 | 297 | // If data has a BOM, it's UTF-8, so further checks unnecessary. |
| 290 | 298 | return; |
| 291 | 299 | } |
| @@ -295,9 +303,9 @@ | ||
| 295 | 303 | return; |
| 296 | 304 | } |
| 297 | 305 | |
| 298 | 306 | // Check for possible UTF-16 BOMs ("little endian" and "big endian") and try to convert the data to UTF-8. |
| 299 | - if ( "\xFF\xFE" === substr( $this->import_data, 0, 2 ) || "\xFE\xFF" === substr( $this->import_data, 0, 2 ) ) { | |
| 307 | + if ( str_starts_with( $this->import_data, "\xFF\xFE" ) || str_starts_with( $this->import_data, "\xFE\xFF" ) ) { | |
| 300 | 308 | // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
| 301 | 309 | $data = @iconv( 'UTF-16', 'UTF-8', $this->import_data ); |
| 302 | 310 | if ( false !== $data ) { |
| 303 | 311 | $this->import_data = $data; |
| @@ -309,9 +317,9 @@ | ||
| 309 | 317 | if ( function_exists( 'mb_detect_encoding' ) ) { |
| 310 | 318 | $current_encoding = mb_detect_encoding( $this->import_data, 'ASCII, UTF-8, ISO-8859-1' ); |
| 311 | 319 | if ( 'UTF-8' !== $current_encoding ) { |
| 312 | 320 | // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged |
| 313 | - $data = @iconv( $current_encoding, 'UTF-8', $this->import_data ); | |
| 321 | + $data = @iconv( $current_encoding, 'UTF-8', $this->import_data ); // @phpstan-ignore argument.type | |
| 314 | 322 | if ( false !== $data ) { |
| 315 | 323 | $this->import_data = $data; |
| 316 | 324 | return; |
| 317 | 325 | } |
| @@ -316,7 +324,25 @@ | ||
| 316 | 324 | return; |
| 317 | 325 | } |
| 318 | 326 | } |
| 319 | 327 | } |
| 328 | + } | |
| 329 | + | |
| 330 | + /** | |
| 331 | + * Replaces Windows line breaks \r\n with Unix line breaks \n. | |
| 332 | + * | |
| 333 | + * This function uses call by reference to save PHP memory on large arrays. | |
| 334 | + * | |
| 335 | + * @since 2.0.0 | |
| 336 | + * | |
| 337 | + * @param array<int, array<int, string>> $an_array Array in which Windows line breaks should be replaced by Unix line breaks. | |
| 338 | + */ | |
| 339 | + protected function normalize_line_endings( array &$an_array ): void { | |
| 340 | + array_walk_recursive( | |
| 341 | + $an_array, | |
| 342 | + static function ( string &$cell_content, int $col_idx ): void { | |
| 343 | + $cell_content = str_replace( "\r\n", "\n", $cell_content ); | |
| 344 | + }, | |
| 345 | + ); | |
| 320 | 346 | } |
| 321 | 347 | |
| 322 | 348 | } // class TablePress_Import_Legacy |