Matthew Daly's Blog

I'm a web developer in Norfolk. This is my blog...

9th February 2020 10:10 am

Don't Use Stdclass

The digital agency I work for specialises in marketing, so some of my work tends to relate to mailing lists. This week I was asked to look at an issue on a Laravel-based export system built by a co-worker who left a few months ago. This particular application pulled data from the Campaign Monitor API about campaigns, and it returned the data as instances of stdClass, something that I’m not terribly happy about.

Now, this was an implementation detail of the Campaign Monitor PHP SDK, which is old and creaky (to say the least…) and doesn’t exactly abide by modern best practices. However, the legacy application I maintain also does this (there’s a lot of stuff that needs sorting out on it, and sadly replacing the stdClass instances is a long way down the list), so I thought it was an interesting subject for a blog post. I consider using stdClass, even just as a dumb container for data, to be an antipattern, and I thought it would be useful to explain my thoughts on this.

Why shouldn’t I use stdClass?

Readability

One of the first things I learned about throwing exceptions is that they should be meaningful. It’s trivial to define a named exception and use that to specify the type of exception, and you can then capture exceptions to handle them differently elsewhere in the application. For instance, a validation error is entirely due to a user submitting the wrong details, and should therefore be handled in an entirely different manner to the database going down.

The same is applicable to an object. If an API client returns an instance of stdClass, that doesn’t tell me anything about what that object is. If I need to pass it elsewhere in a large application, it may become very difficult to understand what it represents without tracking it back to where it came from, which will slow me down. If instead I use a named class, the name can convey what it represents. It may seem like overkill to create a class that adds no new functionality, but the mere fact that it has a name makes your code more meaningful and easier to understand. I can also add DocBlock comments to describe it further.

Of course, just giving something a generic name like Container isn’t much of an improvement, and coming up with meaningful names for classes and methods is notoriously hard. As always, give some serious thoughts into what your objects represent and attempt to give them names that will make it easy to understand what they are if you look at the code base again six months down the line.

Type hinting

A related argument is that it makes type hinting more useful. You can type hint stdClass, but as noted above it tells someone working on the code receiving it very little about where it’s come from, or what it represents, and it doesn’t offer much value since an stdClass could mean anything, and could be created anywhere in the application.

By contrast, named classes provides much more information about what the type hinted parameter represents. For instance, naming your class something such as App\Api\Response\Item, makes it much clearer that that object represents an individual item returned from an API, and others developers working on the same code base (including your future self, who may not necessarily remember all of the details of how you’re implementing it now), will have less trouble understanding what is going on. There’s also a much-reduced likelihood of the same class being used to represent completely different types of data.

New functionality

Finally, are you sure you don’t want to add any functionality? PHP includes a number of interfaces that can be extremely useful for working with this sort of data.

First off, the ArrayAccess interface allows you to access an object’s values using array syntax, which can be useful. Also, implementing either Iterator or IteratorAggregate will allow you to iterate over the object using a foreach loop. The Countable interface is less useful, since all it does is let you get the number of items, but it’s sometimes handy. Finally, the Serializable interface lets you serialise an object so it can be stored in a suitable medium, which can sometimes be useful.

The same also applies to some of the magic methods. The __toString() method, in particular, can be useful for returning a suitable string-based representation of an object - for instance, if it represents an item in a database, it might be appropriate to use this to return the ID of the item, or a text representation of it (eg title for a blog post, or product name for a product in an e-commerce site). The __get() and __set() magic methods may be a bit more dubious, but they can be useful if your object is intended to just be a dumb container as they allow you to make the properties on the object private, but keep them accessible without writing explicit getters and setters. I’d also suggest adding __debugInfo() to objects unless you have a good reason not to, as when you’re debugging it can be hard to see the wood for the trees, and returning only the most pertinent data can make your job a lot easier.

Of course, you don’t have to implement all this functionality from scratch for every class. It often makes sense to create an abstract class that implements this sort of basic container functionality, and then base all your container classes on that, overriding it when necessary.

Summary

Hopefully, this has made it clear how compelling it is to use named classes instead of stdClass, and how much benefit you can get from not just using named classes, but creating your own base container class for them. I’m of the opinion that PHP should probably make stdClass abstract to prevent them from being used like this, and indeed I’m seriously considering the idea of creating a Codesniffer sniff to detect instances of stdClass being instantiated and raise them as an error.

