Configuration in CakePHP 3.0 redux

In a previous post, I outlined some changes that would be coming for configuration in CakePHP 3.0. I’ve recently been thinking a great deal about configuration as well as building some prototype applications. I’ve come to the conclusion that configuration needed to change. Coupling all the classes to Configure while convenient and simple, had a big drawback. It was based on ‘spooky magic at a distance’. Setting data in Configure would populate data in other static classes, most of the time. This can be illustrated with the following:

Show Plain Text
  1. Configure::write('Cache.default', $config);
  2. Cache::write('my_key', $data);

In the above the data inserted into Configure would be magically sucked in by Cache. You didn’t have to do anything, which was nice, but it also used evil magic. As part of CakePHP 3.0 I really wanted to remove the ‘evil magic’ that confused and confounded developers. This magic had other negative effects. Take the following:

Show Plain Text
  1. $config = [
  2.     'engine' => 'File',
  3.     'duration' => '+1 day',
  4. ];
  5. Configure::write('Cache.default', $config);
  6. Cache::write('my_key', $data);
  7.  
  8. $config2 = [
  9.     'engine' => 'File',
  10.     'duration' => '+10 days',
  11. ]
  12. Configure::write('Cache.default', $config2);
  13. Cache::write('new_key', $data);

You would probably expect new_key to be persisted for 10 days, but it wouldn’t. Configuration would only be sucked into Cache on the first use. This is ‘evil magic’ in a nutshell to me.

The new approach

With the evil magic being flushed out, I reverted a pile of changes I did previously and restored config() methods on a number of classes. This has the drawback of decentralizing configuration to some extent. However, it removes magic and makes the code transparent and simple to understand. The new system enabled configuration to be moved into a single configuration file that is loaded during the bootstrap phase. After being read, configuration is injected into each of the static classes. This makes storing configuration in json or ini files simple. An example of the new system is:

Show Plain Text
  1. Cache::config('default', [
  2.     'className' => 'Memcached',
  3.     'duration' => '+1 day'
  4. ]);
  5.  
  6. // Reconfiguring an existing adapter causes an exception.
  7. // It is almost always a mistake when you reconfigure an existing adapter.
  8. Cache::config('default', $settings);

The above interface which was extracted into a trait that is used across the framework. Cache, Log, ConnectionManager, and Email all use the same code. Another benefit to the newer approach is all the details of how it works live in the application bootstrap, not the framework. This makes configuration transparent for the users of CakePHP. Changing how configuration is loaded and handled is much simpler now, as there is no hidden magic.

Takeaways

I took a few good lessons away from this experience:

  • Sometimes consistency is worth giving up to keep code simple and transparent. While keeping everything in configure would have been consistent and simple in some ways. It had serious drawbacks that made usage confusing.
  • Evil magic can start off with the best of intentions. We really wanted to do a good thing with the first implementation of configuration in 3.0. Only later did I realize it was evil magic.
  • Simplicity is hard. It took a lot of work to make the configuration even more simple than it was in 2.x, but I think the effort was worth it.

Comments

Thank you for sharing your thought process :-)

Kim Biesbjerg on 9/9/13

Good!Thanks!

Macnie on 1/1/14

Cakephp 3.0 DEV 为什么没有默认的APP目录呢?

Why is not the default APP catalog?

macnie on 3/12/14

Comments are not open at this time.