>> */ private const CATALOGUE = [ self::CHANNEL_SMS => [ IPPanel::ID => IPPanel::class, ], self::CHANNEL_EMAIL => [ WpMail::ID => WpMail::class, ], ]; /** * Constructed drivers, keyed by "channel:id". * * @var array */ private static array $instances = []; /** * The catalogue after filtering. * * @var array>>|null */ private static ?array $resolved = null; /** * Every driver class available, add-ons included. * * The free plugin ships one provider per channel. Iranian shops are spread * across half a dozen SMS panels and a shop on a different one is not a * customer at all, so the filter matters more here than anywhere else in the * plugin: an add-on contributes providers without this class changing. * * Values stay class *names*. `get()` constructs exactly the one that was * selected, and returning instances here would construct every provider on any * request that sends a single message. * * @return array>> */ public static function catalogue(): array { if ( null !== self::$resolved ) { return self::$resolved; } /** * Filters the notification driver catalogue. * * @param array>> $catalogue Driver classes, keyed by channel then id. */ $filtered = apply_filters( 'rahkar_wp_notification_drivers', self::CATALOGUE ); self::$resolved = []; foreach ( is_array( $filtered ) ? $filtered : self::CATALOGUE as $channel => $drivers ) { if ( ! is_string( $channel ) || ! is_array( $drivers ) ) { continue; } foreach ( $drivers as $id => $class ) { if ( is_string( $class ) && '' !== $class ) { self::$resolved[ $channel ][ (string) $id ] = $class; } } } return self::$resolved; } /** * Forget the filtered catalogue. Test-suite affordance only. * * @internal */ public static function flushCatalogue(): void { self::$resolved = null; } /** * Resolve a driver. * * @param string $channel One of the CHANNEL_* constants. * @param string $id Driver id. */ public static function get( string $channel, string $id ): ?Driver { $key = $channel . ':' . $id; if ( isset( self::$instances[ $key ] ) ) { return self::$instances[ $key ]; } $class = self::catalogue()[ $channel ][ $id ] ?? null; if ( null === $class ) { return null; } self::$instances[ $key ] = new $class(); return self::$instances[ $key ]; } /** * Every driver in a channel, constructed. * * For the settings screen only — it is the one place that legitimately needs * all of them, to render each provider's fields. * * @param string $channel One of the CHANNEL_* constants. * * @return array */ public static function all( string $channel ): array { $drivers = []; foreach ( array_keys( self::catalogue()[ $channel ] ?? [] ) as $id ) { $driver = self::get( $channel, (string) $id ); if ( null !== $driver ) { $drivers[ (string) $id ] = $driver; } } return $drivers; } /** * Driver ids and names for a settings dropdown. * * @param string $channel One of the CHANNEL_* constants. * * @return array */ public static function choices( string $channel ): array { $choices = []; foreach ( self::all( $channel ) as $id => $driver ) { $choices[ $id ] = $driver->name(); } return $choices; } }