Laravel Service Provider - Get a class instance from an intermediate method in a method chain

Viewed 456

I was wondering if you could help me with the following question. First of all, I would like to tell you that if I am asking this question here, it is because I have already tried many options and none have worked for me. It turns out that I am developing a package with Laravel and I am using Laravel's dependency injection. But I am at a crossroads from which I have not found a way out. I'm trying to get the instance of a class in an intermediate method from a method chain, let me explain. Here is the code very similar to what I have:

PackageServiceProvider.php

<?php

class PackageServiceProvider extends ServiceProvider
{   
    public function register()
    {
        $this->configureBindings();
    }

    private function configureBindings()
    {
        $this->app->when(A_Class::class)->needs(B_Interface::class)->give(function () {
            return new B_Class();
        });
    }
    ...

A_Class.php

<?php

class A_Class implements A_Interface
{
    private $b_interface;

    public function __construct(B_Interface $b_interface)
    {
        $this->b_interface = $b_interface;
    }

    public function create($arg1, $arg2)
    {
        return $this->b_interface->method_1()->call_another_method_from_another_class();
    }
}

A_Interface.php

<?php

interface A_Interface extends Arrayable, Rendereable
{
        public function create($arg1, $arg2);

        ...
}
<?php

class B_Class implements B_Interface
{
    public function __construct()
    {
        // Here is the question...
        // How could I get here the instance of the class A_Class?
    }

    public function method_1()
    {
        // should return another class instance
    }

    public function method_2()
    {
        // should return another class instance
    }
}

B_Interface.php

<?php

interface B_Interface
{
        public function method_1();

        public function method_2();

        ...
}

If you look at class B_Class``, in the __constructmethod I'm trying to get the instance of classA_Class``` from where that class is being called. I have tried the following:

class B_Class implements B_Interface
{
    public function __construct(A_Interface $a_interface)
    {
        // Here is the question...
        // How could I get here the instance of the class A_Class?
    }

But I get the following error:

Segmentation fault

I guess there must be some way I can achieve what I need. I would appreciate any help in advance.

1 Answers

Because you are referring to class A inside your class B constructor, and class B in your class A constructor, you have introduced a cyclic dependency.

This will resolve to the error you are experiencing, which is the segmentation fault, as outlined here: https://laravel.io/forum/11-08-2016-circular-dependency-causes-segmentation-fault-error-when-running-php-artisan-optimize

So the answer is to remove the cyclic dependency if possible, as you can have methods from A calling B that calls A for infinity at runtime, and you will get the above error above again.

If your class A and B are relatively small, I would recommend combining them before using a cyclic dependency.

For interest and prosperity, if you want achieve a cyclic dependency, this is possible by registering your Class A with a singleton from inside A's constructor, and putting the reference to the incomplete object into Class B with your code above. I try with laravels singleton here, its untested, but hopefully you'll get the idea.

class A_Class implements A_Interface
{
   public function __construct(B_Interface $b_interface)
   {
      //I dont think you can pass $this to a function when construction is incomplete, hence $that.
      $that = $this;
      App::singleton('A_Class', function($that){
          return $that;
      });
      $this->b_interface = $b_interface;
   }
}

class B_Class implements B_Interface
{
    public function __construct(A_Interface $a_interface)
    {
       //unfinished object, but no more error.
       $this->aClass = App::make('A_Class')
    }
}
Related