25th January 2020 10:25 pm

F*** Phpstorm Man and the High Horse He Rode in on

There’s a particularly unpleasant type of programmer that exists, and you’ve probably met him, either online or in person. I call him PHPStorm Man.

NB: Despite the name I’ve chosen, I’m not singling out users of PHPStorm in particular about this. The first time I encountered PHPStorm Man in person, he was using Sublime Text, and you will find PHPStorm Men using all different editors and IDEs. PHPStorm Man is an archetype defined not by any particular piece of software, but by a common bad attitude, and given that I work primarily with PHP these days, I’ve most often encountered this kind of behaviour from PHPStorm users (or at least, people claiming to be PHPStorm users, since at least online you can’t discount the possibility that they’re trolls). Users of other languages may well see this behaviour most prominently from those who use some other editor or IDE, but the same underlying phenomenon is at work, whether we call him PHPStorm Man, Eclipse Man, Vim Man or PyCharm Man.

Who is PHPStorm Man?

The hero we really, really don't need

PHPStorm Man (and he will almost certainly be a man - while it could be just because our industry is male-dominated, I’ve never known a woman to behave like this, and I strongly suspect it’s nothing more than an industry-specific example of the common phenomenon of mansplaining) doesn’t know squat about your editor or IDE. He just knows his is superior, and he wants you to know it, regardless of the evidence.

His knowledge of your editor is probably either non-existent, grossly outdated, plain ill-informed or second-hand (probably from another PHPStorm Man). If he’s advocating an IDE, he’ll likely equate all text editors with Notepad - he may claim the advantages of his one over yours include such fundamentals as syntax highlighting and autocompletion.

He’ll boast about some feature his editor has that yours doesn’t, even if it does. If your editor lacks that functionality out of the box, but can add it via a plugin, apparently that doesn’t count because although you’re intelligent enough to build a working web app, somehow installing and configuring that plugin is an insurmountable burden (yet mysteriously turning off all the stuff you don’t need in his editor is quick and easy). If it does do that out of the box, he’ll probably find some bullshit reason why his editor’s implementation is better, even if it’s something as pointless and irrelevant as “it’s been around longer”. He’ll likely claim, with absolutely no evidence whatsoever, or indeed in the presence of evidence to the contrary, that you’d be more productive if you only used his editor.

In short, if you aren’t using his editor or IDE of choice, you’re a troglodyte living in a dung hut.

PHPStorm man in the wild

I had an encounter with PHPStorm Man in person a while back. Just over two years ago I started a new job, which as it turned out didn’t last long after I caught the flu that was going around in late 2017 my first week. On the second day, shortly after going over something with me, the senior dev sent me the following message on Slack:

I noticed you’re using Vim. Have you tried using Sublime Text?

I responded that I had, and chose not to use it. There followed a long string of messages along the following lines:

Him: Sublime Text has X!

Me: I have that

Him: Well Sublime Text also has Y!

Me: I have that too, via a plugin

Him: Well, Sublime Text doesn’t need a plugin for that

Me: Irrelevant since the plugin is already installed and configured, and I know how to use it

Him: Well, what about this?

Me: That sounds cool, so I just found a plugin to do that and installed it

Him: And this?

Me: I have absolutely no need for that

Him: Well, Vim is old, Sublime Text is new!

Me: Actually, this is Neovim, which is technically newer than Sublime Text

Him: Well, Sublime Text is a GUI application

Me: Exactly. That makes it slower and forces me to use the mouse, aggravating my RSI. I use the terminal because it’s more efficient

Him: Well, I don’t mind what you use…(despite the evidence of that entire conversation)

With the benefit of hindsight, what I should have responded with was this:

I’m an experienced, professional web developer of over six years, and I chose my editor based on years of personal experience, and have chosen my plugins and configuration based on what’s useful to me, and continue to do so to this day. I don’t appreciate you talking down to me like a child.

Why I personally don’t use an IDE

In my case, I have a particularly good reason not to use any GUI application to develop in. Before I was a developer, I worked for an insurance company in a customer service role, and I didn’t have access to the sort of decent quality keyboards and mice developers habitually use, as well as having output goals linked to discipline and bonus/salary raises and having to use custom internal applications on Windows XP, with dreadful keyboard support. As a result I developed a degree of RSI in both hands, which I’ve found is aggravated by using any application that requires me to use a mouse extensively - I’m generally OK if I only have to type, but reaching for the mouse all the time quickly becomes tiring, and soon after painful.

