Skip to content

Commit

Permalink
support delays in registration of serviceProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
xujiajun committed Oct 30, 2017
1 parent 6b8923c commit 15e31c8
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 41 deletions.
273 changes: 272 additions & 1 deletion src/Framework/Container/Container.php
Original file line number Diff line number Diff line change
@@ -1,11 +1,282 @@
<?php

namespace TastPHP\Framework\Container;

use Pimple\Exception\ExpectedInvokableException;
use Pimple\Exception\FrozenServiceException;
use Pimple\Exception\InvalidServiceIdentifierException;
use Pimple\Exception\UnknownIdentifierException;

/**
* Custom based on Pimple
* Class Container
* @package TastPHP\Framework\Container
*/
class Container extends \Pimple\Container
class Container implements \ArrayAccess
{
private $values = array();
private $factories;
private $protected;
private $frozen = array();
private $raw = array();
private $keys = array();

/**
* Instantiates the container.
*
* Objects and parameters can be passed as argument to the constructor.
*
* @param array $values The parameters or objects
*/
public function __construct(array $values = array())
{
$this->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;
}
}
10 changes: 0 additions & 10 deletions src/Framework/Doctrine/DoctrineServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
}
}
50 changes: 32 additions & 18 deletions src/Framework/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]
];

/**
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 15e31c8

Please sign in to comment.