source = $source ?? new ApiClient(); } /** * Getting the cached definitions for a scope, without HTTP. * * @param string $scope CustomFieldSync::SCOPE_STORY or SCOPE_PUBLICATION. * * @return array> Valid definitions, possibly empty. */ public static function cached( string $scope = CustomFieldSync::SCOPE_STORY ): array { return self::normalise( Options::get( CustomFieldSync::definitions_option_key( $scope ) ) ); } /** * Checking whether a scope holds usable definitions. * * @param string $scope CustomFieldSync::SCOPE_STORY or SCOPE_PUBLICATION. * * @return bool Whether anything is cached. */ public static function is_populated( string $scope = CustomFieldSync::SCOPE_STORY ): bool { return array() !== self::cached( $scope ); } /** * Listing the scopes that have definitions of their own. * * @return string[] Scope names. */ public static function scopes(): array { return array( CustomFieldSync::SCOPE_STORY, CustomFieldSync::SCOPE_PUBLICATION ); } /** * Fetching one scope's definitions afresh and caching them. * * @param string $scope CustomFieldSync::SCOPE_STORY or SCOPE_PUBLICATION. * * @return bool Whether new definitions were stored. */ public function refresh( string $scope = CustomFieldSync::SCOPE_STORY ): bool { return RefreshOutcome::UPDATED === $this->refresh_outcome( $scope ); } /** * Fetching one scope's definitions afresh, caching them, and saying how that went. * * A scope with only unsupported field types counts as empty. * * @param string $scope CustomFieldSync::SCOPE_STORY or SCOPE_PUBLICATION. * * @return string One of the RefreshOutcome constants. */ public function refresh_outcome( string $scope = CustomFieldSync::SCOPE_STORY ): string { $response = $this->source->fetch_custom_fields( $scope ); if ( null === $response ) { return RefreshOutcome::FAILED; } $fetched = self::only_supported_types( self::normalise( $response ) ); if ( array() === $fetched ) { Options::delete( CustomFieldSync::definitions_option_key( $scope ) ); return RefreshOutcome::EMPTY; } Options::set( CustomFieldSync::definitions_option_key( $scope ), $fetched, false ); return RefreshOutcome::UPDATED; } /** * Normalising whatever is stored or fetched into a list of usable definitions. * * Accepts an array or JSON string. Requires only an ID: legacy caches may lack a type, * so the type filter is applied only when storing. * * @param mixed $definitions Stored or fetched value. * * @return array> Valid definitions, reindexed. */ public static function normalise( $definitions ): array { $decoded = Str::json_to_array( $definitions ); if ( null === $decoded ) { return array(); } $valid = array(); foreach ( $decoded as $definition ) { if ( is_array( $definition ) && isset( $definition['id'] ) && is_numeric( $definition['id'] ) ) { $valid[] = $definition; } } return $valid; } /** * Keeping only the definitions of a type the plugin can actually map. * * @param array> $definitions Normalised definitions. * * @return array> The supported ones, reindexed. */ private static function only_supported_types( array $definitions ): array { $supported = array(); foreach ( $definitions as $definition ) { if ( ! isset( $definition['type'] ) || ! is_numeric( $definition['type'] ) ) { continue; } $type = (int) $definition['type']; if ( self::TYPE_TEXT === $type || self::TYPE_SINGLE_SELECTION === $type ) { $supported[] = $definition; } } return $supported; } }