Tag Archives: Zf2

Handling ZF2 FQ Dependency Setter Injections

In a previous article I showed how you could pass in a fully qualified parameter name into the Dependency Injection Container (DiC) if you needed to be specific about where you need to have something injected.  There is an alternate method here that is cleaner than what I did before.  Let’s start with a Test class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Test
{
  protected $test;
 
  public function setTest($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}

The way I did it in the previous example was by defining the fully qualified parameter name by doing this.

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'Test::setTest:0' => 'some data';
      )
    )
  )
);

Which is fine until you you have

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'FriggingLongNamespacedClass' => array( // Class name
      'parameters' => array(
        'FriggingLongNamespacedClass::setFriggingLongNamespacedClassMethod:0' => 'some data';
      )
    )
  )
);

It can get a little convoluted, plus I’m not entirely convinced that’s the best way of doing it.  Parameters seem to be more useful for doing constructor injection.  A better method for setters seems to be through the use of injectors.  Let’s take at an injector configuration.

1
2
3
4
5
6
7
8
9
10
11
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'injections' => array(
        'setTest' => array(
          array('test' => 'somedata')
        )
      )
    )
  )
);

Basically, rather an using the “parameter” key use “injections” and specify an array of injection methods.  Each of those methods needs an associative array of parameter names as the key with the values as the value.  Injectors seem to be much nicer and cleaner than my previous way of doing the fully qualified parameter.

ZF2 Dependency Injection – Multiple Object Instances

When you work with the ZF2 Dependency Injection Container (DiC) when you make multiple requests for an instance of an object you will get the same object back each time.

For example, with this code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Test
{
  protected $test;
 
  public function __construct($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}
 
use Zend\Di\Di;
$di = new Di();
 
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
$test2 = $di->get('Test', array('test' => 'some data'));
var_dump($test2);

You will get the following output

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}

If you provide a different parameter you will get a different object.

1
2
3
4
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
$test2 = $di->get('Test', array('test' => 'some other data'));
var_dump($test2);

Produces

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#11 (1) {
["test":protected]=>
string(15) "some other data"
}

From this we can conclude that when you are using the DiC that if you request a like object with like parameters you will get the same instance of the object.

But what if you want the injection benefits of the DiC but don’t want to share the object?  Use the DiC’s newInstance method instead with the third parameter being false.  This tells the DiC container to refrain from putting it in the shared object pool.  The get() method does not give you the option of retrieving a non-shared instance and so you will need to call the newInstance() method directly.

1
2
3
4
5
$test = $di->get('Test', array('test' => 'some data'));
var_dump($test);
 
$test2 = $di->newInstance('Test', array('test' => 'some data'), false);
var_dump($test2);

prints out

1
2
3
4
5
6
7
8
object(Test)#9 (1) {
["test":protected]=>
string(9) "some data"
}
object(Test)#11 (1) {
["test":protected]=>
string(9) "some data"
}

 

ZF2 Dependency Injection: Managing Configuration – Part 2

In my previous post about Dependency Injection Container (DiC) configuration I made mention of some more complicated scenarios that you can use to configure your objects.  Not that you need help to make it  more complicated.  One of the things I have observed is that the more familiar I am with the DiC the more complicated the configuration gets.  This is both good and bad.  It’s good because you start understanding how and why all of those frigging long example DI configuration arrays are like that.  The bad is that  long DiC configuration arrays greatly reduce the ability of a new developer to start being productive.  That’s why I think this series of blog posts are good.  They show you how to get started small which is really the only way to get started if you aren’t intimately familiar with a DiC.

Let’s take our Test class and change its behavior by adding a method that allows you to set some test data (use your imagination here before complaining in the comments that it’s not a real world example).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Test
  {
  protected $test;
  protected $data;
 
  public function setTestData($test)
  {
    $this->data = $test;
  }
 
  public function getTestData()
  {
    return $this->data;
  }
 
  public function setTest($test)
  {
    $this->test = $test;
  }
 
  public function getTest()
  {
    return $this->test;
  }
}

