Compare commits

...

4 Commits

Author SHA1 Message Date
peptiderepo
5c483fdc34 fix: PHP 8.3 TypeError in dashboard top-articles SUM casts + CHANGELOG (#32)
- admin/partials/dashboard.php lines 108-109: cast $article->total_clicks
  and $article->total_unique to (int) before number_format(). Both are
  SUM() aggregate columns from $wpdb->get_results() returned as strings;
  same PHP 8.3 TypeError vector as the articles-list fix.
- tests/unit/DashboardTopArticlesCastTest.php: two unit tests mirroring
  the ArticlesListTotalCastTest pattern (numeric string + null cases).
- CHANGELOG [2.5.1]: fix (# 31) typo -> (#32); add dashboard fix entry.
- $topic['total_clicks'] at line 137 is safe (already cast to int in
  Peptide_News_Analytics_Reports::get_popular_topics() at assignment).
- $total_clicks/$total_unique at lines 48/52 are safe (array_sum coerces).

Agent-Session: engineer-infra-dashboard-cast-fix-20260617
2026-06-17 06:09:49 +00:00
peptiderepo
c5872c34c9 fix: extend PHPUnit\Framework\TestCase not WP_UnitTestCase in stub suite
This repo uses stub-based PHPUnit, not WP integration tests.
WP_UnitTestCase is not loaded by bootstrap.php.

Agent-Session: fix-articles-500-20260617
2026-06-17 05:55:48 +00:00
peptiderepo
bf87c82f69 fix: PHP 8.3 TypeError in articles-list total count (int cast)
number_format() in PHP 8.3 strictly requires int|float; $wpdb->get_var()
returns string|null. Cast $total to (int) at point of assignment in
admin/partials/articles-list.php to fix HTTP 500 on Articles admin page.

Also add PHPUnit test covering the string->int cast contract.

Fixes #31. Version bump 2.5.0 -> 2.5.1.

Agent-Session: fix-articles-500-20260617
2026-06-17 05:50:50 +00:00
peptiderepo
f1c5a295a4 ci: fix peptide-e2e org ref + deploy.yml permissions (#31)
Fix stale peptiderepo/peptide-e2e refs.

Agent-Session: fix-e2e-ref-20260617
2026-06-17 11:37:57 +07:00
8 changed files with 89 additions and 8 deletions

View File

@@ -3,7 +3,7 @@
#
# Delegates all lint/test/file-size jobs to the estate reusable workflow.
# Per estate standard: workflow_call is required so deploy.yml can gate on it.
# See: peptiderepo/peptide-e2e/.github/workflows/ci.yml@main for job definitions.
# See: peptide-repo/peptide-e2e/.github/workflows/ci.yml@main for job definitions.
##
name: CI
@@ -20,7 +20,7 @@ permissions:
jobs:
ci:
uses: peptiderepo/peptide-e2e/.github/workflows/ci.yml@main
uses: peptide-repo/peptide-e2e/.github/workflows/ci.yml@main
with:
tests: stubs
has_js: true

View File

@@ -23,7 +23,7 @@ jobs:
deploy:
name: Deploy (shared pipeline)
needs: test
uses: peptiderepo/peptide-e2e/.github/workflows/deploy-app.yml@main
uses: peptide-repo/peptide-e2e/.github/workflows/deploy-app.yml@main
with:
target_path: wp-content/plugins/peptide-news-plugin
secrets: inherit

View File

@@ -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

View File

@@ -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",

View File

@@ -105,8 +105,8 @@ $total_articles = count( $top_articles );
<td><a href="<?php echo esc_url( $article->source_url ); ?>" target="_blank" rel="noopener"><?php echo esc_html( $article->title ); ?></a></td>
<td><?php echo esc_html( $article->source ); ?></td>
<td><?php echo esc_html( $article->categories ); ?></td>
<td><strong><?php echo esc_html( number_format( $article->total_clicks ) ); ?></strong></td>
<td><?php echo esc_html( number_format( $article->total_unique ) ); ?></td>
<td><strong><?php echo esc_html( number_format( (int) $article->total_clicks ) ); ?></strong></td>
<td><?php echo esc_html( number_format( (int) $article->total_unique ) ); ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>

View File

@@ -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__ ) );

View File

@@ -0,0 +1,34 @@
<?php
/**
* Test that articles list total count is cast to int.
*
* @package Peptide_News
*/
use PHPUnit\Framework\TestCase;
class ArticlesListTotalCastTest extends TestCase {
/**
* Verify that $wpdb->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 );
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Test that dashboard top-articles SUM columns are cast to int before number_format().
*
* @package Peptide_News
*/
use PHPUnit\Framework\TestCase;
class DashboardTopArticlesCastTest extends TestCase {
/**
* Verify that SUM() aggregate strings from $wpdb->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 );
}
}