PHP CMSes done right: how to enable clients to edit content appropriately

In the previous post, I talked about how CMSes harm websites. I debunked the oft used selling points of faster, cheaper and client empowerment over websites and explained how CMSes butcher semantic markup, code decoupling, tasteful style, speed optimisations, maintenance ease and code freedom.

Now I want to mention a few ways how a CMS can be appropriately used to build a website. There are two scenarios I want to cover: using pre-built scripts and prioritising custom code first.

Pre-built scripts

By pre-built, I mean all you really want is an off-the-shelf setup and don’t care for customisations. So grab an out-of-the-box CMS (Joomla, Drupal, WordPress, etc), install an associated theme and several modules from the CMS’s ecosystem and glue them together. With this sort of set-up, you could have yourself a complex website system such as an e-commerce or blog site running within a day, looking good, and costing zilch if you have the know-how.

In this scenario, a CMS should be your top choice. The benefit of speed and set-up far outweighs the extremely costly alternative of custom coding such a complex system. It is for this reason that thinkMoult runs on WordPress: I just wanted a blog to run on the side with minimal fuss.

As the complexity of the system grows this benefit also grows. It would be rare to recommend to the average client to build a blog from scratch, an ecommerce system, a forum, or even ticketing system.

However once you plan on doing lots of customisations, you’re stuck.

Did that really solve anything?

Not yet, we’ve simply outlined a scenario where the cost benefit far outweighs the effort required to invest in a custom product. Unfortunately, all the issues still exist.

So how do we build a CMS for products which don’t fit those requirements – either small tailored “static poster” where first impressions are key or customised systems?

Sceptics might question why building a CMS now is any different from the CMS giants of the past. My answer is that the PHP ecosystem is maturing and the industry is standardising (see PSR-0, Composer, and latest PHP changelogs). Previously we relied mainly on CMSes as they defined a set of conventions we could live with, but now we have proper ones industry wide.

Place custom code first!

VTemplate CMS

The answer is simple. The CMS should not govern your code! Markup style generating, logic modifying systems should be at least completely decoupled if not thrown away completely. The trick to do this quickly is to isolate exactly what a CMS needs to do: and that is to allow the client to edit content.

That’s right: edit content. Not glue modules, not define routing, not to restyle the page, and never, ever, to touch anything to do with logic.

If they ever need anything more complex than editing content, make a module for it. Make that custom module on top of your existing code, and link it to a config repository – nothing else. All it should do is flick switches, not act as a heavyweight crane.

Now, for editing content – I have five strategies to fix the “butchering” aspect of CMSes:

  1. Start by ensuring your frontend code is completely decoupled from all logic. I like to do this by using Mustache as a templating language. It’s simple by design. If your frontend developers can’t break the site’s logic, your client can’t either.

  2. Write your markup and styles perfectly. Writing perfect markup and styles means your editor won’t have to care about whether that extra <div id=”does_things” class=”active wrapper_l12″> was actually vital to the page operating properly. Everything is simple and only uses standard markup tags.

  3. Use a semantic editor. A semantic editor preserves the goodness of point 2. I use WYMEditor, which has bee around for a while. Not only does it stick to the basic tags, it reads extra styles from documented CSS. This way you won’t have clients with no taste inventing new styles and layouts, but only using what you’ve provided.

  4. Beautify the code! PHP Tidy is built-in and can produce well indented, cleanly styled code markup. Don’t have faith in automatic beautifiers? With your perfect markup and complete style/markup separation in points 2 and 3, all your beautifier deals with is the most basic of markup – which probably only needs indenting before it’s called classy code (no pun intended)!

  5. Whitelist editable elements, not blacklist. The default state for whether content should be editable should be off. Don’t give them more than they ask. Because otherwise they will touch it, and inevitably break it. This means you’re custom isolating segments of editable content for the client (I move it into a Mustache partial), and testing it before handing the baton to the client. It also means you can monitor it much more easily- such as inserting an update notifier so that you can run git diff and verify they didn’t somehow still bork things over due to Murphy’s Law.

