‪TYPO3CMS  ‪main
WebhookCompilerPass.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
21 use Symfony\Component\DependencyInjection\ContainerBuilder;
22 use Symfony\Component\DependencyInjection\Definition;
23 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
27 
39 final class ‪WebhookCompilerPass implements CompilerPassInterface
40 {
41  private ContainerBuilder ‪$container;
42  public function ‪__construct(
43  private readonly string $tagName
44  ) {}
45 
46  public function ‪process(ContainerBuilder ‪$container): void
47  {
48  if (!‪$container->hasDefinition(WebhookTypesRegistry::class)) {
49  return;
50  }
51  $this->container = ‪$container;
52  $webhookTypesRegistryDefinition = ‪$container->findDefinition(WebhookTypesRegistry::class);
53 
54  foreach (‪$container->findTaggedServiceIds($this->tagName) as $serviceName => $tags) {
55  $service = ‪$container->findDefinition($serviceName);
56  $description = '';
57  ‪$identifier = $serviceName;
58  $eventIdentifier = null;
59  // see if we should also auto-wire the message to the event, and register our main message listener
60  $listenerProviderDefinition = ‪$container->findDefinition(ListenerProvider::class);
61  // we can have multiple tags on a service, so we need to loop over them and add all webhook configuration
62  // for this message type
63  foreach ($tags as $attributes) {
64  $description = $attributes['description'] ?? '';
65  if (isset($attributes['identifier'])) {
66  ‪$identifier = $attributes['identifier'];
67  }
68  $method = $attributes['method'] ?? 'createFromEvent';
69  $eventIdentifier = $attributes['event'] ?? $this->‪getParameterType($serviceName, $service, $method);
70  if ($eventIdentifier !== null && $eventIdentifier !== false) {
71  $listenerProviderDefinition->addMethodCall(
72  'addListener',
73  [
74  $eventIdentifier,
75  MessageListener::class,
76  '__invoke',
77  ]
78  );
79  }
80  $webhookTypesRegistryDefinition->addMethodCall('addWebhookType', [‪$identifier, $description, $serviceName, $method, $eventIdentifier]);
81  }
82  }
83  }
84 
88  protected function ‪getParameterType(string $serviceName, Definition $definition, string $method = 'createFromEvent'): ?string
89  {
90  // A Reflection exception should never actually get thrown here, but linters want a try-catch just in case.
91  try {
92  if (!$definition->isAutowired()) {
93  throw new \InvalidArgumentException(
94  sprintf('Service "%s" has webhooks defined but does not declare an event to listen to and is not configured to autowire it from the listener method. Set autowire: true to enable auto-detection of the listener event.', $serviceName),
95  1679613099,
96  );
97  }
98  $params = $this->‪getReflectionMethod($definition, $method)?->getParameters();
99  // Only check if the method has really just one argument
100  if ($params === null || count($params) !== 1) {
101  return null;
102  }
103  $rType = $params[0]->getType();
104  if (!$rType instanceof \ReflectionNamedType) {
105  // Don't connect this webhook message to an event
106  return null;
107  }
108  return $rType->getName();
109  } catch (\ReflectionException $e) {
110  // Don't autowire this to an event
111  return null;
112  }
113  }
114 
120  private function ‪getReflectionMethod(Definition $definition, string $method): ?\ReflectionFunctionAbstract
121  {
122  if (!$class = $definition->getClass()) {
123  return null;
124  }
125 
126  if (!$r = $this->container->getReflectionClass($class)) {
127  return null;
128  }
129 
130  if (!$r->hasMethod($method)) {
131  return null;
132  }
133 
134  $r = $r->getMethod($method);
135  if (!$r->isPublic()) {
136  return null;
137  }
138 
139  return $r;
140  }
141 }
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass\__construct
‪__construct(private readonly string $tagName)
Definition: WebhookCompilerPass.php:42
‪TYPO3\CMS\Webhooks\WebhookTypesRegistry
Definition: WebhookTypesRegistry.php:29
‪TYPO3\CMS\Webhooks\DependencyInjection
Definition: WebhookCompilerPass.php:18
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass\getParameterType
‪getParameterType(string $serviceName, Definition $definition, string $method='createFromEvent')
Definition: WebhookCompilerPass.php:88
‪TYPO3\CMS\Webhooks\Listener\MessageListener
Definition: MessageListener.php:33
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass\$container
‪ContainerBuilder $container
Definition: WebhookCompilerPass.php:41
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass\getReflectionMethod
‪getReflectionMethod(Definition $definition, string $method)
Definition: WebhookCompilerPass.php:120
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass\process
‪process(ContainerBuilder $container)
Definition: WebhookCompilerPass.php:46
‪TYPO3\CMS\Webhooks\DependencyInjection\WebhookCompilerPass
Definition: WebhookCompilerPass.php:40
‪TYPO3\CMS\Core\EventDispatcher\ListenerProvider
Definition: ListenerProvider.php:30
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37