Coder Social home page Coder Social logo

Comments (9)

niden avatar niden commented on May 31, 2024 2

@Laxilef Actually you stepped on Pandora's box. I have spent a great amount of hours trying to fix the DI container, looking towards a PSR-11 implementation. I was unsuccessful because of all the Di::getDefault() calls around the components.

In 4.1 and further I was going to slowly implement a PSR-11 container which would be much more flexible and offer everything we need including lazy loading of components. To get there however we will need to rewrite or refactor a lot of components.

from phalcon.

Laxilef avatar Laxilef commented on May 31, 2024

Working and tested code:

        /**
         * Pass the DI to the instance if it implements
         * \Phalcon\Di\InjectionAwareInterface
         */
        if typeof instance == "object" {
            if instance instanceof InjectionAwareInterface && !(instance->getDi() instanceof DiInterface) {
                instance->setDI(this);
            }
        }

php:

		/**
		 * Pass the DI to the instance if it implements
		 * \Phalcon\Di\InjectionAwareInterface
		 */
		if ( gettype($instance) == "object" ) {
			if ( $instance instanceof InjectionAwareInterface && !($instance->getDi() instanceof DiInterface) ) {
				$instance->setDI($this);
			}
		}

Method code in php:

	/**
	 * @param string $name
	 * @param null $parameters
	 * @return mixed
	 * @throws Exception
	 * @throws \ReflectionException
	 */
	public function get($name, $parameters = null) {
		/**
		 * If the service is shared and it already has a cached instance then
		 * immediately return it without triggering events.
		 */

		$service = $eventsManager = $isShared = $instance = null;

		if ( isset($this->services[$name]) ) {
			$service = $this->services[$name];
			$isShared = $service->isShared();

			if ( $isShared && isset($this->sharedInstances[$name]) ) {
				return $this->sharedInstances[$name];
			}
		}

		$eventsManager = $this->eventsManager;

		/**
		 * Allows for custom creation of instances through the
		 * "di:beforeServiceResolve" event.
		 */
		if ( gettype($eventsManager) == "object" ) {
			$instance = $eventsManager->fire(
				"di:beforeServiceResolve",
				$this,
				[
					"name" => $name,
					"parameters" => $parameters
				]
			);
		}

		if ( gettype($instance) != "object" ) {
			if ( $service !== null ) {
				// The service is registered in the DI.
				try {
					$instance = $service->resolve($parameters, $this);
				} catch ( ServiceResolutionException $e ) {
					throw new Exception(
						"Service '" . $name . "' cannot be resolved"
					);
				}

				// If the service is shared then we'll cache the instance.
				if ( $isShared ) {
					$this->sharedInstances[$name] = $instance;
				}
			} else {
				/**
				 * The DI also acts as builder for any class even if it isn't
				 * defined in the DI
				 */
				if ( !class_exists($name) ) {
					throw new Exception(
						"Service '" . $name . "' wasn't found in the dependency injection container"
					);
				}

				if ( gettype($parameters) == "array" && count($parameters) ) {
					//$instance = call_user_func_array($name, $parameters);
					$reflect  = new \ReflectionClass($name);
					$instance = $reflect->newInstanceArgs($parameters);

				} else {
					//$instance = call_user_func($name);
					$reflect  = new \ReflectionClass($name);
					$instance = $reflect->newInstance();
				}
			}
		}

		/**
		 * Pass the DI to the instance if it implements
		 * \Phalcon\Di\InjectionAwareInterface
		 */
		if ( gettype($instance) == "object" ) {
			if ( $instance instanceof InjectionAwareInterface && !($instance->getDi() instanceof DiInterface) ) {
				$instance->setDI($this);
			}
		}

		/**
		 * Allows for post creation instance configuration through the
		 * "di:afterServiceResolve" event.
		 */
		if ( gettype($eventsManager) == "object" ) {
			$eventsManager->fire(
				"di:afterServiceResolve",
				$this,
				[
					"name" => $name,
					"parameters" => $parameters,
					"instance" => $instance
				]
			);
		}

		return $instance;
	}