Et voila! Your client now can edit the content, not break the logic, keep it semantic, keep the code beautiful, and only touches what we wants. He also has a series of switches for the more complex areas of the site. You’re also keeping watch via that update notifier I mentioned (with a monthly fee, of course).

Back-end wise, you’ve lost nothing of the modular ecosystem that CMSes also advertise, because now we’re coding to the PSR-0 standard, and can see the various items that people offer.

What did we lose? Upfront development speed. What did we gain? Everything.

Note: the picture of the CMS is from a custom solution I developed for Omni Studios. Underneath it’s powered by Mustache, PHP Tidy, and WYMEditor, and good markup/styles, all mentioned in this post. So by custom, I mean rebranding a series of industry tools.

Spread the love
Tagged: , , , , , , , ,


  1. Hi Dion, thanks for the pointer to WYMEditor. It looks great and is much more lightweight than CKEditor. The only issue I have with it is that it is not displaying the images (with relative URLs) in the editor window, while the previewing works fine.

    Is there anything I need to change to make the img tag display correctly?

  2. Cheers hari, CKEditor is a great WYSIWYG editor, but WYMEditor is named after what it does best: WYMIWYG. And that’s exactly why it’s preferred, not just because it’s easily styled and is extremely lightweight.

    As for relative URLs not working, that’s because relative URLs in web development are inherently broken! (more reading: ) Define a base URL somewhere and you’ll be safe.

    With Mustache, it’s very easy for the client to put in these variables into the image: just like {{baseurl}}images/myimage.png – if you want to hide the mustache variables completely, just modify an image upload plugin (WYMEditor has quite a few)!

  3. Hi Dion,

    I’ve set the base href path in , but still the editor is not displaying the images that are correctly set relative to the base path. It seems a very specific issue with the IFRAME base path. Is that correct?

    Yes, I understand the benefits of WYSIWYM rather than WYG. That is why I wanted to give WYMEditor a try.

  4. I use relative paths for images and internal links along with the use of base href tag in head element because it is simply easier to move domains and have internal links still work. If I have hardcoded absolute paths in my blog posts, I have to do a full search replace on the database, whereas with base href I can simply define the base URL in the configuration file.

  5. Hi Dion, with respect to paths in web links, I prefer using the base href in the HTML head element, rather than inserting a base url variable in every link.

    For me the biggest issue is that I don’t have to insert the full URL in every blog post I use.

    I don’t use image upload plugins provided by editors, which put images in its own defined location, but simply upload by SFTP on to my images/ folder.

  6. Correct. Because the editor is loaded in an IFRAME it will not be caught by your content renderer. You’ll have to modify how WYMEditor renders content manually. Perhaps will help you :)

    As for the second comment, all content should use a base url variable. Whether or not it is stored in a database is irrelevant – a database is just a type of repository: an alternative method of storage. One of the biggest issues with web developers is that they treat URL routing as a front-end responsibility, where in fact it is a back-end responsibility!

  7. > As for the second comment, all content should use a base url variable. Whether or not it is stored in a database is irrelevant – a database is just a type of repository: an alternative method of storage. One of the biggest issues with web developers is that they treat URL routing as a front-end responsibility, where in fact it is a back-end responsibility!

    No, I am not talking about internal links auto-generated by the CMS itself but by internal links manually created by the end user in the content pages. As an end user if I use absolute paths in my content pages, I will litter the entire website with absolute links to internal pages, which won’t work properly when I move the site to a different server, for instance.

    I’m confused about whether you are OK or not with the base href tag being used in the HTML head portion and using all internal links relative to it. To me it seems far more logical than hardcoding the domain name in every location (and despite the fact that the CMS can generate internal links with full paths in some places, where links are created manually, it creates more confusion).

  8. Sorry – I have created some confusion it seems. How about this explanation:

    In frontend templates, you should never use a relative URL as such: <a href=”../images/foo.png”>, and nor should you ever use a absolute URL as such: <a href=””> Instead, you should always use a variable. This could be <a href=”<?php echo $baseurl; ?>images/foo.png”>. Even when you add content manually to your site, such as when you post a blog article, you should be putting that variable into the content. The system that renders your template will replace the $baseurl (or equivalent) variable with the proper base url of the website.

    When you move sites to different servers, you just change the $baseurl in your backend’s configuration file, and everything works :)

    Ideally, you won’t be using $baseurl.’contact.php’, but you’ll be using {{baseurl}}contact.php or even something like {{contact_page_url}}. A rule of thumb is that whenever you are writing a URL in your frontend template, you’re incorrectly treating system routing as a frontend responsibility.

    As for base href tags, they are generally not a good idea. This is because you are assuming that the entire template will be rendered using the layout with the base href defined. With modern web applications, we all know that template files are split up into many smaller files and later combined by the rendering system. The very reason we split this up is so that we can reuse frontend elements in different contexts. Different contexts by definition mean that there is no guarantee they will be used in that layout with the base href defined. It could be rendered using a completely different layout, or supplied as an RSS feed, an API, or even a desktop widget.

    Did that help?

  9. Hi Dion,

    Thanks for clarifying that. Though your approach sounds fine, it may not work in all cases. For example, in my own blog application, I’ve not used any templating system, so I cannot use “variables” in the blog content as such.

    > The very reason we split this up is so that we can reuse frontend elements in different contexts. Different contexts by definition mean that there is no guarantee they will be used in that layout with the base href defined. It could be rendered using a completely different layout, or supplied as an RSS feed, an API, or even a desktop widget.

    I think base href in the HTML head is a reasonable and clean solution if one doesn’t plan to reuse the code in all contexts or if you ensure that in every context of use, the header template has the base href tag defined.

    Also I think it’s best to use specific template bits only for specific portions of the output and not reuse for others.

  10. I forgot to mention, using variables inside the static content (user generated pages) of a CMS can make it hard to migrate the data to another CMS. It will render a lot of pages useless. It’s almost as bad as using absolute URLs, but worse because links are guaranteed to break if you migrate CMSes.

    Having experienced all the headaches of moving from one site to another and also moving from one CMS to another, I think that using variable names in user generated content can be a big headache.

    I am not at all convinced that relative URLs are evil, except when used wrongly. I also think that the so-called SEO problem with relative URL can be solved by defining the canonical URL at the server level, so that the canonical URL is enforced by the server rather than the application.

  11. By the way, how do you manage internal linking in your content pages in WordPress? Does WP support variable substitution in content pages?

  12. @comment1:

    Templates were designed to “work in all cases” – for example, I see implementations in 22 languages for mustache. If you don’t use a rendering system it becomes a bit more of a hassle to implement. But as PHP was originally designed to sit within HTML, there is nothing technically wrong with inserting PHP variables right into your code.

    The HTML head is a reasonable solution if you’re just developing for the web ad you know exactly which layouts are going to contain your templates. However come expansion time you might have a bit of trouble :)


    This is exactly why the existing CMSes are terrible. They lock you into “our CMS-way of doing things”. The solution? Don’t use a CMS. Use a standard templating language and it’ll always work no matter what engine you throw in the rest in your backend. It’s a classic case of CMSes doing more than they should: CMSes should _edit content_, not _render content_.


    I don’t, and never looked into it. My blog code doesn’t practice what I preach. WP does have a hook for rendering content, and so it should be relatively easy to link it to a templating language.

    Hope my replies made sense.

  13. Dion, I’m afraid that your concept is a bit above my head. How do you plan to separate the CMS from the rendering engine?

    Now it would be interesting to see that in action.

    I myself prefer, where at all possible, a CMS that generates static plain and pure XHTML files whenever possible with absolute URLs in the generated output (thus rendering the whole debate obsolete), requiring no PHP or server side scripting. But that’s too basic for some sites.

    I guess I would love to see one such script in action.

    As for embedded PHP in the content pages, I think that’s a bad idea because you give too much power to the end users.

  14. For using PHP directly in templates: you are right! It’s a terrible thing to do, but only included out of completeness in my answer.

    I can explain this in more detail perhaps through IM. Please contact me with what IM is best and we can have a chat :)

Leave a Reply

Your email address will not be published. Required fields are marked *