3838use Temporal \Interceptor \WorkflowClientCallsInterceptor ;
3939use Temporal \Internal \Client \ActivityCompletionClient ;
4040use Temporal \Internal \Client \WorkflowProxy ;
41+ use Temporal \Plugin \ClientPluginContext ;
42+ use Temporal \Plugin \ClientPluginInterface ;
43+ use Temporal \Plugin \CompositePipelineProvider ;
44+ use Temporal \Plugin \ConnectionPluginInterface ;
45+ use Temporal \Plugin \PluginRegistry ;
46+ use Temporal \Plugin \ScheduleClientPluginInterface ;
47+ use Temporal \Plugin \WorkerPluginInterface ;
4148use Temporal \Internal \Client \WorkflowRun ;
4249use Temporal \Internal \Client \WorkflowStarter ;
4350use Temporal \Internal \Client \WorkflowStub ;
@@ -63,6 +70,7 @@ class WorkflowClient implements WorkflowClientInterface
6370 private DataConverterInterface $ converter ;
6471 private ?WorkflowStarter $ starter = null ;
6572 private WorkflowReader $ reader ;
73+ private PluginRegistry $ pluginRegistry ;
6674
6775 /** @var Pipeline<WorkflowClientCallsInterceptor, mixed> */
6876 private Pipeline $ interceptorPipeline ;
@@ -72,11 +80,40 @@ public function __construct(
7280 ?ClientOptions $ options = null ,
7381 ?DataConverterInterface $ converter = null ,
7482 ?PipelineProvider $ interceptorProvider = null ,
83+ ?PluginRegistry $ pluginRegistry = null ,
7584 ) {
76- $ this ->interceptorPipeline = ($ interceptorProvider ?? new SimplePipelineProvider ())
77- ->getPipeline (WorkflowClientCallsInterceptor::class);
85+ $ this ->pluginRegistry = $ pluginRegistry ?? new PluginRegistry ();
7886 $ this ->clientOptions = $ options ?? new ClientOptions ();
7987 $ this ->converter = $ converter ?? DataConverter::createDefault ();
88+
89+ // Apply connection plugins (before client-level configuration)
90+ $ connectionPlugins = $ this ->pluginRegistry ->getPlugins (ConnectionPluginInterface::class);
91+ $ serviceClient = Pipeline::prepare ($ connectionPlugins )
92+ /** @see ConnectionPluginInterface::configureServiceClient() */
93+ ->with (static fn (ServiceClientInterface $ serviceClient ) => $ serviceClient , 'configureServiceClient ' )($ serviceClient );
94+
95+ $ pluginContext = new ClientPluginContext (
96+ clientOptions: $ this ->clientOptions ,
97+ dataConverter: $ this ->converter ,
98+ );
99+ $ clientPlugins = $ this ->pluginRegistry ->getPlugins (ClientPluginInterface::class);
100+ Pipeline::prepare ($ clientPlugins )
101+ /** @see ClientPluginInterface::configureClient() */
102+ ->with (static fn (ClientPluginContext $ pluginContext ) => $ pluginContext , 'configureClient ' )($ pluginContext );
103+
104+ $ this ->clientOptions = $ pluginContext ->getClientOptions ();
105+ $ pluginConverter = $ pluginContext ->getDataConverter ();
106+ if ($ pluginConverter !== null ) {
107+ $ this ->converter = $ pluginConverter ;
108+ }
109+
110+ // Build interceptor pipeline: merge plugin-contributed interceptors with user-provided ones
111+ $ provider = new CompositePipelineProvider (
112+ $ pluginContext ->getInterceptors (),
113+ $ interceptorProvider ?? new SimplePipelineProvider (),
114+ );
115+
116+ $ this ->interceptorPipeline = $ provider ->getPipeline (WorkflowClientCallsInterceptor::class);
80117 $ this ->reader = new WorkflowReader ($ this ->createReader ());
81118
82119 // Set Temporal-Namespace metadata
@@ -88,16 +125,34 @@ public function __construct(
88125 );
89126 }
90127
91- /**
92- * @return static
93- */
94128 public static function create (
95129 ServiceClientInterface $ serviceClient ,
96130 ?ClientOptions $ options = null ,
97131 ?DataConverterInterface $ converter = null ,
98132 ?PipelineProvider $ interceptorProvider = null ,
133+ ?PluginRegistry $ pluginRegistry = null ,
99134 ): self {
100- return new self ($ serviceClient , $ options , $ converter , $ interceptorProvider );
135+ return new self ($ serviceClient , $ options , $ converter , $ interceptorProvider , $ pluginRegistry );
136+ }
137+
138+ /**
139+ * Get plugins that also implement WorkerPluginInterface for propagation to workers.
140+ *
141+ * @return list<WorkerPluginInterface>
142+ */
143+ public function getWorkerPlugins (): array
144+ {
145+ return $ this ->pluginRegistry ->getPlugins (WorkerPluginInterface::class);
146+ }
147+
148+ /**
149+ * Get plugins that also implement ScheduleClientPluginInterface for propagation to schedule clients.
150+ *
151+ * @return list<ScheduleClientPluginInterface>
152+ */
153+ public function getScheduleClientPlugins (): array
154+ {
155+ return $ this ->pluginRegistry ->getPlugins (ScheduleClientPluginInterface::class);
101156 }
102157
103158 public function getServiceClient (): ServiceClientInterface
0 commit comments