from phalcon.

Laxilef avatar Laxilef commented on May 31, 2024

Sorry.
This will not work: many Di::getDefault() in the Phalcon source code.

p.s. Only if the code is completely ugly:

		/**
		 * Pass the DI to the instance if it implements
		 * \Phalcon\Di\InjectionAwareInterface
		 */
		if ( gettype($instance) == "object" ) {
			self::setDefault($this); // important for Di::getDefault()
			if ( $instance instanceof InjectionAwareInterface /*&& !($instance->getDi() instanceof DiInterface)*/ ) {
				$reflection = new \ReflectionClass($instance);
				$property = $reflection->getProperty('container');
				$property->setAccessible(true);
				$_container = $property->getValue($instance);

				if ( !$_container ) {
					$instance->setDI($this);
				}
			}
		}

Maybe there are other methods for implementing nested Di for different Applications?

from phalcon.

sergeyklay avatar sergeyklay commented on May 31, 2024

@Laxilef Could you send a patch?

from phalcon.

Laxilef avatar Laxilef commented on May 31, 2024

@sergeyklay I think that this is impossible. The current Phalcon architecture implies a single Di container due to static method calls. For example, Phalcon\Mvc\Model::find() calls Phalcon\Mvc\Model::getPreparedQuery(), which calls Di::getDefault():

        let container = Di::getDefault();
        let manager = <ManagerInterface> container->getShared("modelsManager");

This could be solved using the setDi() static methods, which sets Di to static protected $container, but this will affect all calls to this model from all other containers. And this is wrong, as it will confuse developers and spawn ugly/redundant code.

Maybe you have ideas on how to implement this? Or ideas on how to use the MVC App and Cli App at the same time?

Now this is not possible due to the fact that the MVC App and Cli App use $di>getShared('router'), that is, the same router service name.

from phalcon.

Laxilef avatar Laxilef commented on May 31, 2024

@niden Thank you for the answer!
We will wait for the implementation of this functionality :)

from phalcon.

diplopito avatar diplopito commented on May 31, 2024

Out of curiosity: which is the benefit of using this declaration rather than the standard one, i.e.:

$di = new Di();
$app = new MvcApplication($di);
$app->handle($_GET['_url']);

Why declaring a new Di inside the Di?

from phalcon.

Laxilef avatar Laxilef commented on May 31, 2024

@diplopito Almost the same as namespace.
I wrote an extension for Di to access the parent Di. It looks something like this:

$di = new Di();
$di->setShared('goodService', function() {
	return new GoodService;
});
$di->setShared('app', function() {
	$di = new Di();
	$di->setParent($this);
	/* some code... */
	$app = new MvcApplication($di);
	/* some code... */
	$di->get('goodService')->... /* obj phalcon/cphalcon#1 */
	/* some code... */
	return $app;
});
$di->setShared('cli', function() {
	$di = new Di();
	$di->setParent($this);
	
	$di->setShared('otherGoodService', function() {
		return new GoodService;
	});
	
	/* some code... */
	$app = new CliApplication($di);
	/* some code... */
	$di->get('goodService')->... /* obj phalcon/cphalcon#1 */
	/* some code... */
	return $app;
});

$di->setShared('altCli', function() {
	$di = new Di();
	$di->setParent($this);
	
	$di->setShared('goodService', function() {
		return new GoodService;
	});
	
	/* some code... */
	$app = new CliApplication($di);
	/* some code... */
	$di->get('goodService')->... /* obj phalcon/cphalcon#2 */
	$di->get('otherGoodService')->... /* NOT FOUND, ONLY FOR cli */
	/* some code... */
	return $app;
});

$di->get('app');
$di->get('cli');
$di->get('altCli');

Advantages: app has access to cli, cli has access to app. Each service can redefine within itself any service that exists in parentDi.

And that should have worked too:
$di->get('app')->get('cli')->get('altCli')

P.S. You don’t need to do this, this is an example :)

from phalcon.

diplopito avatar diplopito commented on May 31, 2024

Thank you @Laxilef

from phalcon.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.