We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

DI cache while testing

I have some testing and it call controllers like so:

    \Phalcon\DI::reset();
    \Phalcon\DI::setDefault($this->di);

    $dispatcher = new \Phalcon\Mvc\Dispatcher();
    $dispatcher->setDI(\Phalcon\DI::getDefault());
    $dispatcher->setControllerName('Api\\Controllers\\' . $this->controller);
    $dispatcher->setActionName($action);
    $dispatcher->setParams([$debug]);

    $object = $dispatcher->dispatch();
    return call_user_func([$object, $action . 'Action']);

When i calling controller in one test multiple times i got some strange results. In my controller for this test i dump a DI service that handle a parser request:

    if ($debug) {
      var_dump($this->apiRequest->request);
      var_dump($this->di->get('apiRequest'));
      exit();
    }

In first case i got:

object(stdClass)#368 (3) {
  ["payment_id"]=>
  int(1417693101)
  ["timestamp"]=>
  int(1417693051)
  ["user_id"]=>
  int(4)
}

In second case i got:

object(stdClass)#374 (5) {
  ["request_id"]=>
  int(14224571498071)
  ["atm"]=>
  int(295)
  ["request_hash"]=>
  string(47) "14224571498071:ca3ad13574476de68c4dc2f37c481a63"
  ["request"]=>
  object(stdClass)#375 (4) {
    ["cash"]=>
    array(0) {
    }
    ["payment_id"]=>
    int(1417693101)
    ["timestamp"]=>
    int(1417693051)
    ["user_id"]=>
    int(4)
  }
  ["hash"]=>
  string(40) "ac4fbc57a94d5687f9c35c2927b78b4b6471b05e"
}

My question - is $this-><di_service_name> is not shortcut of $this->di->get(<di_service_name>) ?



2.1k
//$this->apiRequest->request == $this->di->get("apiRequest")->request;

   if ($debug) {
      var_dump($this->apiRequest->request); << 
      var_dump($this->di->get('apiRequest')); <<
      exit();
    }

bro you are clearly dumping 2 different things

By default all DI services available through $this in controller. I think that $this->request and $this->di->get('request') is similar things, and $this->request is just a shortcut in controller to call $this->di->get('request')

And yes, you can see in one case i dump higher level of object. But you can see, that it don't have a cash param.



2.1k
edited Jan '15

interesting. could you post the class thanks. also the version.

It's kinda complicated, i'll try to implement it simplier

Ok, this is a testing project:

Controllers\
- TestController.php
Helpers\
- Runner.php
index.php

Index file:

// index.php
<?php
$loader = new \Phalcon\Loader();
$loader->registerNamespaces([
  'Controllers' => __DIR__ . '/Controllers/',
  'Helpers'     => __DIR__ . '/Helpers/',
]);
$loader->register();

$di = new \Phalcon\DI\FactoryDefault();
$di->set('test_service', function() {
  return json_decode('[1, 2, 3]');
});

\Phalcon\DI::reset();
\Phalcon\DI::setDefault($di);

$firstRun = new \Helpers\Runner();
$secondRun = new \Helpers\Runner();

Helper file, that running:

// Helpers/Runner.php
<?php
namespace Helpers;

class Runner {
  private $di;
  private $numbers;

  public function __construct() {
    $this->numbers = range(1, 100);

    $this->di = \Phalcon\DI::getDefault();
    $this->di->set('test_service', function() {
      return json_decode(json_encode(array_rand($this->numbers, 5)));
    });

    \Phalcon\DI::reset();
    \Phalcon\DI::setDefault($this->di);

    $dispatcher = new \Phalcon\Mvc\Dispatcher();
    $dispatcher->setDI(\Phalcon\DI::getDefault());
    $dispatcher->setControllerName('Controllers\\Test');
    $dispatcher->setActionName('index');

    return $dispatcher->dispatch();
  }
}

And controller:

// Controllers\TestController.php
<?php
namespace Controllers;

class TestController extends \Phalcon\Mvc\Controller {
  public function indexAction() {
    echo 'Calling indexAction' . PHP_EOL;
    echo 'Service from $this: ' . json_encode($this->test_service) . PHP_EOL;
    echo 'Service from DI: ' . json_encode($this->di->get('test_service')) . PHP_EOL;
    echo str_repeat('=', 100) . PHP_EOL;
  }
}

When i run php index.php, i got following output:

Calling indexAction
Service from $this: [4,48,52,72,91]
Service from DI: [29,63,74,79,82]
====================================================================================================
Calling indexAction
Service from $this: [4,48,52,72,91]
Service from DI: [5,24,38,67,83]
====================================================================================================

As we can see, service called from $this cached, no matter how it created set or setShared

Version is 1.3.4



2.1k
edited Jan '15

can you try this https://docs.phalcon.io/en/latest/api/Phalcon_Mvc_User_Component.html

extend your helper class with that and $this->di = \Phalcon\DI::getDefault(); also inject the helper into di

Problem not in Helper class. Main problem in Controller, that caches magick method.



2.1k

OH!

try setShared



2.2k

$this->test_service is a controller property. The controller has the value after the first access to it in the controller. You say it "cached".

If you want to change it, create another controller object.

It will not work, setShared will only retrieve a same intance of service niether to instantiate new one. I'll tried it btw, and it is the same.

I think you looked bad at my code, look at that place:

    $dispatcher = new \Phalcon\Mvc\Dispatcher();
    $dispatcher->setDI(\Phalcon\DI::getDefault());
    $dispatcher->setControllerName('Controllers\\Test');
    $dispatcher->setActionName('index');

    $return = $dispatcher->dispatch();

Every call a new dispatcher instantiated.

