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\DocBlockResolver\DocBlockResolverCache; |
||
21 | use Gacela\Framework\Exception\GacelaNotBootstrappedException; |
||
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
![]() |
|||
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 application root dir set when bootstrapping gacela |
|
71 | */ |
||
72 | 38 | public static function rootDir(): string |
|
73 | 1 | { |
|
74 | if (self::$appRootDir === null) { |
||
75 | 37 | throw new GacelaNotBootstrappedException(); |
|
76 | } |
||
77 | |||
78 | return self::$appRootDir; |
||
79 | } |
||
80 | |||
81 | 106 | /** |
|
82 | * Add an anonymous class as 'Config', 'Factory' or 'Provider' as a global resource |
||
83 | 106 | * bound to the context that it is passed as second argument. |
|
84 | 74 | * |
|
85 | * @param object|string $context It can be the string-key (file path) or the class/object itself. |
||
86 | * If empty then the caller's file will be use |
||
87 | 32 | */ |
|
88 | 32 | public static function addGlobal(object $resolvedClass, object|string $context = ''): void |
|
89 | 32 | { |
|
90 | 32 | if (is_string($context) && is_file($context)) { |
|
91 | 32 | $context = basename($context, '.php'); |
|
92 | 32 | } elseif ($context === '') { |
|
93 | $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); |
||
94 | 32 | $callerFile = $trace[0]['file'] ?? __FILE__; |
|
95 | $context = basename($callerFile, '.php'); |
||
96 | } |
||
97 | |||
98 | 32 | AnonymousGlobal::addGlobal($context, $resolvedClass); |
|
99 | } |
||
100 | |||
101 | 58 | public static function overrideExistingResolvedClass(string $className, object $resolvedClass): void |
|
102 | { |
||
103 | 58 | AnonymousGlobal::overrideExistingResolvedClass($className, $resolvedClass); |
|
104 | 58 | } |
|
105 | 58 | ||
106 | 58 | /** |
|
107 | 58 | * @param null|Closure(GacelaConfig):void $configFn |
|
108 | 58 | */ |
|
109 | 58 | private static function processConfigFnIntoSetup(?Closure $configFn = null): SetupGacelaInterface |
|
110 | 58 | { |
|
111 | 58 | if ($configFn instanceof Closure) { |
|
112 | 58 | return SetupGacela::fromCallable($configFn); |
|
113 | 58 | } |
|
114 | |||
115 | $gacelaFilePath = sprintf( |
||
116 | 106 | '%s%s%s', |
|
117 | self::rootDir(), |
||
118 | 106 | DIRECTORY_SEPARATOR, |
|
119 | self::GACELA_PHP_FILENAME, |
||
120 | 106 | ); |
|
121 | |||
122 | 106 | if (is_file($gacelaFilePath)) { |
|
123 | return SetupGacela::fromFile($gacelaFilePath); |
||
124 | 5 | } |
|
125 | 4 | ||
126 | 1 | return new SetupGacela(); |
|
127 | } |
||
128 | 5 | ||
129 | private static function resetCache(): void |
||
130 | { |
||
131 | AnonymousGlobal::resetCache(); |
||
132 | AbstractFacade::resetCache(); |
||
133 | AbstractFactory::resetCache(); |
||
134 | AbstractClassResolver::resetCache(); |
||
135 | InMemoryCache::resetCache(); |
||
136 | GacelaFileCache::resetCache(); |
||
137 | DocBlockResolverCache::resetCache(); |
||
138 | ClassResolverCache::resetCache(); |
||
139 | ConfigFactory::resetCache(); |
||
140 | Config::resetInstance(); |
||
141 | Locator::resetInstance(); |
||
142 | } |
||
143 | |||
144 | private static function runPlugins(Config $config): void |
||
145 | { |
||
146 | self::$mainContainer = Container::withConfig($config); |
||
147 | |||
148 | $plugins = $config->getSetupGacela()->getPlugins(); |
||
149 | |||
150 | foreach ($plugins as $plugin) { |
||
151 | /** @var callable $current */ |
||
152 | $current = is_string($plugin) |
||
153 | ? self::$mainContainer->get($plugin) |
||
154 | : $plugin; |
||
155 | |||
156 | self::$mainContainer->resolve($current); |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 |