Configuration in CakePHP 3.0

Early work has started for CakePHP 3.0, and I’ve started re-visiting how CakePHP handles configuration and bootstrapping. I want to focus on configuration for this post, as bootstrapping, while related is worthy of its own post. The goal of this post is to provide some context on the planned changes, and to get feedback on those changes. My hope is that by getting feedback early on we can avoid problems & surprises later on.

I decided to start with configuration. Every application needs at some configuration even in a conventions over configuration framework like CakePHP. I feel that configuration can be improved from where it stands today, as there are a few problems.

Problems with configuration

When I think of configuation in CakePHP, I think of the Configure class. From talking with others, they feel the same. Configure has been around as long as CakePHP, and is the primary way most developers do configuration for their application/plugins. However, there are two other ways that configuration is currently managed outside of Configure:

  • Classes like DATABASE_CONFIG and EmailConfig. These classes are my least favorite method. They are clunky and don’t really offer much benefit over Configure in terms of flexibility.
  • Configuration methods like Cache::config() and CakeLog::config(). While these methods appear to offer more traditional dependency injection, they don’t. All engine class references are passed as strings, and you can never inject an instance anywhere.

While having 3 ways to do configuration may not seem like a problem for the experienced developer. But having 3 ways to do basically the same thing creates a steeper learning curve for developers who are new to CakePHP. Ideally, there would be only one way configuration is handled. Having systems like DATABASE_CONFIG greatly increases the difficulty in creating configuration files in formats other than PHP.

Proposed solution

Originally, I was very much in favour of removing the configuration classes and adding more config() methods to the remaining classes needing them. However, Jose and Juan argued that using Configure was a better solution because we’d still need to maintain Configure. We’d still have 2 ways of doing configuration by adopting config() methods, while the goal was 1. While I don’t like that many clasess in CakePHP will be coupled to Configure. I want to avoid adding additional layers just to provide what I think will be a marginal amount of decoupling. While an additional layer could be more ‘proper’ it would also increase difficulty for beginners. Despite the downside of coupling there are some great benefits to putting all configuration in Configure:

  • All core, app, and plugin classes only need one way to create, read and manage configuration.
  • We can leverage the existing Config adapters to enable support for any format/source.
  • Familliarity and simplicity. Existing developers are familliar with Configure, and its a very basic API so its easy for new developers to pick up.

While Configure would be the primary way to handle configuration in a 3.0 application, we’re planning on adding more generic setter injection where it makes sense. For example, you’ll be able to directly add cache engines to Cache:

Show Plain Text
  1.  
  2. <?php
  3. use Vendor\Caching\CustomEngine;
  4.  
  5. $engine = new CustomEngine($dependency);
  6. Cache::engine('custom', $engine);
  7.  

This feature allows you to easily accomodate any type of caching that can’t or doesn’t make sense to store as configuration. In these situations, you’ll be able to use engine() on Cache, and Log to set a specific instance. Other adapter based classes will have similar features.

I’m interested to hear any feedback on these plans. I’m still in the early stages of implementing them and would love to hear people’s thoughts before finishing. If you’re interested in seeing the in progress changes you can find them on my github fork .

Comments

Very good idea. A single way of configuring makes perfect sense and using Configure is easy.

Lucian Sabo on 6/9/12

I like the Cache example that you provided. I think that implementing this type of configuration is definitely a good idea. However, I’m still not quite clear on the issues you brought up earlier in the article; namely the DATABASE_CONFIG class and its changes.

In your example, would you do something like:

$dbconf = new CustomConf();
//set $dbconf params
Configure::config($dbconf);

Or is your dual solution there to keep Configure for low level and to implement the config() methods on higher level classes?

Scott Harwell on 6/9/12

I’m pretty torn about this lol.

I do like the idea of Configure being injected instead of used as Configure::dosomething() (inside of core classes at least). The code would become a bit more verbose I suppose $this->config->dosomething(). I’m leaning towards a common interface (or maybe even a mixin) for configuration for classes that need dynamic or one time use configuration changes like Cache. Can a mixin implement an interface? This interface could also make it clear how the classes should interact with Configure.

Other psuedo configuration things in use: _set(), set(), _mergeVars(). These could be wrapped into the configuration interface i guess?

As for the email and db config classes, they should definitely go away and be standardized to be consistent with the rest of the framework.

So I guess I’m leaning towards the complicated option lol. – configure is injected and a singleton – classes implement a common configuration interface if needed – Object implements Configuration would cover a lot of the framework (maybe that’s overkill) – Configuration interface should allow for one time use configuration changes – Users can supply their own Configure object if they choose to, just implement the interface

I think if the configure class and the interface are introduced to the user on the same documentation page, there is a good chance users will “get it” without too much trouble.

For anyone interested here is an object inheritance graph (2.2.3) http://sitedyno.com/cake-inheritance.svg

Sorry if this doesn’t really help xD

Heath on 6/9/12

Ahh my list doesn’t look very list like:

- configure is injected and a singleton

- classes implement a common configuration interface if needed

- Object implements Configuration would cover a lot of the framework (maybe that’s overkill)

- Configuration interface should allow for one time use configuration changes