We dutifully set up our DiC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'test' => 'some data'
      )
    )
  )
);
 
use Zend\Di\Configuration;
 
$diConfig = new Configuration($config);
$di = new Di();
$diConfig->configure($di);

And we run our test code getting our ‘some data’ parameter, right?

1
string(9) "some data"

Yep.  Hmm, but what about getTestData()?

1
var_dump($test->getTestData());

echos

1
string(9) "some data"

Hmm, that shouldn’t be happening.  We did’t tell the DiC that we wanted to inject data there, did we?  It looks like it’s injecting data into both setter methods.  But why?

Look at the names of the parameters for both setters.  They are both named “test”.  The DiC will find the first method with a matching parameter name and inject the value there (if it couldn’t find it in the constructor) .  You have a couple of options to fix this.  If it’s your own code and you don’t mind refactoring, refactor it.  However, if it’s third party code that you shouldn’t touch or code that other parts of the system are dependent on, you have a problem.

But to get around the problem you can specify the setter parameter as a fully qualified (FQ) parameter (I show another , probably better, method here).  The format is “class::method:paramPos”.  In this example it would be “Test::setTest:0″.  So let’s change our DI configuration.

1
2
3
4
5
6
7
8
9
$config = array(
  'instance' => array(
    'Test' => array( // Class name
      'parameters' => array(
        'Test::setTest:0' => 'some data'
      )
    )
  )
);

And now check the return values of both getters.

1
2
3
$test = $di->get('Test');
var_dump($test->getTest());
var_dump($test->getTestData());

Now we get

1
2
string(9) "some data"
NULL

Works.

A little more advanced ZF2 EventManager usage

<note>I found out that this usage is going to be deprecated.  This feature will remain, but for the GA (and beta 4 most likely) this code may not work.  I will be updating this blog post when that happens</note>

If you look at my previous post on the ZF2 EventManager class you might be tempted to think that you are limited only to listening to events for local instances of the event manager.  But no, my friend, you would be wrong.

The event manager actually provides access to a sort of global event manager.  But it’s more than that.  This global event manager allows you to listen in on events for various targets.

So let’s say that you have an event manager for a given class in a given namespace.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace ns1 {
 
  use Zend\EventManager\EventManager;
 
  class cls1 {
 
    protected $events;
 
    public function __construct()
    {
      $this->events = new EventManager(__CLASS__);
    }
 
    public function execute()
    {
      $this->events->trigger('meesa.triggered', $this);
    }
  }
}

Take a look at the constructor.  What you are doing there is giving the event manager an identifier.  Identifiers are not needed, but if you want to allow other event manager instances to attach to its events an identifier will make it globally known.

Next up we will create a class that attaches to that event manager by calling the StaticEventManager class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace ns2 {
 
  use Zend\EventManager\Event,
  Zend\EventManager\StaticEventManager;
 
  class cls2 {
 
    public function listen(Event $e)
    {
      echo get_class($this) . ' has been called by ' . get_class($e->getTarget());
    }
  }
 
  StaticEventManager::getInstance()->attach('ns1\cls1', 'meesa.triggered', array(new cls2, 'listen'));
}

We’re not calling the class ns1\cls1 in this case, just attaching to its event.  When setting this up we attach to the identifier that we provided in the first class (often a class name), followed by the event name and them providing the  class and method name via a callback.  When the event is triggered that callback will be called and we will we will  print out the target class.

1
2
3
4
use ns1\cls1;
 
$cls = new cls1();
$cls->execute();

When we execute that code we get the following output.

ns2\cls2 has been called by ns1\cls1

So even though ns2\cls2 and ns1\cls1 had virtually no knowledge of each other (we did because we “set them up to succeed”), we were able to call code from across the codebase with great ease.

Zend Framework 2 Event Manager

Yes, I know I work for Zend and that means that I should automatically be familiar with everything the company does.  Especially when it comes to Zend Framework 2.  But I have to confess that while I’m most definitely watching it, I have not been able to work with it in any meaningful sense.