For that reason I’ve developed a workflow that’s strongly dependent on the command line - I use Neovim in the terminal, alongside Byobu so that I can run multiple tabs and switch between them quickly without touching the mouse. Moving to a more GUI-oriented workflow would require me to use the mouse more than I do now, which would probably become physically painful quite quickly. Using an editor or IDE which I found made me more prone to further flare-ups of RSI could have serious consequences for my long-term health, and could potentially be career-ending. If I worked somewhere that mandated a particular IDE that didn’t work well for me, I’d have to either negotiate an exception on health and safety grounds or quit.

I’m also of the personal opinion that much of the functionality of an IDE should not be, in principle, tied to that IDE, but should instead be the province of more general purpose tools that can used, not merely in any editor or IDE but, where appropriate, on a continuous integration server. For instance, language servers provide a tool-agnostic way for any IDE or editor to implement functionality such as completion or navigation, and linters such as ESLint can integrate into any half-decent editor or run in a CI environment. Since these tend to be open source projects, whereas IDE’s are normally commercial offerings, they’re less vulnerable to suddenly disappearing and leaving users high and dry.

There’s also a lot of functionality in an IDE that I rarely, if ever, use. There’s no point including and starting up an FTP client as part of my editor if I’m never going to use it, as it slows the application down, and nor should I have to root around trying to turn off functionality I’m never going to have to use. For a lot of other functionality, there are more powerful standalone applications that I’m used to such as Postman or MySQL Workbench, and I’ll use them as and when I need them - I gain nothing by having them integrated with my editor.

I also like to be able to use the same editor everywhere. I still occasionally dabble in Python, so a language-specific IDE wouldn’t be suitable when switching between languages. I also sometimes work on personal projects on an HP Stream netbook running Xubuntu, which is fine for small PHP projects that don’t require a database server or any web server other than the PHP dev server. I can happily run Neovim on that, but there’s no way it could run an IDE at an acceptable speed.

Last of all, screen real estate is an issue. I don’t like interfaces that are too busy - I cannot stand having anything, at all on my desktop for any length of time at all, and any interface that has too much on screen at once is distracting. I will typically have Neovim open in a terminal, with the NERDTree file finder open on the left, and two panels split in the main body, and that’s all. A big factor in my productivity is how much code I can see at once, and having too much screen real estate taken up by menus and sidebars is counterproductive - with Neovim there’s almost nothing getting in the way.

I personally have had to give this sort of explanation many, many times as to why I use first Vim and then Neovim, and indeed part of the motivation behind writing this post is that I’m sick to death of having to explain myself over and over again and will now be able to merely direct them to this article. Thanks to tools like PHPActor, vim-ale and FZF, I don’t feel like there’s anything I’m missing out on that an IDE would give me, and Psalm is very good at catching type errors without being tied to any one IDE, but that doesn’t stop people telling me I’m missing out on features I already have. Any time I come across a feature I think is cool, I go through the following process:

  • Find cool feature
  • Find plugin that implements said feature
  • Install plugin by adding a single line to my Neovim config and running :PlugUpdate
  • Add a few lines of config
  • Start using feature

Using an IDE would eliminate the middle three steps, but I don’t find those onerous - we’re talking about the work of five minutes, which is insignificant compared to the time taken to learn to use the feature effectively. And a feature you don’t use is one that you still have to start up if it’s present, so making it an opt-in plugin is often a better way to go.

Every other developer will have their own version of this story. Some will have stayed mostly static in their editor choices, while others will be changing all the time - indeed, I’ve sometimes used other editors for specific tasks in the past. In short, everyone has their own reasons for using their editor of choice, and it’s appallingly arrogant to assume that their reasons for using a particular one are less valid than yours.

Am I PHPStorm Man?

As I’ve said before, this behaviour is not confined to PHPStorm users, nor is it in any way universal among them. If you use PHPStorm and enjoy it, then fine, rock on. If you use a different editor or IDE, then that’s fine too - I don’t have a problem with that, and nor should your colleagues or line manager. Using any one editor or IDE does not make you PHPStorm Man. What makes you PHPStorm Man is the patronising attitude.

