cphalcon: [BUG]: cannot register "injectable" services in DI - infinite loop

Questions? Forum: https://phalcon.link/forum or Discord: https://phalcon.link/discord

Describe the bug I create a new custom validation, which i wish to register as a shared service in the DI. This results in immediate exception

To Reproduce

Provide output if related. Provide coredump if any. Use https://docs.phalcon.io/en/latest/generating-backtrace as reference.

Fatal error: Uncaught Error: Maximum function nesting level of '256' reached, aborting! in test.php on line 12

Error: Maximum function nesting level of '256' reached, aborting! in test.php on line 12

Steps to reproduce the behavior:

$di = new Phalcon\Di();

class MyService extends \Phalcon\Di\Injectable
{
}

class CustomValidation extends Phalcon\Validation
{
}

$di->setShared(MyService::class, MyService::class);
$di->setShared(CustomValidation::class, CustomValidation::class);

//$service = $di->getShared(MyService::class);
$service = $di->getShared(CustomValidation::class);

Expected behavior A clear and concise description of what you expected to happen.

I would expect to be able to call this service using getShared method

$service = $di->getShared(CustomValidation::class);

Screenshots If applicable, add screenshots to help explain your problem.

Details

  • Phalcon version: (php --ri phalcon) 4.0.4
  • PHP Version: (php -v) PHP 7.4.2
  • Operating System: windows 10
  • Installation type: download extension from github releases
  • Zephir version (if any): none
  • Server: apache, plain php execution (directly)
  • Other related info (Database, table schema):

Additional context

See attached file test.txt

Additional Info: this error was identified when migrating a project from phalcon 3.4.x (it was previously working in that version)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 15 (8 by maintainers)

Most upvoted comments

Resolved in https://github.com/phalcon/cphalcon/pull/16242

Thank you @cfv1000 for reporting it and all the help reproducing

The Phalcon\Di is basically a services container. All the services should be registered there (as stateless or statefull / shared or not). “Why are you trying to register something as itself?” => the alternative would be to register it as something else. Something that is not self. “you can register it as anything, but not as itself. It MUST be different”. By basic logic, this does not make sense.

If you do not register a service in the container, then the container will try to resolve it automatically - service locator antipattern here. https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/

It will still resolve it, but the definition of the service will never be in the container until it is resolved automatically. I would expect to have access to all service definitions, even before a service is resolved.

As for your SOLID / interfaces and advocating for it, I would agree with the mention: you don’t write code for the principles and the standards. You write code for projects, and you make use of the principles to help you in that endeavor. Use interfaces and anything else that helps you do your job, but don’t abuse them just for the sake of it. Instead of having a project where you have to maintain 100+ classes, you have a project where you MUST maintain 100+ classes and 100+ interfaces.

Don’t forget that at the moment, the problem reported is about having the same feature in 2 separate locations. This is a duplicate. Principle#1: DRY Principle#2: KISS

@Fenikkusu - i believe you missunderstand. First of all, both Di::get and Service::resolve attempt to perform the same task - resolve the service based on a definition. Simply having the same functionality in 2 places it’s a bad PHP design. Split the responsabilities

Di is simply a service container - it will keep a bucket of services (some initialized, some not). When you try to get a service from it, you need to check if the service is shared and initialized. If it isn’t, initialize the service. Either here or in the service itself.

Your suggestion is not ok, because I don’t want to have aliases. I want to simply register a service in the container.
I also don’t want to use anonymous functions if i don’t need to (shorter syntax).
I also don’t want to declare interfaces for every class i write (overhead).