PluginProbe
TablePress – Tables in WordPress made easy / 3.0
TablePress – Tables in WordPress made easy v3.0
3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 2.4.4 All 44 releases
← All changes | models/model-table.php +214 -180 2.0.43.0 View file →
@@ -24,38 +24,35 @@
24 24 /**
25 25 * Instance of the Post Type Model.
26 26 *
27 27 * @since 1.0.0
28 - * @var TablePress_Post_Model
29 28 */
30 - protected $model_post;
29 + protected \TablePress_Post_Model $model_post;
31 30
32 31 /**
33 32 * Name of the Post Meta Field for table options.
34 33 *
35 34 * @since 1.0.0
36 - * @var string
37 35 */
38 - protected $table_options_field_name = '_tablepress_table_options';
36 + protected string $table_options_field_name = '_tablepress_table_options';
39 37
40 38 /**
41 39 * Name of the Post Meta Field for table visibility.
42 40 *
43 41 * @since 1.0.0
44 - * @var string
45 42 */
46 - protected $table_visibility_field_name = '_tablepress_table_visibility';
43 + protected string $table_visibility_field_name = '_tablepress_table_visibility';
47 44
48 45 /**
49 46 * Default set of tables.
50 47 *
51 48 * @since 1.0.0
52 - * @var array $args {
53 - * @type int $last_id Last table ID that was given to a new table.
54 - * @type array $table_post Connections between table ID and post ID (key: table ID, value: post ID).
49 + * @var array{last_id: int, table_post: array<string, int>} {
50 + * @type int $last_id Last table ID that was given to a new table.
51 + * @type array<string, int> $table_post Connections between table ID and post ID (key: table ID, value: post ID).
55 52 * }
56 53 */
57 - protected $default_tables = array(
54 + protected array $default_tables = array(
58 55 'last_id' => 0,
59 56 'table_post' => array(),
60 57 );
61 58
@@ -62,11 +59,10 @@
62 59 /**
63 60 * Instance of WP_Option class for the list of tables.
64 61 *
65 62 * @since 1.0.0
66 - * @var TablePress_WP_Option
67 63 */
68 - protected $tables;
64 + protected \TablePress_WP_Option $tables;
69 65
70 66 /**
71 67 * Init the Table model by instantiating a Post model and loading the list of tables option.
72 68 *
@@ -87,11 +83,11 @@
87 83 * Get the tables option, which holds the connection between table ID and post ID.
88 84 *
89 85 * @since 1.0.0
90 86 *
91 - * @return array Current set of tables.
87 + * @return mixed[] Current set of tables.
92 88 */
93 - public function _debug_get_tables() {
89 + public function _debug_get_tables(): array {
94 90 return $this->tables->get();
95 91 }
96 92
97 93 /**
@@ -98,11 +94,11 @@
98 94 * Update the tables option, which holds the connection between table ID and post ID.
99 95 *
100 96 * @since 1.0.0
101 97 *
102 - * @param array $tables New set of tables.
98 + * @param mixed[] $tables New set of tables.
103 99 */
104 - public function _debug_update_tables( array $tables ) {
100 + public function _debug_update_tables( array $tables ): void {
105 101 $this->tables->update( $tables );
106 102 }
107 103
108 104 /**
@@ -109,16 +105,13 @@
109 105 * Convert a table to a post, which can be stored in the database.
110 106 *
111 107 * @since 1.0.0
112 108 *
113 - * @param array $table Table.
114 - * @param int $post_id Post ID of an existing table, or -1 for a new table.
115 - * @return array Post.
109 + * @param array<string, mixed> $table Table.
110 + * @param int $post_id Post ID of an existing table, or -1 for a new table.
111 + * @return array<string, mixed> Post.
116 112 */
117 - protected function _table_to_post( array $table, $post_id ) {
118 - // Run filters on content in each cell and other fields.
119 - $table = $this->filter_content( $table );
120 -
113 + protected function _table_to_post( array $table, int $post_id ): array {
121 114 // Sanitize each cell, table name, and table description, if the user is not allowed to work with unfiltered HTML.
122 115 if ( ! current_user_can( 'unfiltered_html' ) ) {
123 116 $table = $this->sanitize( $table );
124 117 }
@@ -146,11 +139,11 @@
146 139 *
147 140 * @param WP_Post $post Post.
148 141 * @param string $table_id Table ID.
149 142 * @param bool $load_data Whether the table data shall be loaded.
150 - * @return array Table.
143 + * @return array<string, mixed> Table.
151 144 */
152 - protected function _post_to_table( $post, $table_id, $load_data ) {
145 + protected function _post_to_table( WP_Post $post, string $table_id, bool $load_data ): array {
153 146 $table = array(
154 147 'id' => $table_id,
155 148 'name' => $post->post_title,
156 149 'description' => $post->post_excerpt,
@@ -186,11 +179,11 @@
186 179 *
187 180 * @param string $table_id Table ID.
188 181 * @param bool $load_data Whether the table data shall be loaded.
189 182 * @param bool $load_options_visibility Whether the table options and table visibility shall be loaded.
190 - * @return array|WP_Error Table as an array on success, WP_Error on error.
183 + * @return array<string, mixed>|WP_Error Table as an array on success, WP_Error on error.
191 184 */
192 - public function load( $table_id, $load_data = true, $load_options_visibility = true ) {
185 + public function load( string $table_id, bool $load_data = true, bool $load_options_visibility = true ) /* : array|WP_Error */ {
193 186 if ( empty( $table_id ) ) {
194 187 return new WP_Error( 'table_load_empty_table_id' );
195 188 }
196 189
@@ -218,11 +211,11 @@
218 211 * @since 1.0.0
219 212 *
220 213 * @param bool $prime_meta_cache Optional. Whether the prime the post meta cache when loading the posts.
221 214 * @param bool $run_filter Optional. Whether to run a filter on the list of table IDs.
222 - * @return array Array of table IDs.
215 + * @return string[] Array of table IDs.
223 216 */
224 - public function load_all( $prime_meta_cache = true, $run_filter = true ) {
217 + public function load_all( bool $prime_meta_cache = true, bool $run_filter = true ): array {
225 218 $table_post = $this->tables->get( 'table_post' );
226 219 if ( empty( $table_post ) ) {
227 220 return array();
228 221 }
@@ -232,11 +225,13 @@
232 225
233 226 // This loop now uses the WP cache.
234 227 $table_ids = array();
235 228 foreach ( $table_post as $table_id => $post_id ) {
236 - $table_id = (string) $table_id;
229 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
230 +
237 231 // Load table without data and options to save memory.
238 232 $table = $this->load( $table_id, false, false );
233 +
239 234 // Skip tables that could not be loaded properly.
240 235 if ( ! is_wp_error( $table ) ) {
241 236 $table_ids[] = $table_id;
242 237 }
@@ -247,9 +242,9 @@
247 242 * Filters all table IDs that are loaded.
248 243 *
249 244 * @since 1.4.0
250 245 *
251 - * @param array $table_ids The table IDs that are loaded.
246 + * @param string[] $table_ids The table IDs that are loaded.
252 247 */
253 248 $table_ids = apply_filters( 'tablepress_load_all_tables', $table_ids );
254 249 }
255 250
@@ -260,12 +255,12 @@
260 255 * Sanitize the table to remove undesired HTML code using KSES.
261 256 *
262 257 * @since 1.8.0
263 258 *
264 - * @param array $table Table.
265 - * @return array Sanitized table.
259 + * @param array<string, mixed> $table Table.
260 + * @return array<string, mixed> Sanitized table.
266 261 */
267 - public function sanitize( array $table ) {
262 + public function sanitize( array $table ): array {
268 263 // Sanitize the table name and description.
269 264 $fields = array( 'name', 'description' );
270 265 foreach ( $fields as $field ) {
271 266 $table[ $field ] = wp_kses_post( $table[ $field ] );
@@ -273,9 +268,9 @@
273 268
274 269 // Sanitize each cell.
275 270 foreach ( $table['data'] as $row_idx => $row ) {
276 271 foreach ( $row as $column_idx => $cell_content ) {
277 - $table['data'][ $row_idx ][ $column_idx ] = wp_kses_post( $cell_content ); // Equals wp_filter_post_kses(), but without the unncessary slashes handling.
272 + $table['data'][ $row_idx ][ $column_idx ] = wp_kses_post( $cell_content ); // Equals wp_filter_post_kses(), but without the unnecessary slashes handling.
278 273 }
279 274 }
280 275
281 276 return $table;
@@ -281,55 +276,16 @@
281 276 return $table;
282 277 }
283 278
284 279 /**
285 - * Filter/modify the content of table cells and other fields, e.g. for security hardening.
286 - *
287 - * This is similar to the `sanitize()` method, but executed for all users.
288 - * In 1.10.0, adding `rel="noopener noreferrer"` to all HTML link elements like `<a target=` was added. See https://core.trac.wordpress.org/ticket/43187.
289 - * Since 1.13.0, and on WP 5.6, only `rel="noopener"` is added. See https://core.trac.wordpress.org/ticket/49558.
290 - *
291 - * @since 1.10.0
292 - *
293 - * @param array $table Table.
294 - * @return array Filtered/modified table.
295 - */
296 - public function filter_content( array $table ) {
297 - /**
298 - * Filters whether the contents of table cells and fields should be filtered/modified.
299 - *
300 - * @since 1.10.0
301 - *
302 - * @param bool $filter Whether to filter the content of table cells and other fields. Default true.
303 - */
304 - if ( ! apply_filters( 'tablepress_filter_table_cell_content', true ) ) {
305 - return $table;
306 - }
307 -
308 - // Filter the table name and description.
309 - $fields = array( 'name', 'description' );
310 - foreach ( $fields as $field ) {
311 - $table[ $field ] = wp_targeted_link_rel( $table[ $field ] );
312 - }
313 -
314 - foreach ( $table['data'] as $row_idx => $row ) {
315 - foreach ( $row as $column_idx => $cell_content ) {
316 - $table['data'][ $row_idx ][ $column_idx ] = wp_targeted_link_rel( $cell_content );
317 - }
318 - }
319 -
320 - return $table;
321 - }
322 -
323 - /**
324 280 * Save a table.
325 281 *
326 282 * @since 1.0.0
327 283 *
328 - * @param array $table Table (needs to have $table['id']!).
284 + * @param array<string, mixed> $table Table (needs to have $table['id']!).
329 285 * @return string|WP_Error WP_Error on error, string table ID on success.
330 286 */
331 - public function save( array $table ) {
287 + public function save( array $table ) /* : string|WP_Error */ {
332 288 if ( empty( $table['id'] ) ) {
333 289 return new WP_Error( 'table_save_empty_table_id' );
334 290 }
335 291
@@ -340,11 +296,11 @@
340 296
341 297 $post = $this->_table_to_post( $table, $post_id );
342 298 $new_post_id = $this->model_post->update( $post );
343 299 if ( is_wp_error( $new_post_id ) ) {
344 - // Add an error code to the existing WP_Error.
345 - $new_post_id->add( 'table_save_post_update', '', $post_id );
346 - return $new_post_id;
300 + $error = new WP_Error( 'table_save_post_update', '', $post_id );
301 + $error->merge_from( $new_post_id );
302 + return $error;
347 303 }
348 304 if ( $post_id !== $new_post_id ) {
349 305 return new WP_Error( 'table_save_new_post_id_does_not_match', '', $new_post_id );
350 306 }
@@ -382,20 +338,20 @@
382 338 * Add a new table.
383 339 *
384 340 * @since 1.0.0
385 341 *
386 - * @param array $table Table ($table['id'] is not necessary).
387 - * @param string $copy_or_add Optional. 'copy' if the table is copied, 'add' if it is a new table. Default 'add'.
342 + * @param array<string, mixed> $table Table ($table['id'] is not necessary).
343 + * @param string $copy_or_add Optional. 'copy' if the table is copied, 'add' if it is a new table. Default 'add'.
388 344 * @return string|WP_Error WP_Error on error, string table ID of the new table on success.
389 345 */
390 - public function add( array $table, $copy_or_add = 'add' ) {
346 + public function add( array $table, string $copy_or_add = 'add' ) /* : string|WP_Error */ {
391 347 $post_id = -1; // To insert table.
392 348 $post = $this->_table_to_post( $table, $post_id );
393 349 $new_post_id = $this->model_post->insert( $post );
394 350 if ( is_wp_error( $new_post_id ) ) {
395 - // Add an error code to the existing WP_Error.
396 - $new_post_id->add( 'table_add_post_insert', '' );
397 - return $new_post_id;
351 + $error = new WP_Error( 'table_add_post_insert', '' );
352 + $error->merge_from( $new_post_id );
353 + return $error;
398 354 }
399 355
400 356 $options_saved = $this->_add_table_options( $new_post_id, $table['options'] );
401 357 if ( ! $options_saved ) {
@@ -432,14 +388,14 @@
432 388 *
433 389 * @param string $table_id ID of the table to be copied.
434 390 * @return string|WP_Error WP_Error on error, string table ID of the new table on success.
435 391 */
436 - public function copy( $table_id ) {
392 + public function copy( string $table_id ) /* : string|WP_Error */ {
437 393 $table = $this->load( $table_id, true, true );
438 394 if ( is_wp_error( $table ) ) {
439 - // Add an error code to the existing WP_Error.
440 - $table->add( 'table_copy_table_load', '', $table_id );
441 - return $table;
395 + $error = new WP_Error( 'table_copy_table_load', '', $table_id );
396 + $error->merge_from( $table );
397 + return $error;
442 398 }
443 399
444 400 // Adjust name of copied table.
445 401 if ( '' === trim( $table['name'] ) ) {
@@ -449,19 +405,19 @@
449 405
450 406 // Merge this data into an empty table template.
451 407 $table = $this->prepare_table( $this->get_table_template(), $table, false );
452 408 if ( is_wp_error( $table ) ) {
453 - // Add an error code to the existing WP_Error.
454 - $table->add( 'table_copy_table_prepare', '', $table_id );
455 - return $table;
409 + $error = new WP_Error( 'table_copy_table_prepare', '', $table_id );
410 + $error->merge_from( $table );
411 + return $error;
456 412 }
457 413
458 414 // Add the copied table.
459 415 $new_table_id = $this->add( $table, 'copy' );
460 416 if ( is_wp_error( $new_table_id ) ) {
461 - // Add an error code to the existing WP_Error.
462 - $new_table_id->add( 'table_copy_table_add', '', $table_id );
463 - return $new_table_id;
417 + $error = new WP_Error( 'table_copy_table_add', '', $table_id );
418 + $error->merge_from( $new_table_id );
419 + return $error;
464 420 }
465 421
466 422 /**
467 423 * Fires after an existing table has been copied.
@@ -483,14 +439,16 @@
483 439 *
484 440 * @param string $table_id ID of the table to be deleted.
485 441 * @return bool|WP_Error WP_Error on error, true on success.
486 442 */
487 - public function delete( $table_id ) {
443 + public function delete( string $table_id ) /* : true|WP_Error */ {
488 444 if ( ! $this->table_exists( $table_id ) ) {
489 445 return new WP_Error( 'table_delete_table_does_not_exist', '', $table_id );
490 446 }
491 447
492 448 $post_id = $this->_get_post_id( $table_id ); // No ! false check necessary, as this is covered by table_exists() check above.
449 +
450 + // @phpstan-ignore argument.type
493 451 $deleted = $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
494 452 if ( false === $deleted ) {
495 453 return new WP_Error( 'table_delete_post_could_not_be_deleted', '', $post_id );
496 454 }
@@ -519,9 +477,9 @@
519 477 * Delete all tables.
520 478 *
521 479 * @since 1.0.0
522 480 */
523 - public function delete_all() {
481 + public function delete_all(): void {
524 482 $tables = $this->tables->get();
525 483 if ( empty( $tables['table_post'] ) ) {
526 484 return;
527 485 }
@@ -526,11 +484,13 @@
526 484 return;
527 485 }
528 486
529 487 foreach ( $tables['table_post'] as $table_id => $post_id ) {
530 - $table_id = (string) $table_id;
488 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
489 +
531 490 $this->model_post->delete( $post_id ); // Post Meta fields will be deleted automatically by that function.
532 491 unset( $tables['table_post'][ $table_id ] );
492 +
533 493 // Invalidate table output caches that belong to this table.
534 494 $this->invalidate_table_output_cache( $table_id );
535 495 }
536 496
@@ -553,9 +513,9 @@
553 513 *
554 514 * @param string $table_id Table ID.
555 515 * @return bool Whether the table ID exists.
556 516 */
557 - public function table_exists( $table_id ) {
517 + public function table_exists( string $table_id ): bool {
558 518 $table_post = $this->tables->get( 'table_post' );
559 519 return isset( $table_post[ $table_id ] );
560 520 }
561 521
@@ -564,11 +524,11 @@
564 524 *
565 525 * @since 1.0.0
566 526 *
567 527 * @param bool $single_value Optional. Whether to return just the number of tables from the list, or also count in the database.
568 - * @return int|array Number of Tables (if $single_value), or array of Numbers from list/DB (if ! $single_value).
528 + * @return int|array{list: int, db: int} Number of Tables (if $single_value), or array of Numbers from list/DB (if ! $single_value).
569 529 */
570 - public function count_tables( $single_value = true ) {
530 + public function count_tables( bool $single_value = true ) /* : int|array */ {
571 531 $count_list = count( $this->tables->get( 'table_post' ) );
572 532 if ( $single_value ) {
573 533 return $count_list;
574 534 }
@@ -587,9 +547,9 @@
587 547 * @since 1.8.0 Renamed from _invalidate_table_output_cache to invalidate_table_output_cache and made public.
588 548 *
589 549 * @param string $table_id Table ID.
590 550 */
591 - public function invalidate_table_output_cache( $table_id ) {
551 + public function invalidate_table_output_cache( string $table_id ): void {
592 552 $caches_list_transient_name = 'tablepress_c_' . md5( $table_id );
593 553 $caches_list = get_transient( $caches_list_transient_name );
594 554 if ( false !== $caches_list ) {
595 555 $caches_list = (array) json_decode( $caches_list, true );
@@ -600,13 +560,13 @@
600 560 delete_transient( $caches_list_transient_name );
601 561 }
602 562
603 563 /**
604 - * Flush the caches of the plugins W3 Total Cache, WP Super Cache, Cachify, and Quick Cache.
564 + * Flush the caches of common caching plugins.
605 565 *
606 566 * @since 1.0.0
607 567 */
608 - public function _flush_caching_plugins_caches() {
568 + public function _flush_caching_plugins_caches(): void {
609 569 /**
610 570 * Filters whether the caches of common caching plugins shall be flushed.
611 571 *
612 572 * @since 1.0.0
@@ -650,13 +610,13 @@
650 610 }
651 611
652 612 // Kinsta.
653 613 if ( isset( $GLOBALS['kinsta_cache'] ) && ! empty( $GLOBALS['kinsta_cache']->kinsta_cache_purge ) && is_callable( array( $GLOBALS['kinsta_cache']->kinsta_cache_purge, 'purge_complete_caches' ) ) ) {
654 - $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches();
614 + $GLOBALS['kinsta_cache']->kinsta_cache_purge->purge_complete_caches(); // @phpstan-ignore method.nonObject
655 615 }
656 616 // LiteSpeed Cache.
657 617 if ( is_callable( array( 'LiteSpeed_Cache_Tags', 'add_purge_tag' ) ) ) {
658 - LiteSpeed_Cache_Tags::add_purge_tag( '*' );
618 + LiteSpeed_Cache_Tags::add_purge_tag( '*' ); // @phpstan-ignore class.notFound
659 619 }
660 620 // Pagely.
661 621 if ( class_exists( 'PagelyCachePurge' ) ) {
662 622 $_pagely = new PagelyCachePurge();
@@ -665,23 +625,23 @@
665 625 }
666 626 }
667 627 // Pressidum.
668 628 if ( is_callable( array( 'Ninukis_Plugin', 'get_instance' ) ) ) {
669 - $_pressidum = Ninukis_Plugin::get_instance();
629 + $_pressidum = Ninukis_Plugin::get_instance(); // @phpstan-ignore class.notFound
670 630 if ( is_callable( array( $_pressidum, 'purgeAllCaches' ) ) ) {
671 - $_pressidum->purgeAllCaches();
631 + $_pressidum->purgeAllCaches(); // @phpstan-ignore method.nonObject
672 632 }
673 633 }
674 634 // Savvii.
675 635 if ( defined( '\Savvii\CacheFlusherPlugin::NAME_DOMAINFLUSH_NOW' ) ) {
676 - $_savvii = new \Savvii\CacheFlusherPlugin();
636 + $_savvii = new \Savvii\CacheFlusherPlugin(); // @phpstan-ignore class.notFound
677 637 if ( is_callable( array( $_savvii, 'domainflush' ) ) ) {
678 - $_savvii->domainflush();
638 + $_savvii->domainflush(); // @phpstan-ignore class.notFound
679 639 }
680 640 }
681 641 // WP Fastest Cache.
682 - if ( isset( $GLOBALS['wp_fastest_cache'] ) && is_callable( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) {
683 - $GLOBALS['wp_fastest_cache']->deleteCache();
642 + if ( isset( $GLOBALS['wp_fastest_cache'] ) && is_callable( array( $GLOBALS['wp_fastest_cache'], 'deleteCache' ) ) ) {
643 + $GLOBALS['wp_fastest_cache']->deleteCache( true ); // @phpstan-ignore method.nonObject
684 644 }
685 645 // WP-Optimize.
686 646 if ( function_exists( 'WP_Optimize' ) ) {
687 647 WP_Optimize()->get_page_cache()->purge();
@@ -695,9 +655,9 @@
695 655 *
696 656 * @param string $table_id Table ID.
697 657 * @return int|false Post ID on success, false on error.
698 658 */
699 - protected function _get_post_id( $table_id ) {
659 + protected function _get_post_id( string $table_id ) /* : int|false */ {
700 660 $table_post = $this->tables->get( 'table_post' );
701 661 if ( ! isset( $table_post[ $table_id ] ) ) {
702 662 return false;
703 663 }
@@ -711,9 +671,9 @@
711 671 *
712 672 * @param string $table_id Table ID.
713 673 * @param int $post_id Post ID.
714 674 */
715 - protected function _update_post_id( $table_id, $post_id ) {
675 + protected function _update_post_id( string $table_id, int $post_id ): void {
716 676 $tables = $this->tables->get();
717 677 $tables['table_post'][ $table_id ] = $post_id;
718 678 uksort( $tables['table_post'], 'strnatcasecmp' );
719 679 $this->tables->update( $tables );
@@ -725,9 +685,9 @@
725 685 * @since 1.0.0
726 686 *
727 687 * @param string $table_id Table ID.
728 688 */
729 - protected function _remove_post_id( $table_id ) {
689 + protected function _remove_post_id( string $table_id ): void {
730 690 $tables = $this->tables->get();
731 691 unset( $tables['table_post'][ $table_id ] );
732 692 $this->tables->update( $tables );
733 693 }
@@ -740,9 +700,9 @@
740 700 * @param string $old_id Old table ID.
741 701 * @param string $new_id New table ID.
742 702 * @return bool|WP_Error True on success, WP_Error on error.
743 703 */
744 - public function change_table_id( $old_id, $new_id ) {
704 + public function change_table_id( string $old_id, string $new_id ) /* : true|WP_Error */ {
745 705 $post_id = $this->_get_post_id( $old_id );
746 706 if ( false === $post_id ) {
747 707 return new WP_Error( 'table_change_id_no_post_id_for_table_id', '', $old_id );
748 708 }
@@ -778,9 +738,9 @@
778 738 * @since 1.0.0
779 739 *
780 740 * @return string Unused table ID (e.g. for a new table).
781 741 */
782 - protected function _get_new_table_id() {
742 + protected function _get_new_table_id(): string {
783 743 $tables = $this->tables->get();
784 744 // Need to check new ID candidate in a loop, because a higher ID might already be in use, if a table ID was changed manually.
785 745 do {
786 746 ++$tables['last_id'];
@@ -796,11 +756,11 @@
796 756 * Important: This scheme is versioned via TablePress::table_scheme_version; changes likely need a version update!
797 757 *
798 758 * @since 1.0.0
799 759 *
800 - * @return array Empty table.
760 + * @return array<string, mixed> Empty table.
801 761 */
802 - public function get_table_template() {
762 + public function get_table_template(): array {
803 763 // Attention: Array keys have to be lowercase, to make it possible to match them with Shortcode attributes!
804 764 $table = array(
805 765 'id' => false,
806 766 'name' => '',
@@ -809,10 +769,10 @@
809 769 'last_modified' => wp_date( 'Y-m-d H:i:s' ),
810 770 'author' => get_current_user_id(),
811 771 'options' => array(
812 772 'last_editor' => get_current_user_id(),
813 - 'table_head' => true,
814 - 'table_foot' => false,
773 + 'table_head' => 1,
774 + 'table_foot' => 0,
815 775 'alternating_row_colors' => true,
816 776 'row_hover' => true,
817 777 'print_name' => false,
818 778 'print_name_position' => 'above',
@@ -839,9 +799,9 @@
839 799 * Filters the default template/structure of an empty table.
840 800 *
841 801 * @since 1.0.0
842 802 *
843 - * @param array $table Default template/structure of an empty table.
803 + * @param array<string, mixed> $table Default template/structure of an empty table.
844 804 */
845 805 return apply_filters( 'tablepress_table_template', $table );
846 806 }
847 807
@@ -851,14 +811,14 @@
851 811 * Performs consistency checks on data and visibility settings.
852 812 *
853 813 * @since 1.0.0
854 814 *
855 - * @param array $table Table to merge into.
856 - * @param array $new_table Table to merge.
857 - * @param bool $table_size_check Optional. Whether to check the number of rows and columns (e.g. not necessary for added or copied tables).
858 - * @return array|WP_Error Merged table on success, WP_Error on error.
815 + * @param array<string, mixed> $table Table to merge into.
816 + * @param array<string, mixed> $new_table Table to merge.
817 + * @param bool $table_size_check Optional. Whether to check the number of rows and columns (e.g. not necessary for added or copied tables).
818 + * @return array<string, mixed>|WP_Error Merged table on success, WP_Error on error.
859 819 */
860 - public function prepare_table( array $table, array $new_table, $table_size_check = true ) {
820 + public function prepare_table( array $table, array $new_table, bool $table_size_check = true ) /* : array|WP_Error */ {
861 821 // Table ID must be the same (if there was an ID already).
862 822 if ( false !== $table['id'] && $table['id'] !== $new_table['id'] ) {
863 823 return new WP_Error( 'table_prepare_no_id_match', '', $new_table['id'] );
864 824 }
@@ -899,9 +859,9 @@
899 859
900 860 // All checks were successful, replace original values with new ones.
901 861
902 862 // $table['id'] is either false (and remains false) or already equal to $new_table['id'].
903 - $table['new_id'] = isset( $new_table['new_id'] ) ? $new_table['new_id'] : $table['id'];
863 + $table['new_id'] = $new_table['new_id'] ?? $table['id'];
904 864 $table['name'] = $new_table['name'];
905 865 $table['description'] = $new_table['description'];
906 866 $table['data'] = $new_table['data'];
907 867 // Make sure that cells are stored as strings.
@@ -906,16 +866,12 @@
906 866 $table['data'] = $new_table['data'];
907 867 // Make sure that cells are stored as strings.
908 868 array_walk_recursive(
909 869 $table['data'],
910 - static function( &$cell_content, $col_idx ) {
870 + static function ( /* string|int|float|bool|null */ &$cell_content, int $col_idx ): void {
911 871 $cell_content = (string) $cell_content;
912 - }
872 + },
913 873 );
914 - // $table['author'] = get_current_user_id(); // We don't want this, as it would override the original author.
915 - // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
916 - $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
917 - $table['options']['last_editor'] = get_current_user_id();
918 874 // Table Options.
919 875 if ( isset( $new_table['options'] ) ) { // Options are for example not set for newly added tables.
920 876 // Specials check for certain options.
921 877 if ( isset( $new_table['options']['extra_css_classes'] ) ) {
@@ -929,8 +885,17 @@
929 885 if ( $new_table['options']['datatables_paginate_entries'] < 1 ) {
930 886 $new_table['options']['datatables_paginate_entries'] = 10; // Default value.
931 887 }
932 888 }
889 +
890 + // Backward compatibility: Convert boolean or numeric string "table_head" and "table_foot" options to integer.
891 + if ( isset( $new_table['options']['table_head'] ) ) {
892 + $new_table['options']['table_head'] = absint( $new_table['options']['table_head'] );
893 + }
894 + if ( isset( $new_table['options']['table_foot'] ) ) {
895 + $new_table['options']['table_foot'] = absint( $new_table['options']['table_foot'] );
896 + }
897 +
933 898 // Merge new options.
934 899 $default_table = $this->get_table_template();
935 900 $table['options'] = array_intersect_key( $table['options'], $default_table['options'] );
936 901 $new_table['options'] = array_intersect_key( $new_table['options'], $default_table['options'] );
@@ -939,8 +904,18 @@
939 904 // Table Visibility.
940 905 $table['visibility']['rows'] = $new_table['visibility']['rows'];
941 906 $table['visibility']['columns'] = $new_table['visibility']['columns'];
942 907
908 + // $table['author'] = get_current_user_id(); // We don't want this, as it would override the original author.
909 + // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
910 + $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
911 + $table['options']['last_editor'] = get_current_user_id();
912 +
913 + // Convert CSS classes and some DataTables 1.x parameters to the DataTables 2 variants.
914 + if ( '' !== $table['options']['datatables_custom_commands'] ) {
915 + $table['options']['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table['options']['datatables_custom_commands'] );
916 + }
917 +
943 918 return $table;
944 919 }
945 920
946 921 /**
@@ -947,15 +922,15 @@
947 922 * Save the table options of a table (in a post meta field of the table's post).
948 923 *
949 924 * @since 1.0.0
950 925 *
951 - * @param int $post_id Post ID.
952 - * @param array $options Table options.
926 + * @param int $post_id Post ID.
927 + * @param array<string, mixed> $options Table options.
953 928 * @return bool True on success, false on error.
954 929 */
955 - protected function _add_table_options( $post_id, array $options ) {
930 + protected function _add_table_options( int $post_id, array $options ): bool {
956 931 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
957 - return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options );
932 + return $this->model_post->add_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
958 933 }
959 934
960 935 /**
961 936 * Update the table options of a table (in a post meta field in the table's post).
@@ -961,15 +936,15 @@
961 936 * Update the table options of a table (in a post meta field in the table's post).
962 937 *
963 938 * @since 1.0.0
964 939 *
965 - * @param int $post_id Post ID.
966 - * @param array $options Table options.
940 + * @param int $post_id Post ID.
941 + * @param array<string, mixed> $options Table options.
967 942 * @return bool True on success, false on error.
968 943 */
969 - protected function _update_table_options( $post_id, array $options ) {
944 + protected function _update_table_options( int $post_id, array $options ): bool {
970 945 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
971 - return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options );
946 + return $this->model_post->update_meta_field( $post_id, $this->table_options_field_name, $options ); // @phpstan-ignore argument.type
972 947 }
973 948
974 949 /**
975 950 * Get the table options of a table (from a post meta field of the table's post).
@@ -976,16 +951,22 @@
976 951 *
977 952 * @since 1.0.0
978 953 *
979 954 * @param int $post_id Post ID.
980 - * @return array Table options on success, empty array on error.
955 + * @return array<string, mixed> Table options on success, empty array on error.
981 956 */
982 - protected function _get_table_options( $post_id ) {
957 + protected function _get_table_options( int $post_id ): array {
983 958 $options = $this->model_post->get_meta_field( $post_id, $this->table_options_field_name );
984 959 if ( empty( $options ) ) {
985 960 return array();
986 961 }
987 - return (array) json_decode( $options, true );
962 + $options = (array) json_decode( $options, true );
963 +
964 + // Backward compatibility: Convert boolean "table_head" and "table_foot" options to integer.
965 + $options['table_head'] = absint( $options['table_head'] );
966 + $options['table_foot'] = absint( $options['table_foot'] );
967 +
968 + return $options;
988 969 }
989 970
990 971 /**
991 972 * Save the table visibility of a table (in a post meta field of the table's post).
@@ -991,15 +972,15 @@
991 972 * Save the table visibility of a table (in a post meta field of the table's post).
992 973 *
993 974 * @since 1.0.0
994 975 *
995 - * @param int $post_id Post ID.
996 - * @param array $visibility Table visibility.
976 + * @param int $post_id Post ID.
977 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
997 978 * @return bool True on success, false on error.
998 979 */
999 - protected function _add_table_visibility( $post_id, array $visibility ) {
980 + protected function _add_table_visibility( int $post_id, array $visibility ): bool {
1000 981 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1001 - return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility );
982 + return $this->model_post->add_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1002 983 }
1003 984
1004 985 /**
1005 986 * Update the table visibility of a table (in a post meta field in the table's post).
@@ -1005,15 +986,15 @@
1005 986 * Update the table visibility of a table (in a post meta field in the table's post).
1006 987 *
1007 988 * @since 1.0.0
1008 989 *
1009 - * @param int $post_id Post ID.
1010 - * @param array $visibility Table visibility.
990 + * @param int $post_id Post ID.
991 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
1011 992 * @return bool True on success, false on error.
1012 993 */
1013 - protected function _update_table_visibility( $post_id, array $visibility ) {
994 + protected function _update_table_visibility( int $post_id, array $visibility ): bool {
1014 995 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1015 - return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility );
996 + return $this->model_post->update_meta_field( $post_id, $this->table_visibility_field_name, $visibility ); // @phpstan-ignore argument.type
1016 997 }
1017 998
1018 999 /**
1019 1000 * Get the table visibility of a table (from a post meta field of the table's post).
@@ -1020,14 +1001,17 @@
1020 1001 *
1021 1002 * @since 1.0.0
1022 1003 *
1023 1004 * @param int $post_id Post ID.
1024 - * @return array Table visibility on success, empty array on error.
1005 + * @return array{rows: int[], columns: int[]} Table visibility on success, empty array on error.
1025 1006 */
1026 - protected function _get_table_visibility( $post_id ) {
1007 + protected function _get_table_visibility( int $post_id ): array {
1027 1008 $visibility = $this->model_post->get_meta_field( $post_id, $this->table_visibility_field_name );
1028 1009 if ( empty( $visibility ) ) {
1029 - return array();
1010 + return array(
1011 + 'rows' => array(),
1012 + 'columns' => array(),
1013 + );
1030 1014 }
1031 1015 return json_decode( $visibility, true );
1032 1016 }
1033 1017
@@ -1040,9 +1024,9 @@
1040 1024 * @since 2.0.0 Add optional $remove_old_options parameter.
1041 1025 *
1042 1026 * @param bool $remove_old_options Optional. Whether old table options should be removed from the database. Default true.
1043 1027 */
1044 - public function merge_table_options_defaults( $remove_old_options = true ) {
1028 + public function merge_table_options_defaults( bool $remove_old_options = true ): void {
1045 1029 $table_post = $this->tables->get( 'table_post' );
1046 1030 if ( empty( $table_post ) ) {
1047 1031 return;
1048 1032 }
@@ -1053,10 +1037,23 @@
1053 1037 // Get default Table with default Table Options.
1054 1038 $default_table = $this->get_table_template();
1055 1039
1056 1040 // Go through all tables (this loop now uses the WP cache).
1057 - foreach ( $table_post as $table_id => $post_id ) {
1041 + foreach ( $table_post as $post_id ) {
1058 1042 $table_options = $this->_get_table_options( $post_id );
1043 +
1044 + /**
1045 + * Filters the Table Options before they are merged with the default Table Options.
1046 + *
1047 + * @since 3.0.0
1048 + *
1049 + * @param array<string, mixed> $table_options Table Options.
1050 + * @param array<string, mixed> $default_table_options Default Table Options.
1051 + * @param bool $remove_old_options Whether old table options should be removed from the database.
1052 + * @param int $post_id Post ID of the table.
1053 + */
1054 + $table_options = apply_filters( 'tablepress_table_options_before_merge', $table_options, $default_table['options'], $remove_old_options, $post_id );
1055 +
1059 1056 if ( $remove_old_options ) {
1060 1057 // Remove old (i.e. no longer existing) Table Options.
1061 1058 $table_options = array_intersect_key( $table_options, $default_table['options'] );
1062 1059 }
@@ -1066,13 +1063,46 @@
1066 1063 }
1067 1064 }
1068 1065
1069 1066 /**
1067 + * Updates all tables' "Custom Commands" to use DataTables 2 variants instead of old DataTables 1.x CSS classes and parameters
1068 + *
1069 + * @since 3.0.0
1070 + */
1071 + public function update_custom_commands_datatables_tp30(): void {
1072 + $table_post = $this->tables->get( 'table_post' );
1073 + if ( empty( $table_post ) ) {
1074 + return;
1075 + }
1076 +
1077 + // Prime the meta cache with the table options of all tables.
1078 + update_meta_cache( 'post', array_values( $table_post ) );
1079 +
1080 + foreach ( $table_post as $table_id => $post_id ) {
1081 + $table_options = $this->_get_table_options( $post_id );
1082 +
1083 + // Nothing to do if there are no "Custom Commands".
1084 + if ( '' === $table_options['datatables_custom_commands'] ) {
1085 + continue;
1086 + }
1087 + // Run search/replace.
1088 + $old_custom_commands = $table_options['datatables_custom_commands'];
1089 + $table_options['datatables_custom_commands'] = TablePress::convert_datatables_api_data( $table_options['datatables_custom_commands'] );
1090 + // No need to save (which runs a DB query) if nothing was replaced in the "Custom Commands".
1091 + if ( $old_custom_commands === $table_options['datatables_custom_commands'] ) {
1092 + continue;
1093 + }
1094 +
1095 + $this->_update_table_options( $post_id, $table_options );
1096 + }
1097 + }
1098 +
1099 + /**
1070 1100 * Invalidate all table output caches, e.g. after a plugin update.
1071 1101 *
1072 1102 * @since 1.0.0
1073 1103 */
1074 - public function invalidate_table_output_caches() {
1104 + public function invalidate_table_output_caches(): void {
1075 1105 $table_post = $this->tables->get( 'table_post' );
1076 1106 if ( empty( $table_post ) ) {
1077 1107 return;
1078 1108 }
@@ -1077,8 +1107,9 @@
1077 1107 return;
1078 1108 }
1079 1109
1080 1110 foreach ( $table_post as $table_id => $post_id ) {
1111 + $table_id = (string) $table_id; // Ensure that the table ID is a string, as it comes from an array key where numeric strings are converted to integers.
1081 1112 $this->invalidate_table_output_cache( $table_id );
1082 1113 }
1083 1114 }
1084 1115
@@ -1089,11 +1120,11 @@
1089 1120 * @since 1.5.0
1090 1121 *
1091 1122 * @global wpdb $wpdb WordPress database abstraction object.
1092 1123 */
1093 - public function add_mime_type_to_posts() {
1124 + public function add_mime_type_to_posts(): void {
1094 1125 global $wpdb;
1095 - $wpdb->update( $wpdb->posts, array( 'post_mime_type' => 'application/json' ), array( 'post_type' => $this->model_post->get_post_type() ) );
1126 + $wpdb->update( $wpdb->posts, array( 'post_mime_type' => 'application/json' ), array( 'post_type' => $this->model_post->get_post_type() ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
1096 1127 }
1097 1128
1098 1129 /**
1099 1130 * Add a table ID for a post (table) that was imported through the WP WXR importer to the table ID to post ID map.
@@ -1099,14 +1130,14 @@
1099 1130 * Add a table ID for a post (table) that was imported through the WP WXR importer to the table ID to post ID map.
1100 1131 *
1101 1132 * @since 1.5.0
1102 1133 *
1103 - * @param int|WP_Error $post_id Post ID of the imported post on success. 0 or WP_Error on failure.
1104 - * @param int $original_post_id Original post ID that the post had on the site where it was exported from.
1105 - * @param array $postdata Post data that was imported into the database.
1106 - * @param array $post Original post data as it was exported.
1134 + * @param int|WP_Error $post_id Post ID of the imported post on success. 0 or WP_Error on failure.
1135 + * @param int $original_post_id Original post ID that the post had on the site where it was exported from.
1136 + * @param array<string, mixed> $postdata Post data that was imported into the database.
1137 + * @param array<string, mixed> $post Original post data as it was exported.
1107 1138 */
1108 - public function add_table_id_on_wp_import( $post_id, $original_post_id, array $postdata, array $post ) {
1139 + public function add_table_id_on_wp_import( /* int|WP_Error */ $post_id, int $original_post_id, array $postdata, array $post ): void {
1109 1140 // Bail if the post could not be imported or if the post is not a TablePress table.
1110 1141 if ( is_wp_error( $post_id ) || $this->model_post->get_post_type() !== $postdata['post_type'] ) {
1111 1142 return;
1112 1143 }
@@ -1125,9 +1156,9 @@
1125 1156
1126 1157 // Save the post ID for each of the table IDs.
1127 1158 $post_id_saved = false;
1128 1159 foreach ( $table_ids as $table_id ) {
1129 - $table_id = preg_replace( '/[^a-zA-Z0-9_-]/', '', $table_id );
1160 + $table_id = (string) preg_replace( '/[^a-zA-Z0-9_-]/', '', $table_id );
1130 1161 if ( '' === $table_id || $this->table_exists( $table_id ) ) {
1131 1162 continue;
1132 1163 }
1133 1164 $this->_update_post_id( $table_id, $post_id );
@@ -1145,14 +1176,14 @@
1145 1176 * Remove the `_tablepress_export_table_id` post meta field from the fields that are imported during a WP WXR import.
1146 1177 *
1147 1178 * @since 1.5.0
1148 1179 *
1149 - * @param array $postmeta Post meta fields for the post.
1150 - * @param int $post_id Post ID.
1151 - * @param array $post Post.
1152 - * @return array Modified post meta fields.
1180 + * @param array<string, mixed> $postmeta Post meta fields for the post.
1181 + * @param int $post_id Post ID.
1182 + * @param array<string, mixed> $post Post.
1183 + * @return array<string, mixed> Modified post meta fields.
1153 1184 */
1154 - public function prevent_table_id_post_meta_import_on_wp_import( array $postmeta, $post_id, array $post ) {
1185 + public function prevent_table_id_post_meta_import_on_wp_import( array $postmeta, int $post_id, array $post ): array {
1155 1186 // Bail if the post is not a TablePress table.
1156 1187 if ( $this->model_post->get_post_type() !== $post['post_type'] ) {
1157 1188 return $postmeta;
1158 1189 }
@@ -1177,10 +1208,11 @@
1177 1208 *
1178 1209 * @param bool $skip Whether to skip the current post meta. Default false.
1179 1210 * @param string $meta_key Current meta key.
1180 1211 * @param stdClass $meta Current meta object.
1212 + * @return bool Whether to skip the current post meta (unchanged $skip parameter).
1181 1213 */
1182 - public function add_table_id_to_wp_export( $skip, $meta_key, $meta ) {
1214 + public function add_table_id_to_wp_export( bool $skip, string $meta_key, stdClass $meta ): bool {
1183 1215 // Bail if the exporter doesn't process a TablePress table right now.
1184 1216 if ( $this->table_options_field_name !== $meta_key ) {
1185 1217 return $skip;
1186 1218 }
@@ -1195,17 +1227,19 @@
1195 1227 }
1196 1228
1197 1229 // Pretend that there is a `_tablepress_export_table_id` post meta field with the list of table IDs.
1198 1230 $key = '_tablepress_export_table_id';
1199 - $value = wxr_cdata( implode( ',', $table_ids ) );
1231 + $value = wxr_cdata( implode( ',', $table_ids ) ); // @phpstan-ignore function.notFound
1200 1232
1201 1233 // Hijack the filter and print extra XML code for our faked post meta field.
1234 + // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
1202 1235 echo <<<WXR
1203 - <wp:postmeta>
1204 - <wp:meta_key>{$key}</wp:meta_key>
1205 - <wp:meta_value>{$value}</wp:meta_value>
1206 - </wp:postmeta>\n
1207 -WXR;
1236 + <wp:postmeta>
1237 + <wp:meta_key>{$key}</wp:meta_key>
1238 + <wp:meta_value>{$value}</wp:meta_value>
1239 + </wp:postmeta>\n
1240 + WXR;
1241 + // phpcs:enable
1208 1242
1209 1243 return $skip;
1210 1244 }
1211 1245
@@ -1213,9 +1247,9 @@
1213 1247 * Delete the WP_Option of the model.
1214 1248 *
1215 1249 * @since 1.0.0
1216 1250 */
1217 - public function destroy() {
1251 + public function destroy(): void {
1218 1252 $this->tables->delete();
1219 1253 }
1220 1254
1221 1255 } // class TablePress_Table_Model