Skip to content

When PHP Framework Sucks Series: Magic inside frameworks

Damnjan Jovanovic on December 24, 2018

From all of the topics, I covered at my "When PHP Framework Sucks" series, this one maybe annoys me the most. Not because I have a particular pro... [Read Full]
markdown guide

The reason to not put config information inside PHP is because it makes the application useless when deploying to Heroku, ElasticBeanstalk, CloudFoundry, or any other large-scale cloud service. These cloud providers need to be able to pass critical information to the app such as which port to bind a listener to, or the current URL of the attached database instance, and so forth. Everything else is spot on though on here, nice work.


Hi Kenneth,
Thank you very much for this point I find it very valuable, and I will definitely use it as the only exception for putting configs outside of PHP code. On the other hand, I complained as well that yaml or XML files are used for defining relations between object, as well as annotations.
Thank you once again for spotting this.


Hi Damjan, I agree 100% about not defining classes or relations inside XML or YAML files, or any other file other than a .php file. I don't have much experience with frameworks that work this way, but it would probably drive me crazy to have to work in that manner.


Defining code inside method or class annotations
Defining classes instances in yaml or any file different than .php

These are roughly the same problems which quickly lead to esoteric behaviour that's difficult to divine.

It's creating a new language within a language and compounded by that each library you add might extend it in ways that vary in their documentation.

You do that already building any code. Making functions and methods is creating new vocabulary but at least that's in one syntax.

Creating huge amounts of annotation, yaml, templating languages, etc often only add unnecessary complexity with additional layers of indirection, complicated compile passes, action at a distance, undecipherable states, etc.

Often these additional languages become programming languages outright. You end up using expressions in yaml configurations where you need code, your templating language has it's own unique syntax with loops, conditions, etc. You start seeing @if in your annotations and literals with a completely unique syntax.

Framework is not the right word for these. They're platforms, sometimes inner platforms.

Between browser and backend you can justify a switch between languages but in frameworks that burden is imposed deliberately most often with no real benefit.

The issue isn't that these things are necessarily out of the question but that frameworks force them on you without need and they're a lot more complex than doing things with straight PHP. Documentation missing with PHP? Just read it, it'll do what it need minimally upfront. Making everything configurable and work by magic? Uh oh, be prepared first to wade through 10K lines of convolution, layers, hooks, stages, etc.

The issue isn't that you should never do these things but by default is YAGNI and when you do need some of it you don't need a million lines of framework core to for example simply load a templating library for a specific purpose.

If used wisely, code by convention, configuration pattern, etc can work out alright.

The problem with frameworks, especially the bigger ones, is that they throw this all at you when it's all YAGNI. For all the added extras, it doesn't add much of anything except complexity. Often it makes things worse, not better.

For example, I'm working on a legacy application. The programmers weren't amazing but weren't trash either, they made a fair effort to do things the "framework" way. I need to do a trivial task and the database seems to disconnect due to timeout while the script is doing something else. I look at how to fix this, searching for the exception, apparently I can't because it apparently needlessly needs new instances of everything on a reconnect and the DI is of course going to be static or people are going to put things in vars rather than load every time as they're not normally meant to change in runtime.

Having meta information (the type you might see in annotation) is alright as long as well documented or easily reverse engineered from reading code. Meta information can be easily provided against the class through things such as static methods and decorating with interfaces.

While it can be uglier than annotations, it at least is easily accessible during run time, easier to manage compilation for and isn't in a whacky yet another new syntax which leads into another problem.

By default the simplest possible thing that can work is to just use PHP. Typically for configuration you only need a very small amount of either env vars or json, both of which PHP natively supports. You can also keep your config in PHP (IE, var export, etc) with some debate about portability, it depends on your deploy pipeline.

For pure internal application configuration, for example routing, etc, then there's rarely excuse for it to be anything other than PHP.

If you want to see some real magic, I stumble into it every day, just now I came across this:

Why not just take the interface and that's it? Many of these frameworks are not only poorly written but have magic like this that's undocumented.

It has PHPDoc with the effort taken to add extraneous empty lines (who the hell uses double line spacing) but doesn't document the magic. It's not even updates to use the object type hint saving several lines of code.

These mega frameworks are poorly written, poorly maintained and so full of useless magic like this that I'd say just give up. If you're going to use magic, this case it particularly useless. It's not saving thousands of lines, it's just confusing. Here give me an object and I'll magic you an identity, somehow. This kind of thing is absolutely everywhere. This is also a security component where the last thing you want is hidden behaviour.


I work with two PHP frameworks:
Symfony, not by choice.
CakePHP by choice.

While the first one has every one of the problems you listed here, the second one is the exact opposite. Everything is PHP, you don't have any xml or yaml configuration files.



I've seen the container pattern used in Symfony as a way to allow circular dependencies. If A requires B and B requires A, then one of those needs to require a container and resolve what it needs when it needs it, instead of in constructor.


I saw this where for some inexplicable reason people decided to never use a constructor and to only use getters and setters for each dependency.

code of conduct - report abuse