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 +193 -160 2.1.73.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
@@ -382,13 +338,13 @@
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 ) ) {
@@ -432,9 +388,9 @@
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 395 $error = new WP_Error( 'table_copy_table_load', '', $table_id );
440 396 $error->merge_from( $table );
@@ -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 );
@@ -604,9 +564,9 @@
604 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,11 +866,11 @@
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 874 // Table Options.
915 875 if ( isset( $new_table['options'] ) ) { // Options are for example not set for newly added tables.
916 876 // Specials check for certain options.
@@ -925,8 +885,17 @@
925 885 if ( $new_table['options']['datatables_paginate_entries'] < 1 ) {
926 886 $new_table['options']['datatables_paginate_entries'] = 10; // Default value.
927 887 }
928 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 +
929 898 // Merge new options.
930 899 $default_table = $this->get_table_template();
931 900 $table['options'] = array_intersect_key( $table['options'], $default_table['options'] );
932 901 $new_table['options'] = array_intersect_key( $new_table['options'], $default_table['options'] );
@@ -940,8 +909,13 @@
940 909 // $table['created'] = wp_date( 'Y-m-d H:i:s' ); // We don't want this, as it would override the original datetime.
941 910 $table['last_modified'] = wp_date( 'Y-m-d H:i:s' );
942 911 $table['options']['last_editor'] = get_current_user_id();
943 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 +
944 918 return $table;
945 919 }
946 920
947 921 /**
@@ -948,15 +922,15 @@
948 922 * Save the table options of a table (in a post meta field of the table's post).
949 923 *
950 924 * @since 1.0.0
951 925 *
952 - * @param int $post_id Post ID.
953 - * @param array $options Table options.
926 + * @param int $post_id Post ID.
927 + * @param array<string, mixed> $options Table options.
954 928 * @return bool True on success, false on error.
955 929 */
956 - protected function _add_table_options( $post_id, array $options ) {
930 + protected function _add_table_options( int $post_id, array $options ): bool {
957 931 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
958 - 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
959 933 }
960 934
961 935 /**
962 936 * Update the table options of a table (in a post meta field in the table's post).
@@ -962,15 +936,15 @@
962 936 * Update the table options of a table (in a post meta field in the table's post).
963 937 *
964 938 * @since 1.0.0
965 939 *
966 - * @param int $post_id Post ID.
967 - * @param array $options Table options.
940 + * @param int $post_id Post ID.
941 + * @param array<string, mixed> $options Table options.
968 942 * @return bool True on success, false on error.
969 943 */
970 - protected function _update_table_options( $post_id, array $options ) {
944 + protected function _update_table_options( int $post_id, array $options ): bool {
971 945 $options = wp_json_encode( $options, TABLEPRESS_JSON_OPTIONS );
972 - 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
973 947 }
974 948
975 949 /**
976 950 * Get the table options of a table (from a post meta field of the table's post).
@@ -977,16 +951,22 @@
977 951 *
978 952 * @since 1.0.0
979 953 *
980 954 * @param int $post_id Post ID.
981 - * @return array Table options on success, empty array on error.
955 + * @return array<string, mixed> Table options on success, empty array on error.
982 956 */
983 - protected function _get_table_options( $post_id ) {
957 + protected function _get_table_options( int $post_id ): array {
984 958 $options = $this->model_post->get_meta_field( $post_id, $this->table_options_field_name );
985 959 if ( empty( $options ) ) {
986 960 return array();
987 961 }
988 - 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;
989 969 }
990 970
991 971 /**
992 972 * Save the table visibility of a table (in a post meta field of the table's post).
@@ -992,15 +972,15 @@
992 972 * Save the table visibility of a table (in a post meta field of the table's post).
993 973 *
994 974 * @since 1.0.0
995 975 *
996 - * @param int $post_id Post ID.
997 - * @param array $visibility Table visibility.
976 + * @param int $post_id Post ID.
977 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
998 978 * @return bool True on success, false on error.
999 979 */
1000 - protected function _add_table_visibility( $post_id, array $visibility ) {
980 + protected function _add_table_visibility( int $post_id, array $visibility ): bool {
1001 981 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1002 - 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
1003 983 }
1004 984
1005 985 /**
1006 986 * Update the table visibility of a table (in a post meta field in the table's post).
@@ -1006,15 +986,15 @@
1006 986 * Update the table visibility of a table (in a post meta field in the table's post).
1007 987 *
1008 988 * @since 1.0.0
1009 989 *
1010 - * @param int $post_id Post ID.
1011 - * @param array $visibility Table visibility.
990 + * @param int $post_id Post ID.
991 + * @param array{rows: int[], columns: int[]} $visibility Table visibility.
1012 992 * @return bool True on success, false on error.
1013 993 */
1014 - protected function _update_table_visibility( $post_id, array $visibility ) {
994 + protected function _update_table_visibility( int $post_id, array $visibility ): bool {
1015 995 $visibility = wp_json_encode( $visibility, TABLEPRESS_JSON_OPTIONS );
1016 - 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
1017 997 }
1018 998
1019 999 /**
1020 1000 * Get the table visibility of a table (from a post meta field of the table's post).
@@ -1021,14 +1001,17 @@
1021 1001 *
1022 1002 * @since 1.0.0
1023 1003 *
1024 1004 * @param int $post_id Post ID.
1025 - * @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.
1026 1006 */
1027 - protected function _get_table_visibility( $post_id ) {
1007 + protected function _get_table_visibility( int $post_id ): array {
1028 1008 $visibility = $this->model_post->get_meta_field( $post_id, $this->table_visibility_field_name );
1029 1009 if ( empty( $visibility ) ) {
1030 - return array();
1010 + return array(
1011 + 'rows' => array(),
1012 + 'columns' => array(),
1013 + );
1031 1014 }
1032 1015 return json_decode( $visibility, true );
1033 1016 }
1034 1017
@@ -1041,9 +1024,9 @@
1041 1024 * @since 2.0.0 Add optional $remove_old_options parameter.
1042 1025 *
1043 1026 * @param bool $remove_old_options Optional. Whether old table options should be removed from the database. Default true.
1044 1027 */
1045 - public function merge_table_options_defaults( $remove_old_options = true ) {
1028 + public function merge_table_options_defaults( bool $remove_old_options = true ): void {
1046 1029 $table_post = $this->tables->get( 'table_post' );
1047 1030 if ( empty( $table_post ) ) {
1048 1031 return;
1049 1032 }
@@ -1054,10 +1037,23 @@
1054 1037 // Get default Table with default Table Options.
1055 1038 $default_table = $this->get_table_template();
1056 1039
1057 1040 // Go through all tables (this loop now uses the WP cache).
1058 - foreach ( $table_post as $table_id => $post_id ) {
1041 + foreach ( $table_post as $post_id ) {
1059 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 +
1060 1056 if ( $remove_old_options ) {
1061 1057 // Remove old (i.e. no longer existing) Table Options.
1062 1058 $table_options = array_intersect_key( $table_options, $default_table['options'] );
1063 1059 }
@@ -1067,13 +1063,46 @@
1067 1063 }
1068 1064 }
1069 1065
1070 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 + /**
1071 1100 * Invalidate all table output caches, e.g. after a plugin update.
1072 1101 *
1073 1102 * @since 1.0.0
1074 1103 */
1075 - public function invalidate_table_output_caches() {
1104 + public function invalidate_table_output_caches(): void {
1076 1105 $table_post = $this->tables->get( 'table_post' );
1077 1106 if ( empty( $table_post ) ) {
1078 1107 return;
1079 1108 }
@@ -1078,8 +1107,9 @@
1078 1107 return;
1079 1108 }
1080 1109
1081 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.
1082 1112 $this->invalidate_table_output_cache( $table_id );
1083 1113 }
1084 1114 }
1085 1115
@@ -1090,11 +1120,11 @@
1090 1120 * @since 1.5.0
1091 1121 *
1092 1122 * @global wpdb $wpdb WordPress database abstraction object.
1093 1123 */
1094 - public function add_mime_type_to_posts() {
1124 + public function add_mime_type_to_posts(): void {
1095 1125 global $wpdb;
1096 - $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
1097 1127 }
1098 1128
1099 1129 /**
1100 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,14 +1130,14 @@
1100 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.
1101 1131 *
1102 1132 * @since 1.5.0
1103 1133 *
1104 - * @param int|WP_Error $post_id Post ID of the imported post on success. 0 or WP_Error on failure.
1105 - * @param int $original_post_id Original post ID that the post had on the site where it was exported from.
1106 - * @param array $postdata Post data that was imported into the database.
1107 - * @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.
1108 1138 */
1109 - 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 {
1110 1140 // Bail if the post could not be imported or if the post is not a TablePress table.
1111 1141 if ( is_wp_error( $post_id ) || $this->model_post->get_post_type() !== $postdata['post_type'] ) {
1112 1142 return;
1113 1143 }
@@ -1126,9 +1156,9 @@
1126 1156
1127 1157 // Save the post ID for each of the table IDs.
1128 1158 $post_id_saved = false;
1129 1159 foreach ( $table_ids as $table_id ) {
1130 - $table_id = preg_replace( '/[^a-zA-Z0-9_-]/', '', $table_id );
1160 + $table_id = (string) preg_replace( '/[^a-zA-Z0-9_-]/', '', $table_id );
1131 1161 if ( '' === $table_id || $this->table_exists( $table_id ) ) {
1132 1162 continue;
1133 1163 }
1134 1164 $this->_update_post_id( $table_id, $post_id );
@@ -1146,14 +1176,14 @@
1146 1176 * Remove the `_tablepress_export_table_id` post meta field from the fields that are imported during a WP WXR import.
1147 1177 *
1148 1178 * @since 1.5.0
1149 1179 *
1150 - * @param array $postmeta Post meta fields for the post.
1151 - * @param int $post_id Post ID.
1152 - * @param array $post Post.
1153 - * @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.
1154 1184 */
1155 - 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 {
1156 1186 // Bail if the post is not a TablePress table.
1157 1187 if ( $this->model_post->get_post_type() !== $post['post_type'] ) {
1158 1188 return $postmeta;
1159 1189 }
@@ -1178,10 +1208,11 @@
1178 1208 *
1179 1209 * @param bool $skip Whether to skip the current post meta. Default false.
1180 1210 * @param string $meta_key Current meta key.
1181 1211 * @param stdClass $meta Current meta object.
1212 + * @return bool Whether to skip the current post meta (unchanged $skip parameter).
1182 1213 */
1183 - 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 {
1184 1215 // Bail if the exporter doesn't process a TablePress table right now.
1185 1216 if ( $this->table_options_field_name !== $meta_key ) {
1186 1217 return $skip;
1187 1218 }
@@ -1196,17 +1227,19 @@
1196 1227 }
1197 1228
1198 1229 // Pretend that there is a `_tablepress_export_table_id` post meta field with the list of table IDs.
1199 1230 $key = '_tablepress_export_table_id';
1200 - $value = wxr_cdata( implode( ',', $table_ids ) );
1231 + $value = wxr_cdata( implode( ',', $table_ids ) ); // @phpstan-ignore function.notFound
1201 1232
1202 1233 // Hijack the filter and print extra XML code for our faked post meta field.
1234 + // phpcs:disable WordPress.Security.EscapeOutput.HeredocOutputNotEscaped
1203 1235 echo <<<WXR
1204 - <wp:postmeta>
1205 - <wp:meta_key>{$key}</wp:meta_key>
1206 - <wp:meta_value>{$value}</wp:meta_value>
1207 - </wp:postmeta>\n
1208 -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
1209 1242
1210 1243 return $skip;
1211 1244 }
1212 1245
@@ -1214,9 +1247,9 @@
1214 1247 * Delete the WP_Option of the model.
1215 1248 *
1216 1249 * @since 1.0.0
1217 1250 */
1218 - public function destroy() {
1251 + public function destroy(): void {
1219 1252 $this->tables->delete();
1220 1253 }
1221 1254
1222 1255 } // class TablePress_Table_Model