- Users can supply their own Configure object if they choose to, just implement the interface

Heath on 6/9/12

Scott Harwell: The issue with them currently is that the class reference is hard coded in the consumer, or in the case of CakeEmail there is a property that references which class contains the configuration. This class is then constructed and public properties are read from it. This is problematic as it is entirely unlike any other configuration approach in CakePHP, or most other frameworks. Another problem with these classes is defining the configuration data they hold in a non executable form (like an ini file) requires annoying workarounds, which really shouldn’t be needed.

Heath: I thought of using $this->config->get() and one of the problems it creates is requiring to always construct classes that need access to configuration with the configuration object. For example, everytime you wanted to create an email using predefined configuration you’d need to type out $email = new Cake\Network\Email(Configure::instance());. I don’t really understand how a configuration interface solves the problem. Perhaps you could show an example?

mark story on 6/9/12

Not sure on the inner workings, however even as far as Cache, Email, and Log goes compared to DATABASE_CONFIG, to me configuring should be very similar (as in they are all connection configurations to another service or data source). I definitely agree with you this is an are that could be improved.

Which ever route you decide cleaning them up to be more consistent between them all will help. How about a Connections class?
Connections::db()
Connections::cache()
Connections::email()
Connections::log()

Maybe even making this more flexible for custom connection types on the fly:
Connections::sms() // text message SMS server

Trent on 8/9/12

i would be happy to see some environment detection class in Cake core to easily switch between configurations

grzes on 11/9/12

I think this looks good! Like grzes I’d love to see some environment detection and configuration inheritance between environments out of the box.

Rich on 12/9/12

grzes, Rich: I strongly feel that environment management does not belong at all in the application. Configuration should decide the application environment/behavior, not the other way around. In addition to this, there are a number of excellent tools designed entirely around managing and merging configuration. These tools will always do a better job of this than some class in a web framework.

With regards to merging/inheriting configuration, this is very easy to do by separating the base configuration from the environment specific configuration. Then you can load the files in the correct order, and get the desired results.

mark story on 15/9/12

Hi Mark

What tools do you use to manage and merge configuration for CakePHP applications?

For many of my applications, I’ve been overriding the constructor of DATABASE_CONFIG to detect the environment I’m in, and decide which array will be used at the $default, whether it be for development, UAT, staging or production. It’s not very nice to have database passwords for all these environments in the once spot, but it means I don’t have to fiddle with configuration files during a manual deploy.

If I’m lucky, I get to deploy to a Linux environment, and then get to use Capistrano via SSH and a copy strategy, from our Git repository. For these instances, I can have a config structure outside of the application, and copy the relevant environment files in place that will set the database config, plus any environment specific bootstraps.

If I’m not lucky, its a Windows target, and Capistrano is no longer an option. Having to manually copy environment files into the deploy package is really unattractive.

Can I be cheeky and ask what packaging and deployment tools you use for various target platforms?

Reuben Helms on 16/9/12

Reuben Helms: Sure I generally use capistrano or fabric to do deployments and the environment specific config files are outside of the applications version control. In each deployment environment I make the necessary configuration files and symlink them into each deployment.

I don’t ever deploy to windows servers so I don’t have to deal with them. However, traditional tools like SSH + fabric and capistrano are probably not going to work well there but you could stick with PHP/python scripts.

mark story on 27/9/12

Hi Mark, firstly i have good idea to change the configure mechanizm in CakePHP, because have 3 different options is not good idea for new developers in Cake. I think that cleared vision of configuration is have 1 configure file, where I can configure my default and development connection to database, configure send email mechanizm,and other thinks (from core.php) and i can create section for my custom config variable(example size of thumbnails, number of news on main page etc.) Idea of “I’d love to see some environment detection and configuration inheritance between environments out of the box” from Grzes is awsome, because everybody usually develop on local machine and when the finish application they copy files do server. For new less expirence developers is very nice and good option (remeber framework should help me in develop time).

R4D3K on 1/10/12

I am create svg file in cakephp

Plz help me.

Som Suneh on 5/10/12

R4D3K: The main reason I don’t like environment management in a web framework, is it adds meta-configuration, or configuration for the configuration. Which creates a layer of complexity that is really unnecessary. It also encourages a bad practice of putting all the credentials for all services in all environments in the source code. This means that anyone with your code also has access to all your production databases etc.

If you think about configuration it generally is broken down into 2 types of configuration. Environment specific (database, memcache etc) and generic configuration (plugins, paths, thumbnail sizing). If you have 2 configuration files all the pain and meta configuration goes away. You keep the generic/application configuration in the repository, so its easy to update on deploy. While keeping the environment specific configuration in separate file on each server.

mark story on 15/10/12

Mark, I know that routes are not directly related to this topic, but I would ask for it to be considered in a more easy to use database to create routes.

Thiago Alves Goulart on 20/10/12

Thiago Alves Goulart: What’s making it hard now? I don’t really have any plans to make this specifically easier in 3.0, as I don’t think its overly hard now. I could be very wrong about that though.

mark story on 30/10/12

when cakePHP 3.0 beta will release?
looking forward to using that!

classified ads on 20/2/13

Have your say:

*
* You can use Textile markup, but be reasonable