Until today.  I got to play with the Event Manager.  I did like the plugin functionality in ZF1, but it required some pretty static coding.  In some cases, like the front controller plugins, it makes more sense (though this way seems more desirable).  Of course there are frameworks out there that are more event based as a whole, but what if you’re more familiar with Zend Framework?  I am, and so it makes sense that I would use the event manager in ZF2.  It’s a ZF1 application, but since (it seems) the event manager is self-contained (and the autoloader works with both ZF1 and ZF2) you can simply paste it into your include_path and BOOM, you have an event manager.

So, I had a model (the software kind) layer where all of the models were based off of an abstract class.  The abstract class had a save() method that I wanted to provide various hooks for pre and post save and, more importantly, pre and post validation.  Why validation?  Because I want to stored a hashed password in the database, but I want to validate on the plaintext.  I could have created a bunch of hooks, but that really is annoying.  I could have also overridden the save() method, but that meant adding a fair amount of logic to that method and I’m becoming less and less a fan of overriding logic because it’s insufficient at higher levels of abstraction.  So, instead, I implemented the ZF2 event manager in a ZF1 application.

The model looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
abstract class ModelAbstract
{
 
	const EVENT_PRESAVE = 'modelabstract-pre-save';
	const EVENT_PREVALIDATE = 'modelabstract-pre-validate';
	const EVENT_POSTVALIDATE = 'modelabstract-post-validate';
	const EVENT_POSTSAVE = 'modelabstract-post-save';
 
	/**
	 * @var EventManager
	 */
	protected $eventManager;
 
	public final function __construct()
	{
		$this->eventManager = new EventManager();
		$this->init();
	}
 
	public function init() {}
 
	public function save()
	{
		if ($this->tableName === null) {
			throw new ModelException('Improperly defined model');
		}
		$this->eventManager->trigger(self::EVENT_PREVALIDATE, $this);
		if (!$this->getForm()->isValid($this->data)) {
			return false;
		}
		$this->eventManager->trigger(self::EVENT_POSTVALIDATE, $this);
		$db = \Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('db');
		$this->eventManager->trigger(self::EVENT_PRESAVE, $this);
 
// save the data (redacted due to boredom)
 
		$this->eventManager->trigger(self::EVENT_POSTSAVE, $this);
		return $ret;
	}
}

Then in my Account model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Application_Model_Account extends ModelAbstract
{
	protected $tmpPassword;
 
	public function init()
	{
		// Encrypt the password after validation if the password has changed
		$this->eventManager->attach(
			self::EVENT_POSTVALIDATE,
			function($event) {
				if ($event->getTarget()->passwordChanged()) {
					$event->getTarget()->encryptPassword();
				}
			}
		);
	}
 
	public function passwordChanged() // really annoying. Thanks PHP 5.3
	{
		return $this->tmpPassword == null;
	}
 
	public function encryptPassword()
	{
		if ($this->passwordChanged()) {
			$util = new Util();
			$this->password = $util->encryptPassword($password);
			$this->tmpPassword = null;
		}
	}
}

If the password is changed it sets both $this->password and $this->tmpPassword.  The hashing only occurs if the password has been set and there’s a value in tmpPassword.  (I guess I could have just used a boolean somewhere too).  So now, every time I save the account model the post validate event will be fired and the password will be encrypted.  (The comment on PHP 5.3 is because you cannot use $this in a closure in PHP 5.3.  That functionality will be there in 5.4.)

I believe that the method I used here allows for events to be fired for specific object instances, because each model has its own event manager.  But the event manager will also check for global events as well.  So, technically, the code that I have that does validation could be registered as a global observer to make sure that all models are properly validated prior to the save as well as having instance-specific (such as password changes) events attached to the same event name.

Pretty cool.

… waiting for Matthew to email me with corrections… :-)

[UPDATE]

For those who have complained that I didn’t use some additional kind of methodology or best practice know that the other five billion lines of code that had nothing whatsoever to do with the event manager has been removed… or read my response down below on “Methodology Jackassery”.