In the example given above, what made the senior dev PHPStorm Man was not the initial enquiry as to whether I’d tried Sublime Text, but the fact that he wouldn’t leave it be when confronted with evidence that I either had, could easily obtain, or didn’t need the functionality of his editor in mine, and that he was talking down to an experienced developer like a child.

Obviously, this isn’t a new development - editor wars have long been a feature of our industry, as has the divide between IDE and editor users. But that doesn’t mean I, and no doubt others, don’t get utterly sick of it.

How not to be PHPStorm Man

When talking to users of other editors or IDE’s about the subject of those tools, you should always bear this in mind:

  • If they use a different tool to you, they probably know a hell of a lot more about it than you do, and are unlikely to take kindly to you ignorantly telling them what it can and can’t do
  • Mastering an editor or IDE can take years, and if they’re already invested in one, it’s incredibly arrogant to just assume that they’re less productive in it than they would be in yours - even if they would (and that’s almost certainly debatable), it would take some time to adjust.
  • They’ve probably had this conversation many times before, and are sick of hearing it, especially if they have a few years experience under their belt
  • Not every feature you use is useful to them
  • No-one minds seeing a cool feature, so feel free to demonstrate it, but bear in mind that it’s almost certainly not limited to that platform - if it’s sufficiently cool, someone will have made it available as a plugin on most of the major editors and IDE’s. If they like it, the most likely scenario is that they’ll look to add that feature to their own editor via a plugin
  • Just because it makes you more productive, doesn’t mean that it would make them more productive
  • It’s perfectly possible to enforce consistent code styles and catch errors using standalone tools such as PHP CodeSniffer, Psalm, or ESLint, and these tools can be integrated in any editor, triggered with Git hooks, or run with continuous integration.

Now, it has to be said that sometimes there are some people who plod on with painfully outdated tools, like Notepad. But those tools tend to be limited to either commercial offerings that are no longer maintained or supported, or ones that lack any sort of plugin or extension system, making them limited in terms of how they can integrate with other services, so they’re fairly easy to spot. However, making a particular editor or IDE compulsory is going to be disruptive. If you’re in a leadership position, one way to resolve this is to simply require that everyone’s editor have certain functionality - for instance, if you specify that everyone’s editor must allow integration with PHP CodeSniffer and support for .editorconfig, then anyone using a legacy editor that can’t support those will need to move away from it, but they’ll be able to pick one that suits them, rather than be forced into one they may well dislike. Editors and IDE’s don’t produce proprietary formats the way word processors do - they work with common formats, and if prominent open-source projects can enforce a consistent coding standard with many different editors there’s absolutely no reason why your colleagues can’t do so too,

Summary

This post is a bit of an angry rant, but at the same time it shouldn’t be taken too seriously. As I said, despite the name PHPStorm Man, it’s not specifically about users of any one editor or IDE, but about the widespread, patronising attitude many developers have about editors and IDE’s other than their own in general.

Someone using a different IDE or editor is absolutely none of your business unless you’re their line manager or you work on the same code base, and even then it should only be an issue if it causes a clear effect on their productivity or the quality of their code. If that’s not the case, keep your nose out.

27th October 2019 9:20 pm

Input Components With the Usestate and Useeffect Hooks in React

Like many developers who use React.js, I’ve been eager to explore the Hooks API in the last year or so. They allow for easier ways to share functionality between components, and can allow for a more expressive syntax that’s a better fit for Javascript than class-based components. Unfortunately, they became production ready around the time I rolled out a new React-based home page, so I didn’t want to jump on them immediately in the context of a legacy application. I’m now finding myself with a bit of breathing space, so I’ve begun refactoring these components, and converting some to use hooks, in order to more easily reuse some code that currently resides in a big higher-order component.

The useState and useEffect hooks are by far the most common hooks in most applications. However, I’ve found that the React documentation, while OK at explaining how to use these individually, is not so good at explaining how to use them together, particularly in the case of an input component, which is a common use case when looking to convert existing components. For that reason, I’m going to provide a short example of how you might use them together for that use case.

A simple function component

A basic component for an input might look like this:

//@flow
import React from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
return (
<input type="text" name={props.name} id={props.id} value={props.value} placeholder={props.placeholder} />
);
}
export default Input;

Note I’m using Flow annotations to type the arguments passed to my components. If you prefer Typescript it should be straightforward to convert to that.

As you can see, this component accepts a name, ID, value and placeholder as props. If you add this to an existing React app, or use create-react-app to create one and add this to it, you can include it in another component as follows:

<Input name="foo" id="foo" value="foo" placeholder="foo" />

Adding state

This will render, but as the value will never change it’s not actually of any use in a form. If you’ve written class-based React components before, you’ll know that the usual way to handle this is to move the value of the input from props to state. Prior to the introduction of the Hooks API, while you could create a function component, you couldn’t use state with it, making situations like this difficult to handle. Fortunately, the useState hook now allows you to add state to a function component as follows:

//@flow
import React, { useState } from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
const [value, setValue] = useState(props.value);
return (
<input type="text" name={props.name} id={props.id} value={value} placeholder={props.placeholder} onChange={(e) => setValue(e.target.value)} />
);
}
export default Input;

We import the useState hook at the top, as usual. Then, within the body of the component, we call useState(), passing in the initial value of props.value, and get back two variables in response:

  • value is the value of the state variable, and can be thought of as equivalent to what this.state.value would be in a class-based component
  • setValue is a function for updating value - rather than explicitly defining a function for this, we can just get one back from useState()

Now we can set the value with value={value}. We also need to handle changes in the state, so we add onChange={(e) => setValue(e.target.value)} to call setValue() on a change event on the input.

Handling effects

The component will now allow you to edit the value. However, one problem remains. If you open the React dev tools, go to the props for this component, and set value manually, it won’t be reflected in the input’s value, because the state has diverged from the initial value passed in as a prop. We need to be able to pick up on changes in the props and pass them through as state.

In class-based components, there are lifecycle methods that fire at certain times, such as componentDidMount() and componentDidUpdate(), and we would use those to handle that situation. Hooks condense these into a single useEffect hook that is more widely useful. Here’s how we might overcome this problem in our component:

//@flow
import React, { useState, useEffect } from 'react';
type Props = {
name: string,
id: string,
value: string,
placeholder: string
};
const Input = (props: Props) => {
const [value, setValue] = useState(props.value);
useEffect(() => {
setValue(props.value);
}, [props.value]);
return (
<input type="text" name={props.name} id={props.id} value={value} placeholder={props.placeholder} onChange={(e) => setValue(e.target.value)}/>
);
}
export default Input;

useEffect takes one compulsory argument, in the form of a callback. Here we’re using that callback to set our state variable back to the value of the prop passed through.

Note the second argument, which is an array of variables that should be watched for changes. If we had used the following code instead:

useEffect(() => {
setValue(props.value);
});

Then the callback would fire after every render, reverting the value back and possibly causing an infinite loop. For that reason, we pass through the second argument, which tells React to only fire the callback if one of the specified variables has changed. Here we only want to override the state when the value props passed down to the component changes, so we pass that prop in as an argument.

Summary

This is only a simple example, but it does show how simple and expressive hooks can make your React components, and how to use the useEffect and useState hooks together, which was something I found the documentation didn’t make clear. These two hooks cover a large chunk of the functionality of React, and knowledge of them is essential to using React effectively.

13th October 2019 11:10 pm

Flexible Data Types With the JSON Field

Relational databases have many advantages over other data stores. They’re (mostly) solid, mature products, they have the means to prevent data duplication while still allowing related data to be accessed, and they allow for easy enforcement of data types. However, the latter point has also historically made them less flexible compared to document databases such as MongoDB, which allow for fields to be set dynamically, making it much easier to set content arbitrarily.

One area in which this has caused particular problems is with content management systems, where you might want to be able to set custom content types that need to be treated the same in some circumstances, and have some fields in common, but store different data. If you want to be able to store arbitrary data against a particular entity, historically the main way to do that is to create a meta table to contain keys and values, and set the entity ID as a foreign key in the new table.

Wordpress is a common example of this. Any piece of content is stored in the wp_posts table, which in addition to the generic structure of a post, also includes the post_type field. It’s possible to create and register your own post types, but it’s not possible to store additional custom data in that table. Instead, it’s stored as keys and values in the wp_postmeta table, meaning you need to do a join to retrieve that content at the same time, making for more complex queries.

Another approach is to have a generic entity table that contains the common fields, and separate tables for the rest, and then set up a one-to-one relationship between them. However, that can be fiddly too because it doesn’t allow for custom types in the same way, so it may not fit with your use case if you need to be able to create arbitrary content types, such as for a CMS that allowed for custom content types.

Introducing JSON fields

JSON fields allow you to bring some of the flexibility of document databases to the relational world. They allow you to store whatever arbitrary text data you wish as JSON, and retrieve it as usual. More importantly, they also allow you to query by that data, so you can easily filter by fields that need not be set in stone with a database schema.

This means that in the above example of a CMS, instead of having a separate meta table, you can instead store the meta values in a JSON field, thus removing the need for a join and simplifying querying by those values.

PostgreSQL has had this capability for a long time, but it’s only comparatively recently that MySQL and MariaDB versions that support it have become widespread. Here I’ll show you how you might use it in a Laravel application.

The example will be a content management system with flexible content types. The first step is to create the migration to add the new content table:

<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateContent extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('content', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('type', 20);
$table->json('attributes');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('content');
}
}

Here we’re specifying the following fields:

  • An auto-incrementing ID (feel free to swap this out for a UUID if it makes sense for your application)
  • A string denoting the content type. If you want to limit the values these can accept, you can replace it with an ENUM field
  • The JSON field, named attributes
  • The standard Laravel timestamp fields, created_at and updated_at

If there are certain fields that are common to all of your content types, it would also make sense to define them as fields in the usual way, rather than use the JSON field, since compulsory fields should be enforced by the database.

Next, we’ll create the model:

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Content extends Model
{
protected $table = 'content';
protected $casts = [
'attributes' => 'array'
];
}

Note that we cast the attributes field to an array. If we didn’t do this, we’d need to manually run json_encode() and json_decode() on the field to get it back in a usable form. As it is, we can now easily retrieve fields using array access.

With that done, we can now set up some data:

<?php
$c = new App\Content;
$c->type = 'page';
$c->attributes = [
"type" => "info",
"title" => "Terms",
"content" => "Our Terms",
"layout" => "info",
];
$c->save();
$c = new App\Content;
$c->type = 'link';
$c->attributes = [
"type" => "external",
"link" => "http://example.com",
];
$c->save();
$c = new App\Content;
$c->type = 'page';
$c->attributes = [
"type" => "promotional",
"title" => "My page",
"content" => "This is my page",
"layout" => "default",
];
$c->save();

As you can see, we’ve been able to set out whatever arbitrary fields we wish on these items. We can then call toArray() on a model to get all the fields, including the attributes, or we can call $c->attributes to get all those attributes together. We can also get a field via array access, eg $c->attributes['type'].

Querying the data

The syntax for querying JSON fields is a little bit fiddly:

SELECT * FROM `content` WHERE attributes -> '$.type' = 'promotional';

Fortunately, Eloquent makes it much simpler:

<?php
Content::where('attributes->type', 'promotional')->get();

It’s also possible to order by a JSON field value, but at the time of writing there’s no neat syntax for it, so you have to drop down to writing it using orderByRaw in Eloquent:

<?php
Content::orderByRaw("attributes-> '$.type'")->get();

Eloquent also supports a few other JSON query types, such as querying if an array contains a value, and I suggest referring to the documentation if you want to know more.

Other applications

There are many other scenarios where this approach can be useful. For instance, e-commerce sites often sell many different products that may have arbitrary properties, and it may be necessary to sort and filter by different properties for different types of products. A store that sells, among other things, shoes and storage containers, might need a colour and capacity field for storage containers, and a colour and size field for shoes. Using this approach, you can set up your database in such a way that those arbitrary fields can be set up when needed, and used for filtering.

This approach is not without its downsides. Any data that’s stored in a JSON field can’t be validated by the database in the same way, so the burden of ensuring that it remains in a consistent state is moved to your application code. However, it’s no worse than it would be if you used a document database, and unlike with a document database you can combine JSON and more traditional fields as you see fit.

22nd September 2019 7:00 pm

Storing Wordpress Configuration in Environment Variables

Wordpress configuration can be a serious pain in the proverbial. Hard-coding configuration details in a PHP file is not a terribly safe way of storing the details for your database, as if the server is misconfigured they can be exposed. In addition, it can be a chore to copy and populate the wp-config.php file to a new deploy.

