v1.0.0
This commit is contained in:
commit
1ef08bd731
27 changed files with 4854 additions and 0 deletions
209
includes/class-fontdelbosc-transit-data-manager.php
Normal file
209
includes/class-fontdelbosc-transit-data-manager.php
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<?php
|
||||
/**
|
||||
* Orchestration: caching, stale-while-error, fetch status.
|
||||
*
|
||||
* @package Fontdelbosc_Transit
|
||||
*/
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Coordinates API access with transients.
|
||||
*/
|
||||
class Fontdelbosc_Transit_Data_Manager {
|
||||
|
||||
private const INCIDENTS_TRANSIENT = 'fontdelbosc_transit_incidents';
|
||||
private const INCIDENTS_STALE = 'fontdelbosc_transit_incidents_stale';
|
||||
private const ZBE_INDEX_TRANSIENT = 'fontdelbosc_transit_zbe_index';
|
||||
private const STALE_WINDOW = 6 * HOUR_IN_SECONDS;
|
||||
private const ZBE_STALE_WINDOW = 7 * DAY_IN_SECONDS;
|
||||
|
||||
/**
|
||||
* Get incidents (cached, stale-while-error).
|
||||
*
|
||||
* @return array{incidents: array<int, array<mixed>>, source: string, publication_time: string}|null
|
||||
*/
|
||||
public static function get_incidents(): ?array {
|
||||
$cached = get_transient( self::INCIDENTS_TRANSIENT );
|
||||
|
||||
if ( is_array( $cached ) ) {
|
||||
return self::shape_incidents( $cached );
|
||||
}
|
||||
|
||||
$fresh = Fontdelbosc_Transit_Api_Sct::get_incidents();
|
||||
|
||||
if ( null === $fresh ) {
|
||||
$stale = get_transient( self::INCIDENTS_STALE );
|
||||
|
||||
if ( is_array( $stale ) ) {
|
||||
return self::shape_incidents( $stale );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$options = Fontdelbosc_Transit_Plugin::get_options();
|
||||
$ttl = max( 60, Fontdelbosc_Transit_Payload::int( $options['incident_cache_ttl'] ?? null ) ?? 300 );
|
||||
|
||||
set_transient( self::INCIDENTS_TRANSIENT, $fresh, $ttl );
|
||||
set_transient( self::INCIDENTS_STALE, $fresh, self::STALE_WINDOW );
|
||||
self::record_status(
|
||||
'incidents',
|
||||
array(
|
||||
'last_fetch' => time(),
|
||||
'source' => $fresh['source'],
|
||||
'publication_time' => $fresh['publication_time'],
|
||||
'count' => count( $fresh['incidents'] ),
|
||||
)
|
||||
);
|
||||
|
||||
return $fresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce a cached incidents payload back into its typed shape.
|
||||
*
|
||||
* @param array<mixed> $raw Cached value.
|
||||
* @return array{incidents: array<int, array<mixed>>, source: string, publication_time: string}
|
||||
*/
|
||||
private static function shape_incidents( array $raw ): array {
|
||||
$incidents = array();
|
||||
|
||||
foreach ( Fontdelbosc_Transit_Payload::arr( Fontdelbosc_Transit_Payload::get( $raw, 'incidents' ) ) as $incident ) {
|
||||
if ( is_array( $incident ) ) {
|
||||
$incidents[] = $incident;
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'incidents' => $incidents,
|
||||
'source' => Fontdelbosc_Transit_Payload::str( Fontdelbosc_Transit_Payload::get( $raw, 'source' ) ) ?? '',
|
||||
'publication_time' => Fontdelbosc_Transit_Payload::str( Fontdelbosc_Transit_Payload::get( $raw, 'publication_time' ) ) ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the zones selected in the options (cached, stale-while-error).
|
||||
*
|
||||
* @return array<int, array<mixed>>
|
||||
*/
|
||||
public static function get_zbes(): array {
|
||||
$options = Fontdelbosc_Transit_Plugin::get_options();
|
||||
$selected = Fontdelbosc_Transit_Payload::arr( $options['zbe_municipalities'] ?? Fontdelbosc_Transit_Api_Zbe::DEFAULT_CATALAN_ZONES );
|
||||
$ttl = max( HOUR_IN_SECONDS, Fontdelbosc_Transit_Payload::int( $options['zbe_cache_ttl'] ?? null ) ?? DAY_IN_SECONDS );
|
||||
$zones = array();
|
||||
|
||||
foreach ( $selected as $slug ) {
|
||||
$slug = Fontdelbosc_Transit_Api_Zbe::sanitize_slug( Fontdelbosc_Transit_Payload::str( $slug ) ?? '' );
|
||||
|
||||
if ( '' === $slug ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$key = 'fontdelbosc_transit_zbe_' . $slug;
|
||||
$cache = get_transient( $key );
|
||||
|
||||
if ( is_array( $cache ) ) {
|
||||
$cache['slug'] = $slug;
|
||||
$zones[] = $cache;
|
||||
continue;
|
||||
}
|
||||
|
||||
$zone = Fontdelbosc_Transit_Api_Zbe::get_zone( $slug );
|
||||
|
||||
if ( null === $zone ) {
|
||||
$stale = get_transient( $key . '_stale' );
|
||||
|
||||
if ( is_array( $stale ) ) {
|
||||
$stale['slug'] = $slug;
|
||||
$zones[] = $stale;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$zone['slug'] = $slug;
|
||||
set_transient( $key, $zone, $ttl );
|
||||
set_transient( $key . '_stale', $zone, self::ZBE_STALE_WINDOW );
|
||||
$zones[] = $zone;
|
||||
}
|
||||
|
||||
self::record_status(
|
||||
'zbe',
|
||||
array(
|
||||
'last_fetch' => time(),
|
||||
'zones' => count( $zones ),
|
||||
)
|
||||
);
|
||||
|
||||
return $zones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full zone index for the admin (cached 24 h).
|
||||
*
|
||||
* @return array<int, string>|null
|
||||
*/
|
||||
public static function get_zbe_index(): ?array {
|
||||
$cached = get_transient( self::ZBE_INDEX_TRANSIENT );
|
||||
|
||||
if ( is_array( $cached ) ) {
|
||||
$slugs = array();
|
||||
|
||||
foreach ( $cached as $slug ) {
|
||||
$slug = Fontdelbosc_Transit_Payload::str( $slug );
|
||||
|
||||
if ( null !== $slug ) {
|
||||
$slugs[] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
return $slugs;
|
||||
}
|
||||
|
||||
$index = Fontdelbosc_Transit_Api_Zbe::get_index();
|
||||
|
||||
if ( null !== $index ) {
|
||||
set_transient( self::ZBE_INDEX_TRANSIENT, $index, DAY_IN_SECONDS );
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush every data transient.
|
||||
*/
|
||||
public static function flush(): void {
|
||||
delete_transient( self::INCIDENTS_TRANSIENT );
|
||||
delete_transient( self::INCIDENTS_STALE );
|
||||
delete_transient( self::ZBE_INDEX_TRANSIENT );
|
||||
|
||||
$options = Fontdelbosc_Transit_Plugin::get_options();
|
||||
$slugs = array_map(
|
||||
static fn( mixed $slug ): string => Fontdelbosc_Transit_Payload::str( $slug ) ?? '',
|
||||
array_merge(
|
||||
Fontdelbosc_Transit_Payload::arr( $options['zbe_municipalities'] ?? array() ),
|
||||
self::get_zbe_index() ?? array()
|
||||
)
|
||||
);
|
||||
|
||||
foreach ( array_unique( $slugs ) as $slug ) {
|
||||
$key = 'fontdelbosc_transit_zbe_' . Fontdelbosc_Transit_Api_Zbe::sanitize_slug( Fontdelbosc_Transit_Payload::str( $slug ) ?? '' );
|
||||
delete_transient( $key );
|
||||
delete_transient( $key . '_stale' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a fetch status entry.
|
||||
*
|
||||
* @param string $stream Stream name (incidents, zbe).
|
||||
* @param array<string, mixed> $data Status payload.
|
||||
*/
|
||||
private static function record_status( string $stream, array $data ): void {
|
||||
$status = Fontdelbosc_Transit_Payload::arr( get_option( 'fontdelbosc_transit_status', array() ) );
|
||||
$status[ $stream ] = $data;
|
||||
|
||||
update_option( 'fontdelbosc_transit_status', $status, false );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue