Compare commits
3 Commits
claude/jso
...
claude/pep
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
817690c1b6 | ||
|
|
1880d1db1f | ||
|
|
1e5999e663 |
20
CHANGELOG.md
20
CHANGELOG.md
@@ -3,26 +3,6 @@
|
|||||||
All notable changes to Peptide Repo Core are documented here.
|
All notable changes to Peptide Repo Core are documented here.
|
||||||
Format: [Semantic Versioning](https://semver.org/).
|
Format: [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
## [0.6.2] — 2026-06-13
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Critical**: Peptide monograph pages (e.g. `/peptides/bpc-157/`) returned HTTP 500 after v0.6.0 launch. Root cause: `PR_Core_Jsonld::inject_graph_pieces()` appended plain PHP arrays (Drug, FAQ) to Yoast's `wpseo_schema_graph_pieces` filter. Yoast's `Schema_Generator::filter_graph_pieces_to_generate()` then called `get_class()` and `is_needed()` on every piece, expecting `Abstract_Schema_Piece` objects — a plain array causes `PHP Fatal: get_class(): Argument #1 ($object) must be of type object, array given`. Non-peptide pages (/, /about/, /peptides/ archive) were unaffected because the `is_singular('peptide')` guard prevented entry.
|
|
||||||
- Fix: removed `wpseo_schema_graph_pieces` hook entirely. Drug and FAQPage nodes are now injected via a new `inject_graph_nodes()` method hooked on `wpseo_schema_graph` at priority 12, which receives Yoast's fully-assembled graph as an array of plain arrays — safe for plain array appends.
|
|
||||||
- Added regression test `tests/unit/test-jsonld-peptide-piece.php` that reproduces the v0.6.1 `get_class(array)` fatal and verifies the v0.6.2 `inject_graph_nodes` path emits Drug + FAQ correctly across peptide, non-peptide, and empty-FAQ scenarios.
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- `PR_Core_Jsonld::register_hooks()`: no longer registers `wpseo_schema_graph_pieces`; registers `wpseo_schema_graph` at priority 12 for Drug/FAQ injection (in addition to priority 11 for `enrich_webpage_piece`, unchanged).
|
|
||||||
- `PR_Core_Jsonld::inject_graph_pieces()` renamed to `inject_graph_nodes()`; signature updated from `(array $pieces, $context)` to `(array $graph, $context)` to reflect that it now receives the assembled graph, not the pieces array.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [0.6.1] — 2026-06-13
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fix: Yoast `wpseo_schema_webpage_type` passes `string|array` on singulars — accept `string|array` in `retype_to_medical_webpage()` signature; return type updated to match (PHP 8.1+ union). Eliminates `TypeError` → HTTP 500 on all singular pages (monographs + plain Pages) under `strict_types=1`.
|
|
||||||
- Fix: `enrich_webpage_piece` compared `'WebPage' === $piece['@type']` but Yoast sets `@type` as an array on singular pages. Normalise `@type` to array before `in_array()` check; `lastReviewed`, `reviewedBy`, and `audience` now inject correctly.
|
|
||||||
- Add regression test `tests/unit/test-jsonld-webpage.php`: exercises `retype_to_medical_webpage()` with array input for peptide and non-peptide contexts (no TypeError), and `enrich_webpage_piece` with `@type` as array (enrichment injects). Tests fail against v0.6.0, pass after fix.
|
|
||||||
|
|
||||||
## [0.6.0] — 2026-06-13
|
## [0.6.0] — 2026-06-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -28,17 +28,10 @@ class PR_Core_Jsonld_Webpage {
|
|||||||
* Hooked on: wpseo_schema_webpage_type (Yoast SEO ≥ 19.x).
|
* Hooked on: wpseo_schema_webpage_type (Yoast SEO ≥ 19.x).
|
||||||
* Passes through unchanged on non-peptide pages.
|
* Passes through unchanged on non-peptide pages.
|
||||||
*
|
*
|
||||||
* Yoast contract: passes a plain string (e.g., 'CollectionPage') for
|
* @param string $type Existing Yoast page type (e.g., 'WebPage').
|
||||||
* non-singular special types, but an ARRAY (e.g., ['WebPage']) for every
|
* @return string 'MedicalWebPage' on peptide singles, $type otherwise.
|
||||||
* singular page — including plain Pages and monographs. Under strict_types=1
|
|
||||||
* a string-only signature throws TypeError on all singulars. This method
|
|
||||||
* therefore accepts string|array and returns the same union type so callers
|
|
||||||
* (Yoast's filter chain) receive the value in the shape they passed it.
|
|
||||||
*
|
|
||||||
* @param string|array $type Existing Yoast page type — string or string[].
|
|
||||||
* @return string|array 'MedicalWebPage' (string) on peptide singles; $type unchanged otherwise.
|
|
||||||
*/
|
*/
|
||||||
public function retype_to_medical_webpage( string|array $type ): string|array {
|
public function retype_to_medical_webpage( string $type ): string {
|
||||||
if ( is_singular( PR_Core_Peptide_CPT::POST_TYPE ) ) {
|
if ( is_singular( PR_Core_Peptide_CPT::POST_TYPE ) ) {
|
||||||
return 'MedicalWebPage';
|
return 'MedicalWebPage';
|
||||||
}
|
}
|
||||||
@@ -62,8 +55,8 @@ class PR_Core_Jsonld_Webpage {
|
|||||||
return $graph;
|
return $graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
$post_id = get_the_ID();
|
$post_id = get_the_ID();
|
||||||
$enrichments = $this->build_webpage_enrichments( (int) $post_id );
|
$enrichments = $this->build_webpage_enrichments( (int) $post_id );
|
||||||
|
|
||||||
if ( empty( $enrichments ) ) {
|
if ( empty( $enrichments ) ) {
|
||||||
return $graph;
|
return $graph;
|
||||||
@@ -74,12 +67,8 @@ class PR_Core_Jsonld_Webpage {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yoast may set @type as a plain string OR an array of strings.
|
$type = $piece['@type'] ?? '';
|
||||||
// Normalise to array so in_array() works regardless of shape.
|
if ( 'WebPage' === $type || 'MedicalWebPage' === $type ) {
|
||||||
$raw_type = $piece['@type'] ?? '';
|
|
||||||
$types = is_array( $raw_type ) ? $raw_type : [ $raw_type ];
|
|
||||||
|
|
||||||
if ( in_array( 'WebPage', $types, true ) || in_array( 'MedicalWebPage', $types, true ) ) {
|
|
||||||
foreach ( $enrichments as $key => $value ) {
|
foreach ( $enrichments as $key => $value ) {
|
||||||
$piece[ $key ] = $value;
|
$piece[ $key ] = $value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,7 @@ declare(strict_types=1);
|
|||||||
* - PR_Core_Jsonld_Drug, PR_Core_Jsonld_Webpage, PR_Core_Jsonld_Faq (builders).
|
* - PR_Core_Jsonld_Drug, PR_Core_Jsonld_Webpage, PR_Core_Jsonld_Faq (builders).
|
||||||
* - PR_Core_Peptide_Repository, PR_Core_Peptide_CPT (data).
|
* - PR_Core_Peptide_Repository, PR_Core_Peptide_CPT (data).
|
||||||
*
|
*
|
||||||
* Yoast integration contract (ratified 2026-06-13, v0.6.2):
|
* Contract (ratified 2026-06-11, jsonld-contract-v1.md):
|
||||||
* - wpseo_schema_graph_pieces delivers OBJECTS (Abstract_Schema_Piece subclasses)
|
|
||||||
* to Yoast, which calls get_class() + is_needed() on every item. Injecting plain
|
|
||||||
* arrays there causes a PHP Fatal. Drug/FAQ nodes must be injected via
|
|
||||||
* wpseo_schema_graph instead, which receives the fully-assembled graph as
|
|
||||||
* array-of-arrays and is safe for plain array appends.
|
|
||||||
* - wpseo_schema_graph_pieces is therefore left UNHOOKED by this plugin.
|
|
||||||
* - Integrated path: Yoast owns WebPage and BreadcrumbList — we never duplicate.
|
* - Integrated path: Yoast owns WebPage and BreadcrumbList — we never duplicate.
|
||||||
* - Standalone fallback: emits a complete @graph (Drug + FAQPage) only.
|
* - Standalone fallback: emits a complete @graph (Drug + FAQPage) only.
|
||||||
* - Drug @id: {permalink}#drug (stable for cross-plugin linking).
|
* - Drug @id: {permalink}#drug (stable for cross-plugin linking).
|
||||||
@@ -55,68 +49,59 @@ class PR_Core_Jsonld {
|
|||||||
/**
|
/**
|
||||||
* Register hooks for JSON-LD output.
|
* Register hooks for JSON-LD output.
|
||||||
*
|
*
|
||||||
* Registers the Yoast-integrated path via wpseo_schema_graph (safe for plain
|
* Registers both the Yoast-integrated path (active when Yoast is loaded)
|
||||||
* array appends) and the standalone wp_head fallback (active only when Yoast
|
* and the standalone wp_head fallback (active only when Yoast is absent).
|
||||||
* is absent). Does NOT hook wpseo_schema_graph_pieces — Yoast calls
|
|
||||||
* get_class() + is_needed() on every item in that filter's array and will
|
|
||||||
* fatal on a plain PHP array (see class-level docblock).
|
|
||||||
*
|
*
|
||||||
* Side effects: calls add_filter(), add_action().
|
* Side effects: calls add_filter(), add_action().
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function register_hooks(): void {
|
public function register_hooks(): void {
|
||||||
// wpseo_schema_webpage_type: retype WebPage -> MedicalWebPage on peptide singles.
|
// Integrated path: hook into Yoast's schema graph.
|
||||||
|
// wpseo_schema_graph_pieces: filter on the array of schema piece objects.
|
||||||
|
// wpseo_schema_webpage_type: filter returning the WebPage @type string.
|
||||||
|
add_filter( 'wpseo_schema_graph_pieces', [ $this, 'inject_graph_pieces' ], 11, 2 );
|
||||||
add_filter( 'wpseo_schema_webpage_type', [ $this->webpage_enricher, 'retype_to_medical_webpage' ] );
|
add_filter( 'wpseo_schema_webpage_type', [ $this->webpage_enricher, 'retype_to_medical_webpage' ] );
|
||||||
|
|
||||||
// wpseo_schema_graph: two callbacks at different priorities.
|
|
||||||
// Priority 11 -- enrich_webpage_piece: merge lastReviewed/reviewedBy/audience into the
|
|
||||||
// existing WebPage node that Yoast already emitted.
|
|
||||||
// Priority 12 -- inject_graph_nodes: append Drug + FAQPage arrays to the graph.
|
|
||||||
// Must run after enrich_webpage_piece so the WebPage node is already
|
|
||||||
// enriched before Drug/FAQ are appended.
|
|
||||||
add_filter( 'wpseo_schema_graph', [ $this->webpage_enricher, 'enrich_webpage_piece' ], 11, 2 );
|
add_filter( 'wpseo_schema_graph', [ $this->webpage_enricher, 'enrich_webpage_piece' ], 11, 2 );
|
||||||
add_filter( 'wpseo_schema_graph', [ $this, 'inject_graph_nodes' ], 12, 2 );
|
|
||||||
|
|
||||||
// Standalone fallback: emits only when Yoast is not producing output.
|
// Standalone fallback: emits only when Yoast is not producing output.
|
||||||
add_action( 'wp_head', [ $this, 'emit_standalone_fallback' ], 99 );
|
add_action( 'wp_head', [ $this, 'emit_standalone_fallback' ], 99 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append Drug and FAQPage nodes to Yoast's fully-assembled schema graph.
|
* Inject Drug and FAQPage pieces into Yoast's schema graph.
|
||||||
*
|
*
|
||||||
* Hooked on: wpseo_schema_graph (priority 12, after enrich_webpage_piece).
|
* Hooked on: wpseo_schema_graph_pieces (priority 11, after Yoast's own pieces).
|
||||||
* This filter receives Yoast's completed graph as an array of plain arrays.
|
* This filter receives Yoast's piece objects array. We append plain arrays;
|
||||||
* Appending plain Drug/FAQ arrays here is safe -- unlike wpseo_schema_graph_pieces
|
* Yoast 27.6 accepts both objects and plain arrays in this filter.
|
||||||
* which calls get_class() and is_needed() on each item expecting objects.
|
|
||||||
*
|
*
|
||||||
* @param array<int, array<string, mixed>> $graph Assembled Yoast graph.
|
* @param array<int, mixed> $pieces Existing graph pieces from Yoast.
|
||||||
* @param \Yoast\WP\SEO\Context\Meta_Tags_Context|null $context Yoast schema context.
|
* @param \WPSEO_Schema_Context $context Yoast schema context object.
|
||||||
* @return array<int, array<string, mixed>> Graph with Drug (and optionally FAQ) appended.
|
* @return array<int, mixed> Pieces array with Drug and FAQPage appended.
|
||||||
*/
|
*/
|
||||||
public function inject_graph_nodes( array $graph, $context ): array {
|
public function inject_graph_pieces( array $pieces, $context ): array {
|
||||||
if ( ! is_singular( PR_Core_Peptide_CPT::POST_TYPE ) ) {
|
if ( ! is_singular( PR_Core_Peptide_CPT::POST_TYPE ) ) {
|
||||||
return $graph;
|
return $pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
$post_id = get_the_ID();
|
$post_id = get_the_ID();
|
||||||
if ( ! $post_id ) {
|
if ( ! $post_id ) {
|
||||||
return $graph;
|
return $pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
$peptide = ( new PR_Core_Peptide_Repository() )->find_by_id( (int) $post_id );
|
$peptide = ( new PR_Core_Peptide_Repository() )->find_by_id( (int) $post_id );
|
||||||
if ( ! $peptide ) {
|
if ( ! $peptide ) {
|
||||||
return $graph;
|
return $pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
$graph[] = $this->drug_builder->build( $peptide );
|
$pieces[] = $this->drug_builder->build( $peptide );
|
||||||
|
|
||||||
$faq_node = $this->faq_builder->build( (int) $post_id, get_permalink( (int) $post_id ) );
|
$faq_piece = $this->faq_builder->build( (int) $post_id, get_permalink( (int) $post_id ) );
|
||||||
if ( null !== $faq_node ) {
|
if ( null !== $faq_piece ) {
|
||||||
$graph[] = $faq_node;
|
$pieces[] = $faq_piece;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $graph;
|
return $pieces;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -124,7 +109,7 @@ class PR_Core_Jsonld {
|
|||||||
*
|
*
|
||||||
* Guard: suppressed if Yoast is loaded (function_exists check). This is the
|
* Guard: suppressed if Yoast is loaded (function_exists check). This is the
|
||||||
* no-Yoast fallback required by the jsonld-contract. When Yoast is present,
|
* no-Yoast fallback required by the jsonld-contract. When Yoast is present,
|
||||||
* the integrated path (inject_graph_nodes via wpseo_schema_graph) handles emission.
|
* the integrated path (inject_graph_pieces) handles emission.
|
||||||
*
|
*
|
||||||
* Side effects: outputs a script tag in wp_head.
|
* Side effects: outputs a script tag in wp_head.
|
||||||
*
|
*
|
||||||
@@ -152,9 +137,9 @@ class PR_Core_Jsonld {
|
|||||||
$permalink = get_permalink( (int) $post_id );
|
$permalink = get_permalink( (int) $post_id );
|
||||||
$graph = [ $this->drug_builder->build( $peptide ) ];
|
$graph = [ $this->drug_builder->build( $peptide ) ];
|
||||||
|
|
||||||
$faq_node = $this->faq_builder->build( (int) $post_id, $permalink );
|
$faq_piece = $this->faq_builder->build( (int) $post_id, $permalink );
|
||||||
if ( null !== $faq_node ) {
|
if ( null !== $faq_piece ) {
|
||||||
$graph[] = $faq_node;
|
$graph[] = $faq_piece;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf(
|
printf(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Plugin Name: Peptide Repo Core
|
* Plugin Name: Peptide Repo Core
|
||||||
* Plugin URI: https://peptiderepo.com
|
* Plugin URI: https://peptiderepo.com
|
||||||
* Description: Canonical peptide schema — shared data layer for the peptiderepo.com ecosystem. Provides the peptide CPT, dosing rows, legal status cells, AI candidate queue, disclaimer component, and JSON-LD output.
|
* Description: Canonical peptide schema — shared data layer for the peptiderepo.com ecosystem. Provides the peptide CPT, dosing rows, legal status cells, AI candidate queue, disclaimer component, and JSON-LD output.
|
||||||
* Version: 0.6.2
|
* Version: 0.6.0
|
||||||
* Author: peptiderepo
|
* Author: peptiderepo
|
||||||
* Author URI: https://peptiderepo.com
|
* Author URI: https://peptiderepo.com
|
||||||
* License: GPL-2.0-or-later
|
* License: GPL-2.0-or-later
|
||||||
@@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
|||||||
|
|
||||||
/* ── Constants ────────────────────────────────────────────────────────── */
|
/* ── Constants ────────────────────────────────────────────────────────── */
|
||||||
|
|
||||||
define( 'PR_CORE_VERSION', '0.6.2' );
|
define( 'PR_CORE_VERSION', '0.6.0' );
|
||||||
define( 'PR_CORE_PLUGIN_FILE', __FILE__ );
|
define( 'PR_CORE_PLUGIN_FILE', __FILE__ );
|
||||||
define( 'PR_CORE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
define( 'PR_CORE_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||||
define( 'PR_CORE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
define( 'PR_CORE_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||||
|
|||||||
@@ -1,366 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Regression tests for v0.6.2: inject_graph_pieces (plain array) fatal on Yoast.
|
|
||||||
*
|
|
||||||
* Root cause: v0.6.0 and v0.6.1 hooked wpseo_schema_graph_pieces and appended
|
|
||||||
* plain PHP arrays (Drug, FAQ) to Yoast's pieces array. Yoast's
|
|
||||||
* Schema_Generator::filter_graph_pieces_to_generate() then called get_class()
|
|
||||||
* and is_needed() on every piece expecting objects extending Abstract_Schema_Piece.
|
|
||||||
* Passing a plain array caused:
|
|
||||||
*
|
|
||||||
* PHP Fatal: get_class(): Argument #1 ($object) must be of type object, array given
|
|
||||||
*
|
|
||||||
* This crashed every peptide monograph page (HTTP 500) while /about/ and
|
|
||||||
* /peptides/ archive returned 200 (they don't enter the is_singular('peptide')
|
|
||||||
* branch).
|
|
||||||
*
|
|
||||||
* Fix: move Drug + FAQ injection to wpseo_schema_graph (priority 12) which
|
|
||||||
* receives the fully-assembled graph as an array of plain arrays — safe for
|
|
||||||
* plain array appends. wpseo_schema_graph_pieces is no longer hooked.
|
|
||||||
*
|
|
||||||
* These tests FAIL against v0.6.1 inject_graph_pieces and PASS after the
|
|
||||||
* v0.6.2 inject_graph_nodes fix.
|
|
||||||
*
|
|
||||||
* Run: php tests/unit/test-jsonld-peptide-piece.php
|
|
||||||
* Exit 0 = all pass, 1 = any failure.
|
|
||||||
*
|
|
||||||
* @package PeptideRepoCore
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
// ── Additional stubs ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
if ( ! defined( 'PR_CORE_TARGET_SCHEMA_VERSION' ) ) {
|
|
||||||
define( 'PR_CORE_TARGET_SCHEMA_VERSION', 4 );
|
|
||||||
}
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_postmeta'] = [];
|
|
||||||
$GLOBALS['pr_test_is_singular'] = false;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = '';
|
|
||||||
$GLOBALS['pr_test_the_id'] = 0;
|
|
||||||
|
|
||||||
// Stub the peptide repository — returned when find_by_id is called.
|
|
||||||
$GLOBALS['pr_test_peptide_dto'] = null;
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_post_meta' ) ) {
|
|
||||||
function get_post_meta( $post_id, string $key, bool $single = false ) {
|
|
||||||
if ( $single ) {
|
|
||||||
return $GLOBALS['pr_test_postmeta'][ (int) $post_id ][ $key ] ?? '';
|
|
||||||
}
|
|
||||||
return array_values( $GLOBALS['pr_test_postmeta'][ (int) $post_id ] ?? [] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_permalink' ) ) {
|
|
||||||
function get_permalink( $post_id = null ): string {
|
|
||||||
return 'https://peptiderepo.com/peptides/bpc-157/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_the_ID' ) ) {
|
|
||||||
function get_the_ID(): int {
|
|
||||||
return $GLOBALS['pr_test_the_id'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'is_singular' ) ) {
|
|
||||||
function is_singular( $type = '' ): bool {
|
|
||||||
if ( '' === $type ) {
|
|
||||||
return $GLOBALS['pr_test_is_singular'];
|
|
||||||
}
|
|
||||||
return $GLOBALS['pr_test_is_singular'] && ( $GLOBALS['pr_test_singular_type'] === $type );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_post_modified_time' ) ) {
|
|
||||||
function get_post_modified_time( $format, $gmt, $post_id ) {
|
|
||||||
return '2026-06-13';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_userdata' ) ) {
|
|
||||||
function get_userdata( int $id ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'get_author_posts_url' ) ) {
|
|
||||||
function get_author_posts_url( int $id ): string {
|
|
||||||
return 'https://peptiderepo.com/author/' . $id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'home_url' ) ) {
|
|
||||||
function home_url( string $path = '' ): string {
|
|
||||||
return 'https://peptiderepo.com' . $path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'apply_filters' ) ) {
|
|
||||||
function apply_filters( string $tag, $value ) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'esc_html' ) ) {
|
|
||||||
function esc_html( string $val ): string {
|
|
||||||
return htmlspecialchars( $val, ENT_QUOTES, 'UTF-8' );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'esc_html__' ) ) {
|
|
||||||
function esc_html__( string $text, string $domain = '' ): string {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'wp_strip_all_tags' ) ) {
|
|
||||||
function wp_strip_all_tags( $value ): string {
|
|
||||||
return strip_tags( (string) $value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ! function_exists( 'sanitize_textarea_field' ) ) {
|
|
||||||
function sanitize_textarea_field( $value ): string {
|
|
||||||
return is_scalar( $value ) ? trim( (string) $value ) : '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Stub PR_Core_Peptide_Repository ──────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimal repository stub — returns whatever is in $GLOBALS['pr_test_peptide_dto'].
|
|
||||||
*/
|
|
||||||
class PR_Core_Peptide_Repository {
|
|
||||||
public function find_by_id( int $id ): ?PR_Core_Peptide_DTO {
|
|
||||||
return $GLOBALS['pr_test_peptide_dto'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Load classes under test ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/cpt/class-pr-core-schema-sanitizers.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/cpt/class-pr-core-peptide-cpt.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/dto/class-pr-core-peptide-dto.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld-drug.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld-faq.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld-webpage.php';
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld.php';
|
|
||||||
|
|
||||||
// ── Helper ────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a minimal PR_Core_Peptide_DTO for testing.
|
|
||||||
*
|
|
||||||
* @param array<string, mixed> $overrides Field overrides.
|
|
||||||
* @return PR_Core_Peptide_DTO
|
|
||||||
*/
|
|
||||||
function make_peptide_piece_dto( array $overrides = [] ): PR_Core_Peptide_DTO {
|
|
||||||
return new PR_Core_Peptide_DTO( array_merge( [
|
|
||||||
'id' => 36,
|
|
||||||
'title' => 'BPC-157',
|
|
||||||
'slug' => 'bpc-157',
|
|
||||||
'content' => '',
|
|
||||||
'excerpt' => 'A synthetic pentadecapeptide.',
|
|
||||||
'status' => 'publish',
|
|
||||||
'display_name' => 'BPC-157',
|
|
||||||
'aliases' => [],
|
|
||||||
'molecular_formula' => '',
|
|
||||||
'molecular_weight' => 0.0,
|
|
||||||
'cas_number' => '',
|
|
||||||
'drugbank_id' => '',
|
|
||||||
'chembl_id' => '',
|
|
||||||
'evidence_strength' => 'preclinical',
|
|
||||||
'editorial_review_status' => 'published',
|
|
||||||
'last_editorial_review_at' => '',
|
|
||||||
'medical_editor_id' => 0,
|
|
||||||
'categories' => [],
|
|
||||||
'families' => [],
|
|
||||||
], $overrides ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "== JSON-LD peptide-piece regression tests (v0.6.2) ==\n\n";
|
|
||||||
|
|
||||||
// ── Setup ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_the_id'] = 36;
|
|
||||||
$GLOBALS['pr_test_is_singular'] = true;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = PR_Core_Peptide_CPT::POST_TYPE;
|
|
||||||
$GLOBALS['pr_test_postmeta'][36] = [
|
|
||||||
'_pr_molecular_formula' => 'C62H98N16O22',
|
|
||||||
'_pr_molecular_weight' => '1419.5',
|
|
||||||
'_pr_aliases' => '["Body Protection Compound"]',
|
|
||||||
];
|
|
||||||
$GLOBALS['pr_test_peptide_dto'] = make_peptide_piece_dto();
|
|
||||||
|
|
||||||
$jsonld = new PR_Core_Jsonld();
|
|
||||||
|
|
||||||
// ── Regression: inject_graph_pieces fatal reproduced against old contract ─
|
|
||||||
|
|
||||||
echo "Regression: inject_graph_pieces (OLD hook) with plain array fatal:\n";
|
|
||||||
|
|
||||||
// Simulate what Yoast's filter_graph_pieces_to_generate does:
|
|
||||||
// it calls get_class($piece) on every item. A plain array causes a fatal.
|
|
||||||
$drug_builder = new PR_Core_Jsonld_Drug();
|
|
||||||
$drug_array = $drug_builder->build( $GLOBALS['pr_test_peptide_dto'] );
|
|
||||||
|
|
||||||
$caught_fatal = false;
|
|
||||||
try {
|
|
||||||
// This is the exact operation Yoast does in filter_graph_pieces_to_generate().
|
|
||||||
// Against the old v0.6.1 code (inject via wpseo_schema_graph_pieces), this would
|
|
||||||
// produce: "get_class(): Argument #1 ($object) must be of type object, array given"
|
|
||||||
$class_result = get_class( $drug_array );
|
|
||||||
} catch ( TypeError $e ) {
|
|
||||||
$caught_fatal = true;
|
|
||||||
} catch ( Error $e ) {
|
|
||||||
$caught_fatal = true;
|
|
||||||
}
|
|
||||||
pr_assert( $caught_fatal, 'get_class(array) throws TypeError/Error — confirms the v0.6.1 fatal' );
|
|
||||||
|
|
||||||
// ── Fix: inject_graph_nodes (NEW hook) works without fatal ────────────────
|
|
||||||
|
|
||||||
echo "\nFix: inject_graph_nodes via wpseo_schema_graph (plain array graph):\n";
|
|
||||||
|
|
||||||
// The new inject_graph_nodes method receives an already-built graph (array of arrays).
|
|
||||||
// We simulate the wpseo_schema_graph filter call: $graph = array of plain arrays.
|
|
||||||
$existing_graph = [
|
|
||||||
[
|
|
||||||
'@type' => 'MedicalWebPage',
|
|
||||||
'@id' => 'https://peptiderepo.com/peptides/bpc-157/#webpage',
|
|
||||||
'name' => 'BPC-157',
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'@type' => 'BreadcrumbList',
|
|
||||||
'@id' => 'https://peptiderepo.com/peptides/bpc-157/#breadcrumb',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
// This must NOT throw and must append Drug node.
|
|
||||||
$threw = false;
|
|
||||||
try {
|
|
||||||
$result_graph = $jsonld->inject_graph_nodes( $existing_graph, null );
|
|
||||||
} catch ( Throwable $e ) {
|
|
||||||
$threw = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_assert( ! $threw, 'inject_graph_nodes does NOT throw (no get_class/is_needed calls)' );
|
|
||||||
pr_assert( count( $result_graph ) > count( $existing_graph ), 'inject_graph_nodes appended at least one node' );
|
|
||||||
|
|
||||||
// Drug node must be present.
|
|
||||||
$drug_nodes = array_filter(
|
|
||||||
$result_graph,
|
|
||||||
static function ( $node ) {
|
|
||||||
$types = is_array( $node['@type'] ?? null ) ? $node['@type'] : [ $node['@type'] ?? '' ];
|
|
||||||
return in_array( 'Drug', $types, true );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
pr_assert( count( $drug_nodes ) === 1, 'exactly one Drug node in graph after inject' );
|
|
||||||
|
|
||||||
$drug_node = array_values( $drug_nodes )[0];
|
|
||||||
pr_assert_equals(
|
|
||||||
'https://peptiderepo.com/peptides/bpc-157/#drug',
|
|
||||||
$drug_node['@id'],
|
|
||||||
'Drug @id = {permalink}#drug'
|
|
||||||
);
|
|
||||||
pr_assert( isset( $drug_node['molecularFormula'] ), 'Drug has molecularFormula' );
|
|
||||||
pr_assert( isset( $drug_node['molecularWeight'] ), 'Drug has molecularWeight (QuantitativeValue)' );
|
|
||||||
pr_assert_equals( 'QuantitativeValue', $drug_node['molecularWeight']['@type'], 'molecularWeight @type is QuantitativeValue' );
|
|
||||||
pr_assert_equals( 1419.5, $drug_node['molecularWeight']['value'], 'molecularWeight value correct' );
|
|
||||||
pr_assert_equals( 'g/mol', $drug_node['molecularWeight']['unitText'], 'molecularWeight unitText = g/mol' );
|
|
||||||
pr_assert( isset( $drug_node['alternateName'] ), 'Drug has alternateName' );
|
|
||||||
|
|
||||||
// Original WebPage + BreadcrumbList nodes must be preserved.
|
|
||||||
$webpage_nodes = array_filter( $result_graph, static fn( $n ) => ( $n['@type'] ?? '' ) === 'MedicalWebPage' );
|
|
||||||
pr_assert( count( $webpage_nodes ) === 1, 'MedicalWebPage node preserved (no duplicate)' );
|
|
||||||
|
|
||||||
$breadcrumb_nodes = array_filter( $result_graph, static fn( $n ) => ( $n['@type'] ?? '' ) === 'BreadcrumbList' );
|
|
||||||
pr_assert( count( $breadcrumb_nodes ) === 1, 'BreadcrumbList node preserved (no duplicate)' );
|
|
||||||
|
|
||||||
// ── FAQ: present when _pr_faq_items has items ─────────────────────────────
|
|
||||||
|
|
||||||
echo "\nFAQ injection via inject_graph_nodes:\n";
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_postmeta'][36]['_pr_faq_items'] = wp_json_encode( [
|
|
||||||
[ 'question' => 'What is BPC-157?', 'answer' => 'A synthetic pentadecapeptide.' ],
|
|
||||||
[ 'question' => 'Is it safe?', 'answer' => 'Research use only; not approved for human therapy.' ],
|
|
||||||
] );
|
|
||||||
|
|
||||||
$result_with_faq = $jsonld->inject_graph_nodes( $existing_graph, null );
|
|
||||||
|
|
||||||
$faq_nodes = array_filter(
|
|
||||||
$result_with_faq,
|
|
||||||
static fn( $n ) => ( $n['@type'] ?? '' ) === 'FAQPage'
|
|
||||||
);
|
|
||||||
pr_assert( count( $faq_nodes ) === 1, 'FAQPage node injected when _pr_faq_items has items' );
|
|
||||||
|
|
||||||
$faq_node = array_values( $faq_nodes )[0];
|
|
||||||
pr_assert_equals( 'https://peptiderepo.com/peptides/bpc-157/#faq', $faq_node['@id'], 'FAQPage @id = {permalink}#faq' );
|
|
||||||
pr_assert( isset( $faq_node['mainEntity'] ), 'FAQPage has mainEntity' );
|
|
||||||
pr_assert_equals( 2, count( $faq_node['mainEntity'] ), 'FAQPage has 2 questions' );
|
|
||||||
|
|
||||||
// ── FAQ: absent when _pr_faq_items empty ─────────────────────────────────
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_postmeta'][36]['_pr_faq_items'] = '[]';
|
|
||||||
|
|
||||||
$result_no_faq = $jsonld->inject_graph_nodes( $existing_graph, null );
|
|
||||||
$faq_nodes_empty = array_filter(
|
|
||||||
$result_no_faq,
|
|
||||||
static fn( $n ) => ( $n['@type'] ?? '' ) === 'FAQPage'
|
|
||||||
);
|
|
||||||
pr_assert( count( $faq_nodes_empty ) === 0, 'FAQPage NOT injected when _pr_faq_items is empty array' );
|
|
||||||
|
|
||||||
// ── Non-peptide page: no injection ───────────────────────────────────────
|
|
||||||
|
|
||||||
echo "\nNon-peptide page: no injection:\n";
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_is_singular'] = false;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = '';
|
|
||||||
|
|
||||||
$non_peptide_graph = [
|
|
||||||
[
|
|
||||||
'@type' => 'WebPage',
|
|
||||||
'@id' => 'https://peptiderepo.com/about/#webpage',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$result_non_peptide = $jsonld->inject_graph_nodes( $non_peptide_graph, null );
|
|
||||||
pr_assert( count( $result_non_peptide ) === 1, 'inject_graph_nodes no-ops on non-peptide pages (graph unchanged)' );
|
|
||||||
|
|
||||||
$drug_nodes_np = array_filter( $result_non_peptide, static fn( $n ) => ( is_array( $n['@type'] ?? null ) ? in_array( 'Drug', $n['@type'], true ) : ( $n['@type'] ?? '' ) === 'Drug' ) );
|
|
||||||
pr_assert( count( $drug_nodes_np ) === 0, 'Drug NOT injected on non-peptide page' );
|
|
||||||
|
|
||||||
// ── register_hooks: does NOT hook wpseo_schema_graph_pieces ──────────────
|
|
||||||
|
|
||||||
echo "\nregister_hooks: wpseo_schema_graph_pieces must NOT be hooked:\n";
|
|
||||||
|
|
||||||
$GLOBALS['pr_core_test_state']['added_actions'] = [];
|
|
||||||
$GLOBALS['pr_core_test_state']['added_filters'] = [];
|
|
||||||
$filters_added = [];
|
|
||||||
|
|
||||||
// Override add_filter in the bootstrap to capture calls.
|
|
||||||
// (bootstrap only stubs add_action; we check via a fresh parse of register_hooks.)
|
|
||||||
$jsonld2 = new PR_Core_Jsonld();
|
|
||||||
|
|
||||||
// Use Reflection to inspect what hooks register_hooks would call.
|
|
||||||
// Instead: inspect the class source for the string — if inject_graph_pieces is
|
|
||||||
// still registered on wpseo_schema_graph_pieces, the bug is NOT fixed.
|
|
||||||
$src = file_get_contents( PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld.php' );
|
|
||||||
pr_assert(
|
|
||||||
strpos( $src, "'wpseo_schema_graph_pieces'" ) === false,
|
|
||||||
'class-pr-core-jsonld.php does NOT add_filter wpseo_schema_graph_pieces (correct hook removed)'
|
|
||||||
);
|
|
||||||
pr_assert(
|
|
||||||
strpos( $src, 'inject_graph_nodes' ) !== false,
|
|
||||||
'class-pr-core-jsonld.php references inject_graph_nodes (new method present)'
|
|
||||||
);
|
|
||||||
pr_assert(
|
|
||||||
strpos( $src, "'wpseo_schema_graph'" ) !== false,
|
|
||||||
'class-pr-core-jsonld.php hooks wpseo_schema_graph for injection'
|
|
||||||
);
|
|
||||||
|
|
||||||
// ── Summary ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
exit( pr_test_summary() );
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Regression tests for v0.6.1: Yoast wpseo_schema_webpage_type array contract.
|
|
||||||
*
|
|
||||||
* Root cause caught on staging: v0.6.0 retype_to_medical_webpage() was
|
|
||||||
* strict-typed string, but Yoast passes ['WebPage'] (array) on every singular
|
|
||||||
* page. strict_types=1 → TypeError → HTTP 500 on all monographs + plain Pages.
|
|
||||||
* Secondary bug: enrich_webpage_piece compared 'WebPage' === $piece['@type']
|
|
||||||
* but @type is an array, so enrichment (lastReviewed/reviewedBy/audience)
|
|
||||||
* never injected on any page.
|
|
||||||
*
|
|
||||||
* These tests FAIL against v0.6.0 and PASS after the v0.6.1 fix.
|
|
||||||
*
|
|
||||||
* Run: php tests/unit/test-jsonld-webpage.php
|
|
||||||
* Exit 0 = all pass, 1 = any failure.
|
|
||||||
*
|
|
||||||
* @package PeptideRepoCore
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../bootstrap.php';
|
|
||||||
|
|
||||||
// ── Additional stubs ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
if ( ! defined( 'PR_CORE_TARGET_SCHEMA_VERSION' ) ) {
|
|
||||||
define( 'PR_CORE_TARGET_SCHEMA_VERSION', 4 );
|
|
||||||
}
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_postmeta'] = [];
|
|
||||||
$GLOBALS['pr_test_is_singular'] = false;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = '';
|
|
||||||
$GLOBALS['pr_test_the_id'] = 0;
|
|
||||||
|
|
||||||
function get_post_meta( $post_id, string $key, bool $single = false ) {
|
|
||||||
if ( $single ) {
|
|
||||||
return $GLOBALS['pr_test_postmeta'][ (int) $post_id ][ $key ] ?? '';
|
|
||||||
}
|
|
||||||
return array_values( $GLOBALS['pr_test_postmeta'][ (int) $post_id ] ?? [] );
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_permalink( $post_id = null ): string {
|
|
||||||
return 'https://peptiderepo.com/peptides/bpc-157/';
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_the_ID(): int {
|
|
||||||
return $GLOBALS['pr_test_the_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_singular( $type = '' ): bool {
|
|
||||||
if ( '' === $type ) {
|
|
||||||
return $GLOBALS['pr_test_is_singular'];
|
|
||||||
}
|
|
||||||
return $GLOBALS['pr_test_is_singular'] && ( $GLOBALS['pr_test_singular_type'] === $type );
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_post_modified_time( $format, $gmt, $post_id ) {
|
|
||||||
return '2026-04-25';
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_userdata( int $id ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_author_posts_url( int $id ): string {
|
|
||||||
return 'https://peptiderepo.com/author/' . $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function home_url( string $path = '' ): string {
|
|
||||||
return 'https://peptiderepo.com' . $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function apply_filters( string $tag, $value ) {
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load class under test.
|
|
||||||
require_once PR_CORE_PLUGIN_DIR . 'includes/frontend/class-pr-core-jsonld-webpage.php';
|
|
||||||
|
|
||||||
echo "== JSON-LD WebPage v0.6.1 regression tests ==\n\n";
|
|
||||||
|
|
||||||
$enricher = new PR_Core_Jsonld_Webpage();
|
|
||||||
|
|
||||||
// ── retype_to_medical_webpage: array input, non-peptide singular ──────────
|
|
||||||
|
|
||||||
echo "retype_to_medical_webpage — array input:\n";
|
|
||||||
|
|
||||||
// Non-peptide singular: Yoast passes ['WebPage']; must return unchanged, no TypeError.
|
|
||||||
$GLOBALS['pr_test_is_singular'] = true;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = 'page'; // plain WP Page, not peptide CPT.
|
|
||||||
|
|
||||||
$result = $enricher->retype_to_medical_webpage( [ 'WebPage' ] );
|
|
||||||
pr_assert_equals( [ 'WebPage' ], $result, 'non-peptide singular: array returned unchanged' );
|
|
||||||
|
|
||||||
// Peptide singular: Yoast passes ['WebPage']; must return 'MedicalWebPage' (string).
|
|
||||||
$GLOBALS['pr_test_is_singular'] = true;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = 'peptide';
|
|
||||||
|
|
||||||
$result = $enricher->retype_to_medical_webpage( [ 'WebPage' ] );
|
|
||||||
pr_assert_equals( 'MedicalWebPage', $result, 'peptide singular: returns MedicalWebPage (string)' );
|
|
||||||
|
|
||||||
// String input still works (non-singular special types like CollectionPage).
|
|
||||||
$GLOBALS['pr_test_is_singular'] = false;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = '';
|
|
||||||
|
|
||||||
$result = $enricher->retype_to_medical_webpage( 'CollectionPage' );
|
|
||||||
pr_assert_equals( 'CollectionPage', $result, 'non-singular string: passes through unchanged' );
|
|
||||||
|
|
||||||
// ── enrich_webpage_piece: @type as array injects enrichments ─────────────
|
|
||||||
|
|
||||||
echo "\nenrich_webpage_piece — @type as array:\n";
|
|
||||||
|
|
||||||
$GLOBALS['pr_test_is_singular'] = true;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = 'peptide';
|
|
||||||
$GLOBALS['pr_test_the_id'] = 42;
|
|
||||||
$GLOBALS['pr_test_postmeta'][42] = [
|
|
||||||
'_pr_last_reviewed' => '2026-06-01',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Graph piece with @type as an array (real Yoast shape on singulars).
|
|
||||||
$graph = [
|
|
||||||
[
|
|
||||||
'@type' => [ 'WebPage' ],
|
|
||||||
'@id' => 'https://peptiderepo.com/peptides/bpc-157/#webpage',
|
|
||||||
'name' => 'BPC-157',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$result_graph = $enricher->enrich_webpage_piece( $graph, new stdClass() );
|
|
||||||
$piece = $result_graph[0] ?? [];
|
|
||||||
|
|
||||||
pr_assert( isset( $piece['lastReviewed'] ), '@type=array: lastReviewed injected' );
|
|
||||||
pr_assert_equals( '2026-06-01', $piece['lastReviewed'], '@type=array: lastReviewed value correct' );
|
|
||||||
pr_assert( isset( $piece['reviewedBy'] ), '@type=array: reviewedBy injected' );
|
|
||||||
pr_assert( isset( $piece['audience'] ), '@type=array: audience injected' );
|
|
||||||
|
|
||||||
// Also works when @type is 'MedicalWebPage' (string — after our retype fires first).
|
|
||||||
$graph_retyed = [
|
|
||||||
[
|
|
||||||
'@type' => 'MedicalWebPage',
|
|
||||||
'@id' => 'https://peptiderepo.com/peptides/bpc-157/#webpage',
|
|
||||||
'name' => 'BPC-157',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$result_retyped = $enricher->enrich_webpage_piece( $graph_retyed, new stdClass() );
|
|
||||||
pr_assert( isset( $result_retyped[0]['lastReviewed'] ), 'MedicalWebPage string @type: enrichment injects' );
|
|
||||||
|
|
||||||
// Non-peptide page: enrichment must NOT fire even with array @type.
|
|
||||||
$GLOBALS['pr_test_is_singular'] = true;
|
|
||||||
$GLOBALS['pr_test_singular_type'] = 'page';
|
|
||||||
|
|
||||||
$graph_non_peptide = [
|
|
||||||
[
|
|
||||||
'@type' => [ 'WebPage' ],
|
|
||||||
'@id' => 'https://peptiderepo.com/about/#webpage',
|
|
||||||
],
|
|
||||||
];
|
|
||||||
|
|
||||||
$result_non_peptide = $enricher->enrich_webpage_piece( $graph_non_peptide, new stdClass() );
|
|
||||||
pr_assert( ! isset( $result_non_peptide[0]['lastReviewed'] ), 'non-peptide: lastReviewed NOT injected' );
|
|
||||||
|
|
||||||
// ── Summary ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
exit( pr_test_summary() );
|
|
||||||
Reference in New Issue
Block a user