I slightly tune-up a code, to check that it's not a same objects:

<?php
// Helpers\Runner.php
namespace Helpers;

class Runner{
  private $di;
  private $numbers;

  public function __construct() {
    $this->numbers = range(1, 100);

    $this->di = \Phalcon\DI::getDefault();
    $this->di->set('test_service', function() {
      return json_decode(json_encode(array_rand($this->numbers, 5)));
    });

    \Phalcon\DI::reset();
    \Phalcon\DI::setDefault($this->di);

    $controller = new \Controllers\TestController();
    $controller->indexAction();
  }
}

It output a following now:

Version: 1.3.4
Calling indexAction
Controller Object ID: 0000000016b4fafc000000007cfdd348
Service from $this: [14,20,30,39,75]
Service from DI: [4,8,33,39,60]
====================================================================================================
Calling indexAction
Controller Object ID: 0000000016b4fafd000000007cfdd348
Service from $this: [14,20,30,39,75]
Service from DI: [1,5,12,88,91]
====================================================================================================

It's a different objects 0000000016b4fafc000000007cfdd348 -> 0000000016b4fafd000000007cfdd348. I make a hash using spl_object_hash.



2.2k

I'm not sure, but you probably use signle DI container.

And two dispatchers use the shared controller from the same container.

If you see above i removed dispatcher at all, to check if this bug take place in dispatcher.



2.2k

Oh! It is not the same. You are right.

Version: 1.3.4 Calling indexAction Controller Object ID: 0000000016b4fafc000000007cfdd348 Service from $this: [14,20,30,39,75] Service from DI: [4,8,33,39,60]

Calling indexAction Controller Object ID: 0000000016b4fafd000000007cfdd348 Service from $this: [14,20,30,39,75] Service from DI: [1,5,12,88,91]



It's a different objects 0000000016b4fa**fc**000000007cfdd348 -> 0000000016b4fa**fd**000000007cfdd348.
I make a hash using `spl_object_hash`.

I simplified code, it currently can be reproduced from this code:

<?php

class TestController extends \Phalcon\Mvc\Controller {
  public function indexAction() {
    echo 'Calling indexAction' . PHP_EOL;

    echo 'Controller Object ID: ' . spl_object_hash($this) . PHP_EOL;
    echo 'Service from $this: ' . json_encode($this->test_service) . PHP_EOL;
    echo 'Service from DI: ' . json_encode($this->di->get('test_service')) . PHP_EOL;
    echo str_repeat('=', 100) . PHP_EOL;
  }
}

$di = new \Phalcon\DI\FactoryDefault();
$di->set('test_service', function() {
  return json_decode(json_encode(array_rand(range(1, 100), 5)));
});

\Phalcon\DI::reset();
\Phalcon\DI::setDefault($di);

echo 'Version: ' . Phalcon\Version::get() . PHP_EOL;
echo PHP_EOL;

$controller = new TestController();
$controller->indexAction();

$controller = new TestController();
$controller->indexAction();

It outputs:

Version: 1.3.4

Calling indexAction
Controller Object ID: 000000002d6dccb4000000006874776e
Service from $this: [18,55,69,76,77]
Service from DI: [23,39,83,84,88]
====================================================================================================
Calling indexAction
Controller Object ID: 000000002d6dccb7000000006874776e
Service from $this: [18,55,69,76,77]
Service from DI: [10,43,56,70,78]
====================================================================================================


2.2k

I reproduced it.

Version: 1.3.4

Calling indexAction
Controller Object ID: 000000000aaff29d00007fcd4294a598
Service from $this: [15,31,41,84,88]
Service from DI: [4,5,15,75,88]
====================================================================================================
Calling indexAction
Controller Object ID: 000000000aaff29e00007fcd4294a598
Service from $this: [15,31,41,84,88]
Service from DI: [11,26,73,86,87]
====================================================================================================

I think we should ask @phalcon, if it's a normal or a bug.



2.2k

It is strange, not normal, I think.



2.1k
edited Jan '15

i believe i know why, calm your horses. because you are making rand calls.

basically when you are calling the test service, the result is not cached. you are making rand calls everytime, array_rand is randomizing the numbers everytime you call it. to make it constant it has to be static.

class test_service
{
    public $rand;

    public function __construct()
    {
        $rand = array_rand(range(1, 100), 5);
    }
}

$ts1 = new test_service();
var_dump($ts1->rand);
$ts2 = new test_service(); 
var_dump($ts2->rand);
//different result
class test_service
{
        public static $rand;

        public function __construct()
        {
            if(!self::$rand)  // << might be typo here nv tried you get the idea
                self::initrand();
        }

        private static function initrand()
        {
            self::$rand = array_rand(range(1, 100), 5);
        }
}

$ts1 = new test_service(); 
var_dump($ts1->rand); // << might be typo here nv tried you get the idea
$ts2 = new test_service(); 
var_dump($ts2->rand);
//same result

@7thcubic you don't get it? It's not a problem in array_rand or any other function in PHP, it's a problem while you trying to access DI in controller through magick method using $this->test_service in this case in different instances (with different DiC) result will be the same, it's looking like something caches it.



2.1k

not at all. what i see from the samples you gave me is exactly what i said in the earlier post. and if u say it caches it you are right in a way. the service is known to the di, but it does nothing until someone/something calls for that exact service, after which it it placed in an array of initiated objects.

like the postman, he knows where everybody lives, but he doesnt place anyone on his route list unless he has a parcel, and for the very day the recipient will not change until the next day. so yeah it caches some sort if that's what you are trying to imply.

but based on the sample codes you gave, i am unable to understand as i clearly explained to you what happened.

so are you trying to say it should cache? or should not? = ="