From 377bf22af33850f9eb44f6e190271882d558330c Mon Sep 17 00:00:00 2001 From: peptiderepo <273788462+peptiderepo@users.noreply.github.com> Date: Wed, 17 Jun 2026 13:16:36 +0700 Subject: [PATCH] fix: PHP 8.3 TypeError on Articles admin page (v2.5.1) (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squash-merge of PR #32 — QA APPROVED (2026-06-17 SHA 5c483fdc) Agent-Session: 2026-06-17-phase1-vps-runner-deploy-500fixes --- CHANGELOG.md | 6 +++ admin/partials/articles-list.php | 2 +- admin/partials/dashboard.php | 4 +- peptide-news-plugin.php | 4 +- tests/unit/ArticlesListTotalCastTest.php | 34 +++++++++++++++++ tests/unit/DashboardTopArticlesCastTest.php | 41 +++++++++++++++++++++ 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 tests/unit/ArticlesListTotalCastTest.php create mode 100644 tests/unit/DashboardTopArticlesCastTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index ba2b9af..e47b91d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to Peptide News Plugin are documented here. Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). Versioning: [Semantic Versioning](https://semver.org/). +## [2.5.1] - 2026-06-17 + +### Fixed +- PHP 8.3 `TypeError` on Articles admin page: `number_format()` rejects the numeric string returned by `$wpdb->get_var()`. Cast `$total` to `(int)` at point of assignment in `admin/partials/articles-list.php` (#32). +- PHP 8.3 `TypeError` on Analytics Dashboard page: `number_format()` rejects SUM aggregate strings returned by `$wpdb->get_results()`. Cast `$article->total_clicks` and `$article->total_unique` to `(int)` inline in `admin/partials/dashboard.php` (same-class fix, no PR number — follow-on to #32). + ## [2.5.0] — 2026-06-16 ### Changed diff --git a/admin/partials/articles-list.php b/admin/partials/articles-list.php index c13b872..9b3ed5c 100755 --- a/admin/partials/articles-list.php +++ b/admin/partials/articles-list.php @@ -19,7 +19,7 @@ $per_page = 25; $offset = ( $page - 1 ) * $per_page; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -$total = $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ); +$total = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $articles = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} ORDER BY published_at DESC LIMIT %d OFFSET %d", diff --git a/admin/partials/dashboard.php b/admin/partials/dashboard.php index c79f5ad..395cc2f 100755 --- a/admin/partials/dashboard.php +++ b/admin/partials/dashboard.php @@ -105,8 +105,8 @@ $total_articles = count( $top_articles ); title ); ?> source ); ?> categories ); ?> - total_clicks ) ); ?> - total_unique ) ); ?> + total_clicks ) ); ?> + total_unique ) ); ?> diff --git a/peptide-news-plugin.php b/peptide-news-plugin.php index dfe77bd..8eb2490 100755 --- a/peptide-news-plugin.php +++ b/peptide-news-plugin.php @@ -3,7 +3,7 @@ * Plugin Name: Peptide News Aggregator * Plugin URI: https://github.com/peptiderepo/peptide-news-plugin * Description: Aggregates and displays the latest peptide research news from multiple sources with click analytics and trend reporting. - * Version: 2.5.0 + * Version: 2.5.1 * Author: peptiderepo * Author URI: https://github.com/peptiderepo * License: GPL-2.0+ @@ -24,7 +24,7 @@ if ( ! defined( 'WPINC' ) ) { /** * Current plugin version — follows SemVer. */ -define( 'PEPTIDE_NEWS_VERSION', '2.5.0' ); +define( 'PEPTIDE_NEWS_VERSION', '2.5.1' ); define( 'PEPTIDE_NEWS_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); define( 'PEPTIDE_NEWS_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); define( 'PEPTIDE_NEWS_PLUGIN_BASENAME', plugin_basename( __FILE__ ) ); diff --git a/tests/unit/ArticlesListTotalCastTest.php b/tests/unit/ArticlesListTotalCastTest.php new file mode 100644 index 0000000..b671fd9 --- /dev/null +++ b/tests/unit/ArticlesListTotalCastTest.php @@ -0,0 +1,34 @@ +get_var returning a numeric string does not + * cause a TypeError in number_format() (PHP 8.3 strict typing). + */ + public function test_total_cast_prevents_type_error() { + $total_as_string = '451'; // what $wpdb->get_var() returns + $total = (int) $total_as_string; + $this->assertIsInt( $total ); + $this->assertSame( 451, $total ); + // This should not throw TypeError on PHP 8.3 + $formatted = number_format( $total ); + $this->assertSame( '451', $formatted ); + } + + /** + * Verify null return (missing table) results in 0, not a TypeError. + */ + public function test_null_total_cast_to_zero() { + $total = (int) null; + $this->assertSame( 0, $total ); + $formatted = number_format( $total ); + $this->assertSame( '0', $formatted ); + } +} diff --git a/tests/unit/DashboardTopArticlesCastTest.php b/tests/unit/DashboardTopArticlesCastTest.php new file mode 100644 index 0000000..10ff37f --- /dev/null +++ b/tests/unit/DashboardTopArticlesCastTest.php @@ -0,0 +1,41 @@ +get_results() do not + * cause a TypeError in number_format() (PHP 8.3 strict typing). + * Mirrors the fix in admin/partials/dashboard.php lines 108-109. + */ + public function test_total_clicks_cast_prevents_type_error() { + $article = (object) array( + 'total_clicks' => '1234', // numeric string from $wpdb->get_results() + 'total_unique' => '987', + ); + // Simulate the inline cast applied in dashboard.php + $formatted_clicks = number_format( (int) $article->total_clicks ); + $formatted_unique = number_format( (int) $article->total_unique ); + $this->assertSame( '1,234', $formatted_clicks ); + $this->assertSame( '987', $formatted_unique ); + } + + /** + * Verify NULL SUM() (no rows) results in 0, not a TypeError. + */ + public function test_null_sum_cast_to_zero() { + $article = (object) array( + 'total_clicks' => null, + 'total_unique' => null, + ); + $formatted_clicks = number_format( (int) $article->total_clicks ); + $formatted_unique = number_format( (int) $article->total_unique ); + $this->assertSame( '0', $formatted_clicks ); + $this->assertSame( '0', $formatted_unique ); + } +}