diff --git a/src/Framework/Container/Container.php b/src/Framework/Container/Container.php index 038a611..bd4f2da 100644 --- a/src/Framework/Container/Container.php +++ b/src/Framework/Container/Container.php @@ -1,11 +1,282 @@ factories = new \SplObjectStorage(); + $this->protected = new \SplObjectStorage(); + + foreach ($values as $key => $value) { + $this->offsetSet($key, $value); + } + } + + /** + * Sets a parameter or an object. + * + * Objects must be defined as Closures. + * + * Allowing any PHP callable leads to difficult to debug problems + * as function names (strings) are callable (creating a function with + * the same name as an existing parameter would break your container). + * + * @param string $id The unique identifier for the parameter or object + * @param mixed $value The value of the parameter or a closure to define an object + * + * @throws FrozenServiceException Prevent override of a frozen service + */ + public function offsetSet($id, $value) + { + + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + $this->values[$id] = $value; + $this->keys[$id] = true; + } + + /** + * Gets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function offsetGet($id) + { + if (!isset($this->keys[$id])) { + $tmpId = ucfirst($id); + if (!isset($this->values['serviceProviders'][$tmpId])) { + throw new UnknownIdentifierException($id); + } + + $provider = $this->values['serviceProviders'][$tmpId][0]; + $provider = new $provider($this); + $provider->register(); + } + + if ( + isset($this->raw[$id]) + || !is_object($this->values[$id]) + || isset($this->protected[$this->values[$id]]) + || !method_exists($this->values[$id], '__invoke') + ) { + return $this->values[$id]; + } + + if (isset($this->factories[$this->values[$id]])) { + return $this->values[$id]($this); + } + + $raw = $this->values[$id]; + $val = $this->values[$id] = $raw($this); + $this->raw[$id] = $raw; + + $this->frozen[$id] = true; + + return $val; + } + + /** + * Checks if a parameter or an object is set. + * + * @param string $id The unique identifier for the parameter or object + * + * @return bool + */ + public function offsetExists($id) + { + return isset($this->keys[$id]); + } + + /** + * Unsets a parameter or an object. + * + * @param string $id The unique identifier for the parameter or object + */ + public function offsetUnset($id) + { + if (isset($this->keys[$id])) { + if (is_object($this->values[$id])) { + unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]); + } + + unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]); + } + } + + /** + * Marks a callable as being a factory service. + * + * @param callable $callable A service definition to be used as a factory + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function factory($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.'); + } + + $this->factories->attach($callable); + + return $callable; + } + + /** + * Protects a callable from being interpreted as a service. + * + * This is useful when you want to store a callable as a parameter. + * + * @param callable $callable A callable to protect from being evaluated + * + * @return callable The passed callable + * + * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object + */ + public function protect($callable) + { + if (!method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Callable is not a Closure or invokable object.'); + } + + $this->protected->attach($callable); + + return $callable; + } + + /** + * Gets a parameter or the closure defining an object. + * + * @param string $id The unique identifier for the parameter or object + * + * @return mixed The value of the parameter or the closure defining an object + * + * @throws UnknownIdentifierException If the identifier is not defined + */ + public function raw($id) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->raw[$id])) { + return $this->raw[$id]; + } + + return $this->values[$id]; + } + + /** + * Extends an object definition. + * + * Useful when you want to extend an existing object definition, + * without necessarily loading that object. + * + * @param string $id The unique identifier for the object + * @param callable $callable A service definition to extend the original + * + * @return callable The wrapped callable + * + * @throws UnknownIdentifierException If the identifier is not defined + * @throws FrozenServiceException If the service is frozen + * @throws InvalidServiceIdentifierException If the identifier belongs to a parameter + * @throws ExpectedInvokableException If the extension callable is not a closure or an invokable object + */ + public function extend($id, $callable) + { + if (!isset($this->keys[$id])) { + throw new UnknownIdentifierException($id); + } + + if (isset($this->frozen[$id])) { + throw new FrozenServiceException($id); + } + + if (!is_object($this->values[$id]) || !method_exists($this->values[$id], '__invoke')) { + throw new InvalidServiceIdentifierException($id); + } + + if (isset($this->protected[$this->values[$id]])) { + @trigger_error(sprintf('How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?', $id), E_USER_DEPRECATED); + } + + if (!is_object($callable) || !method_exists($callable, '__invoke')) { + throw new ExpectedInvokableException('Extension service definition is not a Closure or invokable object.'); + } + + $factory = $this->values[$id]; + + $extended = function ($c) use ($callable, $factory) { + return $callable($factory($c), $c); + }; + + if (isset($this->factories[$factory])) { + $this->factories->detach($factory); + $this->factories->attach($extended); + } + + return $this[$id] = $extended; + } + + /** + * Returns all defined value names. + * + * @return array An array of value names + */ + public function keys() + { + return array_keys($this->values); + } + + /** + * Registers a service provider. + * + * @param ServiceProviderInterface $provider A ServiceProviderInterface instance + * @param array $values An array of values that customizes the provider + * + * @return static + */ + public function register(ServiceProviderInterface $provider, array $values = array()) + { + $provider->register($this); + + foreach ($values as $key => $value) { + $this[$key] = $value; + } + return $this; + } } \ No newline at end of file diff --git a/src/Framework/Doctrine/DoctrineServiceProvider.php b/src/Framework/Doctrine/DoctrineServiceProvider.php index 5be84d8..02fd0e8 100644 --- a/src/Framework/Doctrine/DoctrineServiceProvider.php +++ b/src/Framework/Doctrine/DoctrineServiceProvider.php @@ -14,15 +14,5 @@ public function register() $this->app->singleton('doctrineService')->register($this->app); \Config::inject('dbs'); - - //TODO - if (class_exists('\\TastPHP\\Service\\ServiceKernel')) { - $this->app['service_kernel'] = \TastPHP\Service\ServiceKernel::instance(); - $this->app['service_kernel']->setContainer($this->app); - $this->app['service_kernel']->setConnection($this->app['dbs']); - if (!empty($this->app['service_kernel']->getConnection()['master'])) { - $this->app['service_kernel']->getConnection()['master']->exec("SET names utf8mb4"); - } - } } } \ No newline at end of file diff --git a/src/Framework/Kernel.php b/src/Framework/Kernel.php index 3565e77..f451980 100644 --- a/src/Framework/Kernel.php +++ b/src/Framework/Kernel.php @@ -2,16 +2,28 @@ namespace TastPHP\Framework; +use TastPHP\Framework\Cache\CacheServiceProvider; +use TastPHP\Framework\Cache\FileCacheServiceProvider; +use TastPHP\Framework\Cache\RedisServiceProvider; use TastPHP\Framework\Config\Config; use TastPHP\Framework\Config\ConfigServiceProvider; +use TastPHP\Framework\Container\PimpleContainerProvider; +use TastPHP\Framework\CsrfToken\CsrfTokenServiceProvider; +use TastPHP\Framework\Doctrine\DoctrineServiceProvider; use TastPHP\Framework\EventDispatcher\EventDispatcherServiceProvider; use TastPHP\Framework\ExceptionHandler\ExceptionHandlerServiceProvider; use TastPHP\Framework\Container\Container; +use TastPHP\Framework\Jwt\JwtServiceProvider; use TastPHP\Framework\ListenerRegister\ListenerRegisterServiceProvider; use TastPHP\Framework\Http\RequestServiceProvider; +use TastPHP\Framework\Logger\LoggerServiceProvider; +use TastPHP\Framework\Queue\QueueServiceProvider; use TastPHP\Framework\Router\RouterServiceProvider; +use TastPHP\Framework\Service\ServiceKernelProvider; +use TastPHP\Framework\SwiftMailer\SwiftMailerServiceProvider; use TastPHP\Framework\Traits\KernelListeners; use TastPHP\Framework\Traits\KernelTrait; +use TastPHP\Framework\Twig\TwigServiceProvider; /** * Class Kernel @@ -36,23 +48,25 @@ class Kernel extends Container * @var serviceProviders array */ protected $serviceProviders = [ - 'Config' => ConfigServiceProvider::class, - 'Request' => RequestServiceProvider::class, - 'Redis' => "", - 'Cache' => "", - 'FileCache' => "", - 'Logger' => "", - 'EventDispatcher' => EventDispatcherServiceProvider::class, - 'Twig' => "", - 'Doctrine' => "", - 'CsrfToken' => "", - 'Jwt' => "", - 'ListenerRegister' => ListenerRegisterServiceProvider::class, - 'SwiftMailer' => "", - 'Queue' => "", - 'Router' => RouterServiceProvider::class, - 'ExceptionHandler' => ExceptionHandlerServiceProvider::class, - 'Container' => "" + 'Config' => [ConfigServiceProvider::class,true], + 'Request' => [RequestServiceProvider::class,true], + 'Redis' => [RedisServiceProvider::class,false], + 'Cache' => [CacheServiceProvider::class,false], + 'FileCache' => [FileCacheServiceProvider::class,false], + 'Logger' => [LoggerServiceProvider::class,false], + 'EventDispatcher' => [EventDispatcherServiceProvider::class,true], + 'Twig' => [TwigServiceProvider::class,false], +// 'Doctrine' => [DoctrineServiceProvider::class,false], //v1.7.8 remove + 'Dbs' => [DoctrineServiceProvider::class,false], + 'ServiceKernel' => [ServiceKernelProvider::class,false], + 'CsrfToken' => [CsrfTokenServiceProvider::class,false], + 'Jwt' => [JwtServiceProvider::class,false], + 'ListenerRegister' => [ListenerRegisterServiceProvider::class,true], + 'SwiftMailer' => [SwiftMailerServiceProvider::class,false], + 'Queue' => [QueueServiceProvider::class,false], + 'Router' => [RouterServiceProvider::class,true], + 'ExceptionHandler' => [ExceptionHandlerServiceProvider::class,true], + 'Container' => [PimpleContainerProvider::class,false] ]; /** @@ -67,7 +81,7 @@ class Kernel extends Container public function __construct(array $values = []) { $start = microtime(true); - $this['version'] = 'v1.7.7'; + $this['version'] = 'v1.7.8'; $this['start_time'] = $start; self::$instance = $this; parent::__construct($values); diff --git a/src/Framework/Service/ServiceKernelProvider.php b/src/Framework/Service/ServiceKernelProvider.php new file mode 100644 index 0000000..5836e67 --- /dev/null +++ b/src/Framework/Service/ServiceKernelProvider.php @@ -0,0 +1,25 @@ +app['serviceKernel'] = \TastPHP\Service\ServiceKernel::instance(); + $this->app['serviceKernel']->setContainer($this->app); + $this->app['serviceKernel']->setConnection($this->app['dbs']); + if (!empty($this->app['serviceKernel']->getConnection()['master'])) { + $this->app['serviceKernel']->getConnection()['master']->exec("SET names utf8mb4"); + } + } + } +} \ No newline at end of file diff --git a/src/Framework/Traits/KernelTrait.php b/src/Framework/Traits/KernelTrait.php index 031a4e4..6e3a2d7 100644 --- a/src/Framework/Traits/KernelTrait.php +++ b/src/Framework/Traits/KernelTrait.php @@ -73,10 +73,19 @@ public function aliasLoader() */ public function registerServices() { - foreach ($this->serviceProviders as $provider) { + $this['serviceProviders'] = $this->serviceProviders; + + foreach ($this->serviceProviders as $providerArray) { + list($provider,$enabled) = $providerArray; + + if (!$enabled) { + continue; + } + if (empty($provider) || !class_exists($provider)) { continue; } + $provider = new $provider(self::$instance); $provider->register(); } @@ -172,52 +181,58 @@ public function getListeners($key = '') protected function registerRedisService() { - $this->replaceServiceProvider("Redis", RedisServiceProvider::class); + $this->replaceServiceProvider("Redis", [RedisServiceProvider::class,true]); } protected function registerCacheService() { - $this->replaceServiceProvider("Cache", CacheServiceProvider::class); + $this->replaceServiceProvider("Cache", [CacheServiceProvider::class,true]); } protected function registerFileCacheService() { - $this->replaceServiceProvider("FileCache", FileCacheServiceProvider::class); + $this->replaceServiceProvider("FileCache", [FileCacheServiceProvider::class,true]); } protected function registerLoggerService() { - $this->replaceServiceProvider("Logger", LoggerServiceProvider::class); + $this->replaceServiceProvider("Logger", [LoggerServiceProvider::class,true]); } protected function registerTwigService() { - $this->replaceServiceProvider("Twig", TwigServiceProvider::class); + $this->replaceServiceProvider("Twig", [TwigServiceProvider::class,true]); } - protected function registerDoctrineService() + //v1.7.8 remove +// protected function registerDoctrineService() +// { +// $this->replaceServiceProvider("Doctrine", [DoctrineServiceProvider::class,true]); +// } + + protected function registerDbsService() { - $this->replaceServiceProvider("Doctrine", DoctrineServiceProvider::class); + $this->replaceServiceProvider("Dbs", [DoctrineServiceProvider::class,true]); } protected function registerCsrfTokenService() { - $this->replaceServiceProvider("CsrfToken", CsrfTokenServiceProvider::class); + $this->replaceServiceProvider("CsrfToken", [CsrfTokenServiceProvider::class,true]); } protected function registerJwtService() { - $this->replaceServiceProvider("Jwt", JwtServiceProvider::class); + $this->replaceServiceProvider("Jwt", [JwtServiceProvider::class,true]); } protected function registerSwiftMailerService() { - $this->replaceServiceProvider("SwiftMailer", SwiftMailerServiceProvider::class); + $this->replaceServiceProvider("SwiftMailer", [SwiftMailerServiceProvider::class,true]); } protected function registerQueueService() { - $this->replaceServiceProvider("Queue", QueueServiceProvider::class); + $this->replaceServiceProvider("Queue", [QueueServiceProvider::class,true]); } // kernel listener register