Skip to content

Commit 212c051

Browse files
committed
Add AddOns
1 parent cf4e1e0 commit 212c051

File tree

10 files changed

+1201
-939
lines changed

10 files changed

+1201
-939
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
"require": {
77
"php": "^8.2",
88
"composer-plugin-api": "^2.0",
9-
"illuminate/collections": "^8.54 || ^9 || ^10",
109
"mammatus/app": "dev-master",
1110
"mammatus/kubernetes-attributes": "dev-main",
11+
"mammatus/kubernetes-contracts": "dev-main",
1212
"mammatus/kubernetes-events": "dev-main",
13+
"mammatus/kubernetes-keda": "dev-main",
1314
"mammatus/life-cycle-events": "^2",
1415
"mammatus/queue-attributes": "dev-main",
1516
"mammatus/queue-contracts": "dev-main",
@@ -19,11 +20,10 @@
1920
"react/async": "^4.2",
2021
"roave/better-reflection": "^6.26",
2122
"wyrihaximus/broadcast-contracts": "^1.3",
22-
"wyrihaximus/list-classes-in-directory": "^1.6",
23+
"wyrihaximus/generative-composer-plugin-tooling": "^1",
2324
"wyrihaximus/monolog-factory": "^2",
2425
"wyrihaximus/psr-3-context-logger": "^2.0",
2526
"wyrihaximus/simple-twig": "^2.1",
26-
"wyrihaximus/string-get-in": "^1.1",
2727
"wyrihaximus/ticking-promise": "^3.1"
2828
},
2929
"require-dev": {

composer.lock

Lines changed: 1042 additions & 680 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

etc/generated_templates/AbstractList_.php.twig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ abstract class AbstractList_
2020
{% for worker in workers %}
2121
/** @see \{{ worker.class }} */
2222
yield new Worker(
23-
{% if worker.split_out == true %}
23+
{% if worker.splitOut == true %}
2424
'kubernetes',
2525
{% else %}
2626
'internal',
2727
{% endif %}
2828
'{{ worker.consumer.queue }}',
2929
{{ worker.consumer.concurrency }},
3030
\{{ worker.class }}::class,
31+
\json_decode('{{ worker.consumer.addOns|json_encode()|raw }}', true), /** @phpstan-ignore-line */
3132
);
3233
{% endfor %}
3334
}

src/App.php

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,14 @@
77
use Interop\Queue\Consumer;
88
use Interop\Queue\Context;
99
use Mammatus\LifeCycleEvents\Shutdown;
10-
use Mammatus\Queue\Contracts\Worker;
1110
use Mammatus\Queue\Contracts\Worker as WorkerContract;
1211
use Mammatus\Queue\Generated\AbstractList_;
1312
use Psr\Container\ContainerInterface;
1413
use Psr\Log\LoggerInterface;
15-
use React\EventLoop\Loop;
16-
use RuntimeException;
1714
use Throwable;
1815
use WyriHaximus\Broadcast\Contracts\Listener;
19-
use WyriHaximus\PSR3\ContextLogger\ContextLogger;
2016

17+
use function assert;
2118
use function React\Async\async;
2219
use function React\Async\await;
2320
use function React\Promise\all;
@@ -57,7 +54,7 @@ public function run(string $className): int
5754
return 0;
5855
}
5956

