repositories[ $repository->get_slug() ] = $repository; } } public function find_ban_for_host( $host ) { return ( new Chain_Source( ...array_values( $this->repositories ) ) )->find_ban_for_host( $host ); } /** * Gets a list of bans. * * @param Filters $filters * @param Multi_Cursor $cursor * @param string $source * * @return Multi_Repository_Results */ public function get_bans( Filters $filters, Multi_Cursor $cursor = null, $source = '' ) { if ( $source ) { $repository = $this->repository( $source ); if ( $filters->get_limit() && $cursor && $repo_cursor = $cursor->get_cursor( $repository ) ) { $filters = $filters->with_before_cursor( $repo_cursor ); } $next_cursor = null; $results = $repository->get_bans( $filters ); if ( $filters->get_limit() ) { $next_cursor = $this->cursor(); $next_cursor->with_cursor( $repository, Cursor::from_results( $results ) ); } return new Multi_Repository_Results( $results, $next_cursor ); } if ( ! $this->repositories ) { return new Multi_Repository_Results( [] ); } if ( ! $filters->get_limit() ) { return new Multi_Repository_Results( array_merge( ...array_map( static function ( Repository $repository ) use ( $filters ) { return $repository->get_bans( $filters ); }, $this->repositories, [] ) ) ); } $count_per_repo = $filters->get_limit() / count( $this->repositories ); $populated_repos = $cursor ? array_filter( $this->repositories, static function ( Repository $repository ) use ( $cursor, $count_per_repo ) { return $cursor->get_last_count( $repository ) >= $count_per_repo; } ) : $this->repositories; $count_per_repo = floor( $filters->get_limit() / count( $populated_repos ) ); $extra = $filters->get_limit() - $count_per_repo * count( $populated_repos ); $next_cursor = $this->cursor(); $results = []; foreach ( $populated_repos as $repository ) { $limit = $count_per_repo; if ( $extra ) { $limit += $extra; $extra = 0; } $repo_filters = $filters->with_limit( $limit ); if ( $cursor && $repo_cursor = $cursor->get_cursor( $repository ) ) { $repo_filters = $repo_filters->with_before_cursor( $repo_cursor ); } $result = $repository->get_bans( $repo_filters ); $next_cursor = $next_cursor->with_cursor( $repository, Cursor::from_results( $result ) ); $results[] = $result; } $bans = $results ? array_merge( ...$results ) : []; usort( $bans, static function ( Repository_Ban $a, Repository_Ban $b ) { $a = $a->get_created_at(); $b = $b->get_created_at(); return $b->getTimestamp() - $a->getTimestamp(); } ); return new Multi_Repository_Results( $bans, $next_cursor ); } /** * Counts the list of bans. * * @param Filters $filters * @param string $source * * @return int */ public function count_bans( Filters $filters, $source = '' ) { if ( $source ) { return $this->repository( $source )->count_bans( $filters ); } $count = 0; foreach ( $this->repositories as $repository ) { $count += $repository->count_bans( $filters ); } return $count; } /** * Gets the list of supported query filters. * * A list of {@see Filters} constants declaring the available options. * * @param string $source * * @return string[] */ public function get_supported_filters( $source = '' ) { if ( $source ) { return $this->repository( $source )->get_supported_filters(); } $supported = []; foreach ( $this->repositories as $repository ) { $supported[] = $repository->get_supported_filters(); } return $supported ? array_intersect( Filters::ALL, ...$supported ) : []; } /** * Gets a Ban from the given source. * * @param string $source * @param int $id * * @return Repository_Ban|null */ public function get( $source, $id ) { return $this->repository( $source )->get( $id ); } /** * Checks if creates are supported by the given repository. * * @param string $source * * @return bool */ public function supports_create( $source ) { return $this->repository( $source ) instanceof Creatable; } /** * Gets the JSON Schema for creating a new ban. * * @param string $source * * @return array */ public function get_creation_schema( $source ) { $repository = $this->repository( $source ); if ( ! $repository instanceof Creatable ) { throw new Unsupported_Operation( sprintf( esc_html__( 'The ban source "%s" does not support creating a ban.', 'it-l10n-ithemes-security-pro' ), $source ) ); } return $repository->get_creation_schema(); } /** * Checks if updates are supported by the given repository. * * @param string $source * * @return bool */ public function supports_update( $source ) { return $this->repository( $source ) instanceof Updatable; } /** * Gets the JSON Schema for creating a new ban. * * @param string $source * * @return array */ public function get_update_schema( $source ) { $repository = $this->repository( $source ); if ( ! $repository instanceof Updatable ) { throw new Unsupported_Operation( sprintf( esc_html__( 'The ban source "%s" does not support updating a ban.', 'it-l10n-ithemes-security-pro' ), $source ) ); } return $repository->get_update_schema(); } /** * Fills a ban from request data. * * @param string $source * @param array $data * @param Repository_Ban|null $ban * * @return Repository_Ban */ public function fill( $source, array $data, Repository_Ban $ban = null ) { $repository = $this->repository( $source ); if ( ! $repository instanceof Persistable ) { throw new Unsupported_Operation( sprintf( esc_html__( 'The ban source "%s" does not support saving bans.', 'it-l10n-ithemes-security-pro' ), $source ) ); } if ( $ban && $ban->get_source() !== $source ) { throw new Invalid_Argument_Exception( sprintf( esc_html__( 'The source "%1$s" does not match the previous ban source "%2$s".', 'it-l10n-ithemes-security-pro' ), $source, $ban->get_source() ) ); } return $repository->fill( $data, $ban ); } /** * Persists a ban to the repository. * * @param Repository_Ban $ban * * @return Repository_Ban * @throws WP_Error */ public function persist( Repository_Ban $ban ) { $repository = $this->repository( $ban->get_source() ); if ( ! $repository instanceof Persistable ) { throw new Unsupported_Operation( sprintf( esc_html__( 'The ban source "%s" does not support saving bans.', 'it-l10n-ithemes-security-pro' ), $ban->get_source() ) ); } return $repository->persist( $ban ); } /** * Checks if deletes are supported by the given repository. * * @param string $source * * @return bool */ public function supports_delete( $source ) { return $this->repository( $source ) instanceof Deletable; } /** * Deletes a ban. * * @param Repository_Ban $ban */ public function delete( Repository_Ban $ban ) { $repository = $this->repository( $ban->get_source() ); if ( ! $repository instanceof Deletable ) { throw new Unsupported_Operation( sprintf( esc_html__( 'The ban source "%s" does not support deleting a ban.', 'it-l10n-ithemes-security-pro' ), $ban->get_source() ) ); } $repository->delete( $ban ); } /** * Gets the list of repository slugs this object proxies. * * @return string[] */ public function get_sources() { return array_keys( $this->repositories ); } /** * Creates a cursor instance. * * @param string $formatted * * @return Multi_Cursor */ public function cursor( $formatted = '' ) { return new Multi_Cursor( $formatted, ...array_values( $this->repositories ) ); } /** * Gets the repository with the given slug. * * @param string $slug * * @return Repository */ private function repository( $slug ) { if ( ! isset( $this->repositories[ $slug ] ) ) { throw new Unknown_Source( sprintf( esc_html__( 'Unknown ban source "%s".', 'it-l10n-ithemes-security-pro' ), $slug ) ); } return $this->repositories[ $slug ]; } }