pluginRootDir = rtrim( $trimmed, '/\\' ); $this->templateResolverOverride = $templateResolverOverride; $this->arrayDumperOverride = $arrayDumperOverride; $this->rendererOverride = $rendererOverride; $this->linkBuilderOverride = $linkBuilderOverride; $this->disclaimerBuilderOverride = $disclaimerBuilderOverride; $this->imageBuilderOverride = $imageBuilderOverride; $this->factoryOverride = $factoryOverride; } /** * Bind every Phase-5 rendering collaborator into the registry * and pre-load the global `affilun_template()` helper file. * * Resolution is eager so a misconfiguration (for example a * missing template directory under the supplied plugin root) * surfaces during * {@see ServiceProviderRegistry::registerAll()} rather than at * the first frontend render. * * The template-helpers.php file is required exactly once so the * global `affilun_template()` symbol is available before any caller * invokes it. The file's `function_exists` guard means a stray * second require during testing is harmless. * * @param ServiceProviderRegistry $registry Registry driving this * bootstrap pass. */ public function register( ServiceProviderRegistry $registry ): void { // The provider source file lives in the same directory as // template-helpers.php, so the relative path is unambiguous. require_once __DIR__ . '/template-helpers.php'; $resolver = $this->resolveTemplateResolver(); $dumper = $this->resolveArrayDumper(); $linkBuilder = $this->resolveLinkBuilder(); $disclaimerBld = $this->resolveDisclaimerBuilder(); $imageBuilder = $this->resolveImageBuilder(); $factory = $this->resolveFactory( $linkBuilder, $disclaimerBld, $imageBuilder ); $renderer = $this->resolveRenderer( $resolver, $dumper ); $this->resolvedTemplateResolver = $resolver; $registry->bind( self::BINDING_TEMPLATE_RESOLVER, $resolver ); $registry->bind( self::BINDING_VIEW_MODEL_ARRAY_DUMPER, $dumper ); $registry->bind( self::BINDING_LINK_ATTRIBUTE_BUILDER, $linkBuilder ); $registry->bind( self::BINDING_DISCLAIMER_BUILDER, $disclaimerBld ); $registry->bind( self::BINDING_IMAGE_PRESENTATION_BUILDER, $imageBuilder ); $registry->bind( self::BINDING_PRODUCT_CARD_VIEW_MODEL_FACTORY, $factory ); $registry->bind( self::BINDING_PRODUCT_CARD_RENDERER, $renderer ); } /** * Subscribe the bound {@see TemplateResolver} to the * `affilun_template_resolver` filter and register a diagnostic * `affilun_template_helpers_loaded` filter so external code can * verify the helper file has been loaded. * * The resolver-returning callback captures the resolver instance * cached on this provider during register(); it ignores the * previous filter value so a third-party can chain a custom * resolver via a higher-priority filter callback. * * @param HookRegistrar $hooks Shared hook registrar. */ public function boot( HookRegistrar $hooks ): void { $resolver = $this->resolvedTemplateResolver; $hooks->addFilter( 'affilun_template_resolver', static function ( $previousValue = null ) use ( $resolver ): ?TemplateResolver { unset( $previousValue ); return $resolver; }, 10, 1 ); $hooks->addFilter( 'affilun_template_helpers_loaded', static function ( $previousValue = null ): bool { unset( $previousValue ); return true; }, 10, 1 ); } /** * Resolve the §6.5 {@see TemplateResolver} the registry binding * exposes. * * Returns the constructor-injected override when present; * otherwise constructs the production default wired against * `${pluginRootDir}/templates` with the WordPress stylesheet / * template directory probes the resolver provides by default. */ private function resolveTemplateResolver(): TemplateResolver { if ( $this->templateResolverOverride !== null ) { return $this->templateResolverOverride; } return new TemplateResolver( $this->pluginRootDir . DIRECTORY_SEPARATOR . 'templates' ); } /** * Resolve the §8.2 / §8.4 {@see ViewModelArrayDumper} the * registry binding exposes. */ private function resolveArrayDumper(): ViewModelArrayDumper { if ( $this->arrayDumperOverride !== null ) { return $this->arrayDumperOverride; } return new ViewModelArrayDumper(); } /** * Resolve the §11 {@see LinkAttributeBuilder} the registry * binding exposes. */ private function resolveLinkBuilder(): LinkAttributeBuilder { if ( $this->linkBuilderOverride !== null ) { return $this->linkBuilderOverride; } return new LinkAttributeBuilder(); } /** * Resolve the §10.8 {@see DisclaimerBuilder} the registry * binding exposes. */ private function resolveDisclaimerBuilder(): DisclaimerBuilder { if ( $this->disclaimerBuilderOverride !== null ) { return $this->disclaimerBuilderOverride; } return new DisclaimerBuilder(); } /** * Resolve the §10.4 / §12 {@see ImagePresentationBuilder} the * registry binding exposes. */ private function resolveImageBuilder(): ImagePresentationBuilder { if ( $this->imageBuilderOverride !== null ) { return $this->imageBuilderOverride; } return new ImagePresentationBuilder(); } /** * Resolve the §9.1 {@see ProductCardViewModelFactory} the * registry binding exposes. * * @param LinkAttributeBuilder $linkBuilder §11 builder bound into the registry. * @param DisclaimerBuilder $disclaimerBld §10.8 builder bound into the registry. * @param ImagePresentationBuilder $imageBuilder §10.4 / §12 builder bound into the registry. */ private function resolveFactory( LinkAttributeBuilder $linkBuilder, DisclaimerBuilder $disclaimerBld, ImagePresentationBuilder $imageBuilder ): ProductCardViewModelFactory { if ( $this->factoryOverride !== null ) { return $this->factoryOverride; } return new ProductCardViewModelFactory( $linkBuilder, $disclaimerBld, $imageBuilder ); } /** * Resolve the {@see ProductCardRenderer} the registry binding * exposes. * * @param TemplateResolver $resolver Resolver bound into the registry. * @param ViewModelArrayDumper $dumper Dumper bound into the registry. */ private function resolveRenderer( TemplateResolver $resolver, ViewModelArrayDumper $dumper ): ProductCardRenderer { if ( $this->rendererOverride !== null ) { return $this->rendererOverride; } return new ProductCardRenderer( $resolver, $dumper ); } }