60-
private function setupConsumer(\Mammatus\Queue\Worker $worker): int
57+
private function setupConsumer(Worker $worker): int
6158
{
6259
$consumer = $this->context->createConsumer(new Queue($worker->queue));
6360
$workerInstance = $this->container->get($worker->class);

src/Composer/Collector.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Mammatus\Queue\Composer;
6+
7+
use Mammatus\Kubernetes\Attributes\SplitOut;
8+
use Mammatus\Queue\Attributes\Consumer;
9+
use Roave\BetterReflection\Reflection\ReflectionClass;
10+
use WyriHaximus\Composer\GenerativePluginTooling\Item as ItemContract;
11+
use WyriHaximus\Composer\GenerativePluginTooling\ItemCollector;
12+
13+
use function array_key_exists;
14+
15+
final class Collector implements ItemCollector
16+
{
17+
/** @return iterable<ItemContract> */
18+
public function collect(ReflectionClass $class): iterable
19+
{
20+
$attributes = [];
21+
foreach ((new \ReflectionClass($class->getName()))->getAttributes() as $attributeReflection) {
22+
$attribute = $attributeReflection->newInstance();
23+
$attributes[$attribute::class] = $attribute;
24+
}
25+
26+
if (! array_key_exists(Consumer::class, $attributes)) {
27+
return;
28+
}
29+
30+
yield new Item(
31+
$class->getName(),
32+
$attributes[Consumer::class],
33+
array_key_exists(SplitOut::class, $attributes),
34+
);
35+
}
36+
}

src/Composer/Installer.php

Lines changed: 2 additions & 249 deletions
Original file line numberDiff line numberDiff line change
@@ -5,51 +5,12 @@
55
namespace Mammatus\Queue\Composer;
66

77
use Composer\Composer;
8-
use Composer\Config;
98
use Composer\EventDispatcher\EventSubscriberInterface;
109
use Composer\IO\IOInterface;
11-
use Composer\Package\PackageInterface;
12-
use Composer\Package\RootPackageInterface;
1310
use Composer\Plugin\PluginInterface;
1411
use Composer\Script\Event;
1512
use Composer\Script\ScriptEvents;
16-
use Illuminate\Support\Collection;
17-
use Mammatus\Kubernetes\Attributes\SplitOut;
18-
use Mammatus\Queue\Attributes\Consumer;
19-
use Mammatus\Queue\Contracts\Worker;
20-
use Roave\BetterReflection\BetterReflection;
21-
use Roave\BetterReflection\Reflection\ReflectionClass;
22-
use Roave\BetterReflection\Reflector\DefaultReflector;
23-
use Roave\BetterReflection\Reflector\Exception\IdentifierNotFound;
24-
use Roave\BetterReflection\SourceLocator\Type\Composer\Factory\MakeLocatorForComposerJsonAndInstalledJson;
25-
use Roave\BetterReflection\SourceLocator\Type\Composer\Psr\Exception\InvalidPrefixMapping;
26-
use RuntimeException;
27-
28-
use function array_key_exists;
29-
use function count;
30-
use function dirname;
31-
use function explode;
32-
use function file_exists;
33-
use function is_array;
34-
use function is_string;
35-
use function microtime;
36-
use function round;
37-
use function rtrim;
38-
use function Safe\chmod;
39-
use function Safe\file_get_contents;
40-
use function Safe\file_put_contents;
41-
use function Safe\mkdir;
42-
use function Safe\spl_autoload_register;
43-
use function sprintf;
44-
use function str_replace;
45-
use function strlen;
46-
use function strpos;
47-
use function substr;
48-
use function WyriHaximus\getIn;
49-
use function WyriHaximus\listClassesInDirectories;
50-
use function WyriHaximus\Twig\render;
51-
52-
use const DIRECTORY_SEPARATOR;
13+
use WyriHaximus\Composer\GenerativePluginTooling\GenerativePluginExecutioner;
5314

5415
final class Installer implements PluginInterface, EventSubscriberInterface
5516
{
@@ -79,214 +40,6 @@ public function uninstall(Composer $composer, IOInterface $io): void
7940
*/
8041
public static function findActions(Event $event): void
8142
{
82-
$start = microtime(true);
83-
$io = $event->getIO();
84-
$composer = $event->getComposer();
85-
$vendorDir = $composer->getConfig()->get('vendor-dir');
86-
if (! is_string($vendorDir)) {
87-
throw new RuntimeException('Vendor dir must be a string');
88-
}
89-
90-
// Composer is bugged and doesn't handle root package autoloading properly yet
91-
if (array_key_exists('psr-4', $composer->getPackage()->getAutoload())) {
92-
foreach ($composer->getPackage()->getAutoload()['psr-4'] as $ns => $p) {
93-
/** @phpstan-ignore-next-line */
94-
$p = dirname($vendorDir) . '/' . $p;
95-
spl_autoload_register(static function ($class) use ($ns, $p): void {
96-
if (strpos($class, $ns) !== 0) {
97-
return;
98-
}
99-
100-
$fileName = $p . str_replace('\\', DIRECTORY_SEPARATOR, substr($class, strlen($ns))) . '.php';
101-
/** @phpstan-ignore-next-line */
102-
if (! file_exists($fileName)) {
103-
return;
104-
}
105-
106-
include $fileName;
107-
});
108-
}
109-
}
110-
111-
/** @psalm-suppress UnresolvableInclude */
112-
require_once $composer->getConfig()->get('vendor-dir') . '/wyrihaximus/list-classes-in-directory/src/functions_include.php';
113-
/** @psalm-suppress UnresolvableInclude */
114-
require_once $composer->getConfig()->get('vendor-dir') . '/wyrihaximus/string-get-in/src/functions_include.php';
115-
/** @psalm-suppress UnresolvableInclude */
116-
require_once $composer->getConfig()->get('vendor-dir') . '/wyrihaximus/constants/src/Numeric/constants_include.php';
117-
/** @psalm-suppress UnresolvableInclude */
118-
require_once $composer->getConfig()->get('vendor-dir') . '/igorw/get-in/src/get_in.php';
119-
/** @psalm-suppress UnresolvableInclude */
120-
require_once $composer->getConfig()->get('vendor-dir') . '/jetbrains/phpstorm-stubs/PhpStormStubsMap.php';
121-
/** @psalm-suppress UnresolvableInclude */
122-
require_once $composer->getConfig()->get('vendor-dir') . '/thecodingmachine/safe/generated/filesystem.php';
123-
/** @psalm-suppress UnresolvableInclude */
124-
require_once $composer->getConfig()->get('vendor-dir') . '/thecodingmachine/safe/generated/strings.php';
125-
/** @psalm-suppress UnresolvableInclude */
126-
require_once $composer->getConfig()->get('vendor-dir') . '/wyrihaximus/simple-twig/src/functions_include.php';
127-
/** @psalm-suppress UnresolvableInclude */
128-
129-
$io->write('<info>mammatus/queue:</info> Locating actions');
130-
131-
$actions = self::findAllActions($composer, $io);
132-
133-
$io->write(sprintf('<info>mammatus/queue:</info> Found %s action(s)', count($actions)));
134-
135-
$classContentsList = render(
136-
file_get_contents(
137-
self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage()) . '/etc/generated_templates/AbstractList_.php.twig',
138-
),
139-
['workers' => $actions],
140-
);
141-
142-
$installPathList = self::locateRootPackageInstallPath($composer->getConfig(), $composer->getPackage())
143-
. '/src/Generated/AbstractList_.php';
144-
145-
file_put_contents($installPathList, $classContentsList);
146-
chmod($installPathList, 0664);
147-
148-
$io->write(sprintf(
149-
'<info>mammatus/queue:</info> Generated static abstract queue manager and queue list in %s second(s)',
150-
round(microtime(true) - $start, 2),
151-
));
152-
}
153-
154-
/**
155-
* Find the location where to put the generate PHP class in.
156-
*/
157-
private static function locateRootPackageInstallPath(
158-
Config $composerConfig,
159-
RootPackageInterface $rootPackage,
160-
): string {
161-
$vendorDir = $composerConfig->get('vendor-dir');
162-
if (! is_string($vendorDir)) {
163-
throw new RuntimeException('Vendor dir must be a string');
164-
}
165-
166-
// You're on your own
167-
if ($rootPackage->getName() === 'mammatus/queue') {
168-
return dirname($vendorDir);
169-
}
170-
171-
return $vendorDir . '/mammatus/queue';
172-
}
173-
174-
/** @return array<mixed> */
175-
private static function findAllActions(Composer $composer, IOInterface $io): array
176-
{
177-
$vendorDir = $composer->getConfig()->get('vendor-dir');
178-
if (! is_string($vendorDir)) {
179-
throw new RuntimeException('Vendor dir must be a string');
180-
}
181-
182-
retry:
183-
try {
184-
$classReflector = new DefaultReflector(
185-
(new MakeLocatorForComposerJsonAndInstalledJson())(dirname($vendorDir), (new BetterReflection())->astLocator()),
186-
);
187-
} catch (InvalidPrefixMapping $invalidPrefixMapping) {
188-
mkdir(explode('" is not a', explode('" for prefix "', $invalidPrefixMapping->getMessage())[1])[0]);
189-
goto retry;
190-
}
191-
192-
$packages = $composer->getRepositoryManager()->getLocalRepository()->getCanonicalPackages();
193-
$packages[] = $composer->getPackage();
194-
195-
return (new Collection($packages))->filter(static function (PackageInterface $package): bool {
196-
return count($package->getAutoload()) > 0;
197-
})->filter(static function (PackageInterface $package): bool {
198-
/** @phpstan-ignore-next-line */
199-
return getIn($package->getExtra(), 'mammatus.queue.has-workers', false);
200-
})->filter(static function (PackageInterface $package): bool {
201-
return array_key_exists('classmap', $package->getAutoload()) || array_key_exists('psr-4', $package->getAutoload());
202-
})->flatMap(static function (PackageInterface $package) use ($vendorDir): array {
203-
$packageName = $package->getName();
204-
$autoload = $package->getAutoload();
205-
$paths = [];
206-
foreach (['classmap', 'psr-4'] as $item) {
207-
if (! array_key_exists($item, $autoload)) {
208-
continue;
209-
}
210-
211-
foreach ($autoload[$item] as $path) {
212-
if (is_string($path)) {
213-
if ($package instanceof RootPackageInterface) {
214-
$paths[] = dirname($vendorDir) . DIRECTORY_SEPARATOR . $path;
215-
} else {
216-
$paths[] = $vendorDir . DIRECTORY_SEPARATOR . $packageName . DIRECTORY_SEPARATOR . $path;
217-
}
218-
}
219-
220-
if (! is_array($path)) {
221-
continue;
222-
}
223-
224-
foreach ($path as $p) {
225-
if ($package instanceof RootPackageInterface) {
226-
$paths[] = dirname($vendorDir) . DIRECTORY_SEPARATOR . $p;
227-
} else {
228-
$paths[] = $vendorDir . DIRECTORY_SEPARATOR . $packageName . DIRECTORY_SEPARATOR . $p;
229-
}
230-
}
231-
}
232-
}
233-
234-
return $paths;
235-
})->map(static function (string $path): string {
236-
return rtrim($path, '/');
237-
})->filter(static function (string $path): bool {
238-
/** @phpstan-ignore-next-line */
239-
return file_exists($path);
240-
})->flatMap(static function (string $path): array {
241-
return [...listClassesInDirectories($path)];
242-
/** @phpstan-ignore-next-line */
243-
})->flatMap(static function (string $class) use ($classReflector, $io): array {
244-
try {
245-
/** @psalm-suppress PossiblyUndefinedVariable */
246-
return [
247-
(static function (ReflectionClass $reflectionClass): ReflectionClass {
248-
$reflectionClass->getInterfaces();
249-
$reflectionClass->getMethods();
250-
251-
return $reflectionClass;
252-
})($classReflector->reflectClass($class)),
253-
];
254-
} catch (IdentifierNotFound $identifierNotFound) {
255-
$io->write(sprintf(
256-
'<info>mammatus/queue:</info> Error while reflecting "<fg=cyan>%s</>": <fg=yellow>%s</>',
257-
$class,
258-
$identifierNotFound->getMessage(),
259-
));
260-
}
261-
262-
return [];
263-
})->filter(static function (ReflectionClass $class): bool {
264-
return $class->isInstantiable();
265-
})->filter(static function (ReflectionClass $class): bool {
266-
return $class->implementsInterface(Worker::class);
267-
})->flatMap(static function (ReflectionClass $class): array {
268-
$attributes = [];
269-
foreach ((new \ReflectionClass($class->getName()))->getAttributes() as $attributeReflection) {
270-
$attribute = $attributeReflection->newInstance();
271-
$attributes[$attribute::class] = $attribute;
272-
}
273-
274-
return [
275-
[
276-
'class' => $class->getName(),
277-
'attributes' => $attributes,
278-
],
279-
];
280-
})->filter(static function (array $classNattributes): bool {
281-
return array_key_exists(Consumer::class, $classNattributes['attributes']);
282-
})->flatMap(static function (array $classNattributes): array {
283-
return [
284-
[
285-
'class' => $classNattributes['class'],
286-
'consumer' => $classNattributes['attributes'][Consumer::class],
287-
'split_out' => array_key_exists(SplitOut::class, $classNattributes['attributes']),
288-
],
289-
];
290-
})->toArray();
43+
GenerativePluginExecutioner::execute($event->getComposer(), $event->getIO(), new Plugin());
29144
}
29245
}

0 commit comments

Comments
 (0)