Skip to content
Snippets Groups Projects
Commit 82c93e2d authored by Mateusz Charytoniuk's avatar Mateusz Charytoniuk
Browse files

feat: RequiresPhpExtension attribute

parent 0f85b9fc
No related branches found
No related tags found
No related merge requests found
Showing
with 86 additions and 10 deletions
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\Attribute;
use Attribute;
use Distantmagic\Resonance\Attribute as BaseAttribute;
#[Attribute(Attribute::IS_REPEATABLE | Attribute::TARGET_CLASS)]
final readonly class RequiresPhpExtension extends BaseAttribute
{
/**
* @param non-empty-string $name
*/
public function __construct(public string $name) {}
}
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance\Command;
use Distantmagic\Resonance\Attribute\ConsoleCommand;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\WantsFeature;
use Distantmagic\Resonance\Command;
use Distantmagic\Resonance\Feature;
......@@ -16,6 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface;
name: 'grpc:generate',
description: 'Generate GRPC stubs'
)]
#[RequiresPhpExtension('grpc')]
#[WantsFeature(Feature::Grpc)]
final class GrpcGenerate extends Command
{
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance\Command;
use Distantmagic\Resonance\Attribute\ConsoleCommand;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\WantsFeature;
use Distantmagic\Resonance\Command;
use Distantmagic\Resonance\CoroutineCommand;
......@@ -22,6 +23,8 @@ use Symfony\Component\Console\Output\OutputInterface;
name: 'postfix:bounce',
description: 'Handles email bounces (requires mailparse)'
)]
#[RequiresPhpExtension('http')]
#[RequiresPhpExtension('mailparse')]
#[WantsFeature(Feature::Postfix)]
final class PostfixBounce extends CoroutineCommand
{
......@@ -36,10 +39,6 @@ final class PostfixBounce extends CoroutineCommand
protected function executeInCoroutine(InputInterface $input, OutputInterface $output): int
{
if (!extension_loaded('mailparse') || !extension_loaded('http')) {
throw new RuntimeException('You need to install "http" and "mailparse" extensions');
}
$content = stream_get_contents(STDIN);
if (false === $content || empty($content)) {
......
......@@ -6,10 +6,10 @@ namespace Distantmagic\Resonance\Command;
use Distantmagic\Resonance\ApplicationConfiguration;
use Distantmagic\Resonance\Attribute\ConsoleCommand;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Command;
use Distantmagic\Resonance\InotifyIterator;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Swoole\Process;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
......@@ -17,8 +17,9 @@ use Symfony\Component\Console\Output\OutputInterface;
#[ConsoleCommand(
name: 'watch:command',
description: 'Watch project files for changes (requires inotify)'
description: 'Watch project files for changes'
)]
#[RequiresPhpExtension('inotify')]
final class Watch extends Command
{
private ?Process $process = null;
......@@ -37,10 +38,6 @@ final class Watch extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!extension_loaded('inotify')) {
throw new RuntimeException('You need to install "inotify" extension');
}
/**
* @var string $childCommandName
*/
......
......@@ -5,8 +5,10 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use Closure;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\DependencyInjectionContainerException\AmbiguousProvider;
use Distantmagic\Resonance\DependencyInjectionContainerException\DisabledFeatureProvider;
use Distantmagic\Resonance\DependencyInjectionContainerException\MissingPhpExtension;
use Distantmagic\Resonance\DependencyInjectionContainerException\MissingProvider;
use Ds\Map;
use Ds\Set;
......@@ -224,6 +226,17 @@ readonly class DependencyInjectionContainer
*/
private function makeClassFromReflection(ReflectionClass $reflectionClass, DependencyStack $stack): object
{
$reflectionClassAttributeManager = new ReflectionClassAttributeManager($reflectionClass);
$requiredPhpExtensions = $reflectionClassAttributeManager->findAttributes(RequiresPhpExtension::class);
if (!$requiredPhpExtensions->isEmpty()) {
foreach ($requiredPhpExtensions as $requiredPhpExtension) {
if (!extension_loaded($requiredPhpExtension->name)) {
throw new MissingPhpExtension($reflectionClass->name, $requiredPhpExtension->name);
}
}
}
$constructorReflection = $reflectionClass->getConstructor();
if ($constructorReflection) {
......
<?php
declare(strict_types=1);
namespace Distantmagic\Resonance\DependencyInjectionContainerException;
use Distantmagic\Resonance\DependencyInjectionContainerException;
use Throwable;
class MissingPhpExtension extends DependencyInjectionContainerException
{
/**
* @param class-string $className
* @param non-empty-string $extensionName
*/
public function __construct(
string $className,
string $extensionName,
?Throwable $previous = null,
) {
parent::__construct(
sprintf(
'To use "%s" you need to install "%s" PHP extension.',
$className,
$extensionName,
),
$previous
);
}
}
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
......@@ -13,6 +14,7 @@ use Doctrine\DBAL\Driver\AbstractMySQLDriver;
*
* @psalm-suppress DeprecatedInterface
*/
#[RequiresPhpExtension('pdo')]
#[Singleton]
class DoctrineMySQLDriver extends AbstractMySQLDriver
{
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
......@@ -13,6 +14,7 @@ use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
*
* @psalm-suppress DeprecatedInterface
*/
#[RequiresPhpExtension('pdo')]
#[Singleton]
class DoctrinePostgreSQLDriver extends AbstractPostgreSQLDriver
{
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
......@@ -13,6 +14,7 @@ use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
*
* @psalm-suppress DeprecatedInterface
*/
#[RequiresPhpExtension('pdo')]
#[Singleton]
class DoctrineSQLiteDriver extends AbstractSQLiteDriver
{
......
......@@ -6,11 +6,13 @@ namespace Distantmagic\Resonance;
use DateTimeImmutable;
use DateTimeInterface;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use IntlDateFormatter;
use Swoole\Http\Request;
#[Singleton]
#[RequiresPhpExtension('intl')]
final readonly class IntlFormatter
{
private IntlDateFormatterRepository $formatters;
......
......@@ -5,12 +5,14 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use CurlHandle;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Generator;
use JsonSerializable;
use RuntimeException;
use Swoole\Coroutine\Channel;
#[RequiresPhpExtension('curl')]
#[Singleton]
readonly class LlamaCppClient
{
......
......@@ -4,9 +4,11 @@ declare(strict_types=1);
namespace Distantmagic\Resonance;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use SQLite3;
#[RequiresPhpExtension('sqlite3')]
#[Singleton]
readonly class SQLiteVSSConnectionBuilder
{
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\DatabaseConfiguration;
use Distantmagic\Resonance\DatabaseConnectionPoolRepository;
......@@ -17,6 +18,7 @@ use Swoole\Database\PDOConfig;
/**
* @template-extends SingletonProvider<DatabaseConnectionPoolRepository>
*/
#[RequiresPhpExtension('pdo')]
#[Singleton(provides: DatabaseConnectionPoolRepository::class)]
final readonly class DatabaseConnectionPoolRepositoryProvider extends SingletonProvider
{
......
......@@ -6,6 +6,7 @@ namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\ApplicationConfiguration;
use Distantmagic\Resonance\Attribute\GrantsFeature;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\DoctrineConnectionRepository;
use Distantmagic\Resonance\DoctrineEntityManagerRepository;
......@@ -21,6 +22,7 @@ use Symfony\Component\Filesystem\Filesystem;
/**
* @template-extends SingletonProvider<DoctrineEntityManagerRepository>
*/
#[RequiresPhpExtension('pdo')]
#[GrantsFeature(Feature::Doctrine)]
#[Singleton(provides: DoctrineEntityManagerRepository::class)]
final readonly class DoctrineEntityManagerRepositoryProvider extends SingletonProvider
......
......@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Distantmagic\Resonance\SingletonProvider;
use Distantmagic\Resonance\Attribute\BuildsPDOPoolConnection;
use Distantmagic\Resonance\Attribute\RequiresPhpExtension;
use Distantmagic\Resonance\Attribute\RequiresSingletonCollection;
use Distantmagic\Resonance\Attribute\Singleton;
use Distantmagic\Resonance\HttpResponderCollection;
......@@ -19,6 +20,7 @@ use Distantmagic\Resonance\SingletonProvider;
/**
* @template-extends SingletonProvider<HttpResponderCollection>
*/
#[RequiresPhpExtension('pdo')]
#[RequiresSingletonCollection(SingletonCollection::PDOPoolConnectionBuilder)]
#[Singleton(provides: PDOPoolConnectionBuilderCollection::class)]
final readonly class PDOPoolConnectionBuilderCollectionProvider extends SingletonProvider
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment