register(MyField::class); * }); * * The action runs once, lazily, the first time the registry is used. `aop_field_types` filters the final list. * * @since 1.0.0 * Part of the Field Types API (docs/field-types-api.md), stable since 1.0.0 and semantically versioned. */ final class FieldRegistry { /** * Raised on every breaking change of the Field Types API, so an add-on can check it. */ public const API_LEVEL = 1; /** * The types that ship with the plugin. */ private const BUILT_IN = [ Types\Checkbox::class, Types\Subtitle::class, Types\Description::class, Types\HorizontalRule::class, Types\Image::class, Types\WysiwygEditor::class, Types\ColorPicker::class, Types\RadioButton::class, Types\Select::class, Types\Number::class, Types\TextField::class, Types\Textarea::class, Types\Email::class, Types\Link::class, Types\Password::class, Types\DateField::class, Types\TimeField::class, Types\DateTimeField::class, Types\Range::class, Types\ButtonGroup::class, Types\FileField::class, ]; /** * @var self|null */ private static $instance; /** * @var array */ private $types = []; /** * @var array */ private $problems = []; /** * @var bool */ private $booted = false; public static function instance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Forget everything, so the next use starts again (tests). */ public static function reset(): void { self::$instance = null; } /** * Register a field type class. Returns false (and records a problem, no fatal) when it cannot be used. */ public function register(string $class): bool { if (!class_exists($class) || !is_subclass_of($class, FieldType::class)) { /* translators: %s: a PHP class name */ $this->problems[] = sprintf(__('%s is not a field type.', 'admin-options-pages'), $class); return false; } $type = $class::type(); if (isset($this->types[$type])) { /* translators: 1: a field type, 2: a PHP class name */ $this->problems[] = sprintf(__('The field type "%1$s" is already registered; %2$s was ignored.', 'admin-options-pages'), $type, $class); return false; } $this->types[$type] = new $class(); return true; } public function get(string $type): ?FieldType { return $this->all()[$type] ?? null; } /** * @return array Keyed by type. */ public function all(): array { $this->boot(); return $this->types; } /** * What went wrong while registering (shown to administrators). * * @return array */ public function problems(): array { $this->boot(); return $this->problems; } private function boot(): void { if ($this->booted) { return; } $this->booted = true; foreach (self::BUILT_IN as $class) { $this->register($class); } /** * Register your own field types. * * @since 1.0.0 * * @param FieldRegistry $registry */ do_action('aop_register_field_types', $this); $classes = array_map('get_class', $this->types); /** * Filter the field types: remove one, or replace it with another class. * * @since 1.0.0 * * @param array> $classes Field type classes keyed by type. */ $filtered = (array) apply_filters('aop_field_types', $classes); $this->types = []; foreach ($filtered as $type => $class) { if (!is_string($class) || !$this->register($class)) { continue; } if (!isset($this->types[$type])) { // The class answers to another type than the key it was filtered under. $this->types = array_diff_key($this->types, [$class::type() => true]); /* translators: 1: a PHP class name, 2: a field type */ $this->problems[] = sprintf(__('%1$s is not the class of the field type "%2$s".', 'admin-options-pages'), $class, $type); } } } }