A fundamental principle of The Twelve-Factor App is that config should be stored in the environment. While Wordpress does predate this, there’s no reason why we can’t abide by this. Storing Wordpress configuration in environment variables rather than the wp-config.php file has the following advantages:

  • It’s more secure since the config is not stored in a file in the web root, but in the web server config
  • It makes managing the wp-config.php file less of a chore - it can be safely committed to version control, and you won’t need to change it to match your local configuration, running the risk of accidentally committing and pushing to production with broken config
  • Deployment to new servers is simpler because there’s no need to update the wp-config.php
  • The risk of neglecting to change the database details and accidentally messing up the production database when working locally is virtually eliminated

I’ve seen solutions for this that use DotEnv, but you don’t actually need to install that to be able to use environment variables with Wordpress. In fact, in some way it’s better if you don’t as too many developers use .env files in production. PHP natively has the ability to get data from environment variables using the getenv() function, so it’s easier to use that than to pull in a third-party library.

Here’s an abbreviated example of a wp-config.php file that’s been updated to pull the settings from environment variables:

<?php
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', getenv('DB_NAME') );
/** MySQL database username */
define( 'DB_USER', getenv('DB_USER') );
/** MySQL database password */
define( 'DB_PASSWORD', getenv('DB_PASSWORD') );
/** MySQL hostname */
define( 'DB_HOST', getenv('DB_HOST') );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
define( 'AUTH_KEY', getenv('AUTH_KEY') );
define( 'SECURE_AUTH_KEY', getenv('SECURE_AUTH_KEY') );
define( 'LOGGED_IN_KEY', getenv('LOGGED_IN_KEY') );
define( 'NONCE_KEY', getenv('NONCE_KEY') );
define( 'AUTH_SALT', getenv('AUTH_SALT') );
define( 'SECURE_AUTH_SALT', getenv('SECURE_AUTH_SALT') );
define( 'LOGGED_IN_SALT', getenv('LOGGED_IN_SALT') );
define( 'NONCE_SALT', getenv('NONCE_SALT') );
$table_prefix = 'wp_';
define( 'WP_DEBUG', getenv('WP_DEBUG') );
/* That's all, stop editing! Happy publishing. */
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', dirname( __FILE__ ) . '/' );
}
/** Sets up WordPress vars and included files. */
require_once( ABSPATH . 'wp-settings.php' );

If you’re using Lando for local development, you will need to specify a file to include that contains the environment variables you wish to set, as in this example:

name: wordpress
recipe: wordpress
config:
webroot: .
env_file:
- .env

This filename can be any arbitrarily chosen name. Then, you define the values for those variables in the same way you normally would in a .env file. Here’s an abbreviated example that excludes the crypto settings (though those should be placed here too):

DB_NAME=wordpress
DB_USER=wordpress
DB_PASSWORD=wordpress
DB_HOST=database
WP_DEBUG=true
...

This will work fine during local development, but in production, or if you’re using something like Vagrant for local development, you’ll want to set the environment variables in the server configuration. For Apache, this is best set in the Virtualhost configuration, although you should be able to set it in an .htaccess file if all else fails. You need to use the SetEnv directive, as in this example:

SetEnv DB_NAME wordpress
SetEnv DB_USER wordpress
SetEnv DB_PASSWORD wordpress
SetEnv DB_HOST database
SetEnv WP_DEBUG true

For Nginx, assuming you’re using FastCGI, you need to set it in the server configuration for that site using the fastcgi_param directive, as shown below:

fastcgi_param DB_NAME wordpress;
fastcgi_param DB_USER wordpress;
fastcgi_param DB_PASSWORD wordpress;
fastcgi_param DB_HOST database;
fastcgi_param WP_DEBUG true;

Since Wordpress doesn’t ship with any kind of command-line task runner, this should be sufficient for most installs. However, if you’re using WP CLI, that will break it as it won’t have access to environment variables set by Apache or Nginx, so you’ll also need to set them for the user that runs WP CLI by adding them to their Bash config in the usual way.

Recent Posts

Lightweight Laravel - Deconstructing a Full Stack Framework

What I Want in a PHP CMS

Flow Typed AJAX Responses With React Hooks

Caching the Laravel User Provider With a Decorator

The Trouble With Integrated Static Analysis

About me

I'm a web and mobile app developer based in Norfolk. My skillset includes Python, PHP and Javascript, and I have extensive experience working with CodeIgniter, Laravel, Zend Framework, Django, Phonegap and React.js.