gacela-project /
gacela
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | namespace Gacela\Framework; |
||
| 6 | |||
| 7 | use Closure; |
||
| 8 | use Gacela\Framework\Bootstrap\GacelaConfig; |
||
| 9 | use Gacela\Framework\Bootstrap\SetupGacela; |
||
| 10 | use Gacela\Framework\Bootstrap\SetupGacelaInterface; |
||
| 11 | use Gacela\Framework\ClassResolver\AbstractClassResolver; |
||
| 12 | use Gacela\Framework\ClassResolver\Cache\GacelaFileCache; |
||
| 13 | use Gacela\Framework\ClassResolver\Cache\InMemoryCache; |
||
| 14 | use Gacela\Framework\ClassResolver\ClassResolverCache; |
||
| 15 | use Gacela\Framework\ClassResolver\GlobalInstance\AnonymousGlobal; |
||
| 16 | use Gacela\Framework\Config\Config; |
||
| 17 | use Gacela\Framework\Config\ConfigFactory; |
||
| 18 | use Gacela\Framework\Container\Container; |
||
| 19 | use Gacela\Framework\Container\Locator; |
||
| 20 | use Gacela\Framework\Exception\GacelaNotBootstrappedException; |
||
| 21 | use Gacela\Framework\ServiceResolver\DocBlockResolverCache; |
||
| 22 | |||
| 23 | use function is_string; |
||
| 24 | use function sprintf; |
||
| 25 | |||
| 26 | final class Gacela |
||
| 27 | { |
||
| 28 | private const GACELA_PHP_FILENAME = 'gacela.php'; |
||
| 29 | |||
| 30 | private static ?Container $mainContainer = null; |
||
| 31 | |||
| 32 | private static ?string $appRootDir = null; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * Define the entry point of Gacela. |
||
| 36 | * |
||
| 37 | 106 | * @param null|Closure(GacelaConfig):void $configFn |
|
| 38 | */ |
||
| 39 | 106 | public static function bootstrap(string $appRootDir, ?Closure $configFn = null): void |
|
| 40 | 106 | { |
|
| 41 | self::$appRootDir = $appRootDir; |
||
| 42 | 106 | self::$mainContainer = null; |
|
| 43 | |||
| 44 | 106 | $setup = self::processConfigFnIntoSetup($configFn); |
|
| 45 | 58 | ||
| 46 | if ($setup->shouldResetInMemoryCache()) { |
||
| 47 | self::resetCache(); |
||
| 48 | 106 | } |
|
| 49 | 106 | ||
| 50 | 106 | $config = Config::createWithSetup($setup); |
|
| 51 | $config->setAppRootDir($appRootDir) |
||
| 52 | 106 | ->init(); |
|
| 53 | |||
| 54 | self::runPlugins($config); |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @template T |
||
| 59 | * |
||
| 60 | * @param class-string<T> $className |
||
|
0 ignored issues
–
show
Documentation
Bug
introduced
by
Loading history...
|
|||
| 61 | * |
||
| 62 | 7 | * @return T|null |
|
| 63 | */ |
||
| 64 | 7 | public static function get(string $className): mixed |
|
| 65 | { |
||
| 66 | return Locator::getSingleton($className, self::$mainContainer); |
||
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | 38 | * Get the main dependency injection container. |
|
| 71 | * This is the actual container created during bootstrap with all runtime bindings and frozen services. |
||
| 72 | 38 | * |
|
| 73 | 1 | * @throws GacelaNotBootstrappedException if Gacela has not been bootstrapped yet |
|
| 74 | */ |
||
| 75 | 37 | public static function container(): Container |
|
| 76 | { |
||
| 77 | if (!self::$mainContainer instanceof Container) { |
||
| 78 | throw new GacelaNotBootstrappedException(); |
||
| 79 | } |
||
| 80 | |||
| 81 | 106 | return self::$mainContainer; |
|
| 82 | } |
||
| 83 | 106 | ||
| 84 | 74 | /** |
|
| 85 | * Get the application root dir set when bootstrapping gacela |
||
| 86 | */ |
||
| 87 | 32 | public static function rootDir(): string |
|
| 88 | 32 | { |
|
| 89 | 32 | if (self::$appRootDir === null) { |
|
| 90 | 32 | throw new GacelaNotBootstrappedException(); |
|
| 91 | 32 | } |
|
| 92 | 32 | ||
| 93 | return self::$appRootDir; |
||
| 94 | 32 | } |
|
| 95 | |||
| 96 | /** |
||
| 97 | * Add an anonymous class as 'Config', 'Factory' or 'Provider' as a global resource |
||
| 98 | 32 | * bound to the context that it is passed as second argument. |
|
| 99 | * |
||
| 100 | * @param object|string $context It can be the string-key (file path) or the class/object itself. |
||
| 101 | 58 | * If empty then the caller's file will be use |
|
| 102 | */ |
||
| 103 | 58 | public static function addGlobal(object $resolvedClass, object|string $context = ''): void |
|
| 104 | 58 | { |
|
| 105 | 58 | if (is_string($context) && is_file($context)) { |
|
| 106 | 58 | $context = basename($context, '.php'); |
|
| 107 | 58 | } elseif ($context === '') { |
|
| 108 | 58 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); |
|
| 109 | 58 | $callerFile = $trace[0]['file'] ?? __FILE__; |
|
| 110 | 58 | $context = basename($callerFile, '.php'); |
|
| 111 | 58 | } |
|
| 112 | 58 | ||
| 113 | 58 | AnonymousGlobal::addGlobal($context, $resolvedClass); |
|
| 114 | } |
||
| 115 | |||
| 116 | 106 | public static function overrideExistingResolvedClass(string $className, object $resolvedClass): void |
|
| 117 | { |
||
| 118 | 106 | AnonymousGlobal::overrideExistingResolvedClass($className, $resolvedClass); |
|
| 119 | } |
||
| 120 | 106 | ||
| 121 | /** |
||
| 122 | 106 | * @param null|Closure(GacelaConfig):void $configFn |
|
| 123 | */ |
||
| 124 | 5 | private static function processConfigFnIntoSetup(?Closure $configFn = null): SetupGacelaInterface |
|
| 125 | 4 | { |
|
| 126 | 1 | if ($configFn instanceof Closure) { |
|
| 127 | return SetupGacela::fromCallable($configFn); |
||
| 128 | 5 | } |
|
| 129 | |||
| 130 | $gacelaFilePath = sprintf( |
||
| 131 | '%s%s%s', |
||
| 132 | self::rootDir(), |
||
| 133 | DIRECTORY_SEPARATOR, |
||
| 134 | self::GACELA_PHP_FILENAME, |
||
| 135 | ); |
||
| 136 | |||
| 137 | if (is_file($gacelaFilePath)) { |
||
| 138 | return SetupGacela::fromFile($gacelaFilePath); |
||
| 139 | } |
||
| 140 | |||
| 141 | return new SetupGacela(); |
||
| 142 | } |
||
| 143 | |||
| 144 | private static function resetCache(): void |
||
| 145 | { |
||
| 146 | AnonymousGlobal::resetCache(); |
||
| 147 | AbstractFacade::resetCache(); |
||
| 148 | AbstractFactory::resetCache(); |
||
| 149 | AbstractClassResolver::resetCache(); |
||
| 150 | InMemoryCache::resetCache(); |
||
| 151 | GacelaFileCache::resetCache(); |
||
| 152 | DocBlockResolverCache::resetCache(); |
||
| 153 | ClassResolverCache::resetCache(); |
||
| 154 | ConfigFactory::resetCache(); |
||
| 155 | Config::resetInstance(); |
||
| 156 | Locator::resetInstance(); |
||
| 157 | } |
||
| 158 | |||
| 159 | private static function runPlugins(Config $config): void |
||
| 160 | { |
||
| 161 | self::$mainContainer = Container::withConfig($config); |
||
| 162 | |||
| 163 | $plugins = $config->getSetupGacela()->getPlugins(); |
||
| 164 | |||
| 165 | foreach ($plugins as $plugin) { |
||
| 166 | /** @var callable $current */ |
||
| 167 | $current = is_string($plugin) |
||
| 168 | ? self::$mainContainer->get($plugin) |
||
| 169 | : $plugin; |
||
| 170 | |||
| 171 | self::$mainContainer->resolve($current); |
||
| 172 | } |
||
| 173 | } |
||
| 174 | } |
||
| 175 |