loading...
Cover image for When PHP Framework Sucks Series: Magic inside frameworks

When PHP Framework Sucks Series: Magic inside frameworks

damnjan profile image Damnjan Jovanovic ・3 min read

When PHP Framework Sucks (5 Part Series)

1) When PHP Framework Sucks Series: PHP Frameworks overview 2) When PHP Framework Sucks Series: Business logic free Controllers 3) When PHP Framework Sucks Series: Framework topic on a job interview 4) When PHP Framework Sucks Series: Magic inside frameworks 5) When PHP Framework Sucks Series: How not to shape your app in the shape of the framework

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 problem with style or standards for annotations and configurations than because I'm unable to reuse code and expect the same behavior with any other environment.
What I meant when I say "framework magic"? I prefer to complain about these four magic points:

  1. Defining code inside method or class annotations
  2. Defining classes instances in yaml or any file different than .php
  3. All config inside yaml, XML, or any other file
  4. Containers which "contains" anything

Defining code inside method or class annotations

We tend to use annotations so often for defining method properties. I recently saw complete documentation for swagger in controller above every action. My god, it was so long that I wasn't able to fit it in my screen height. The notation which exceeds screen hight! That's a red alarm of first grade. Long notations are not just ugly, they create so much noise, that prevents you from seeing the actual code.
It is also very common to see validation inside notation. I disagree with that so much. First, if you change the library for validation, reuse your code somewhere else, validation library becomes deprecated or similar, you will end up with the useless string above your method.
Instead, use some Value Object for validating specific data types, try to enforce strong types in every method.

/**
 *@library I‘m the useless piece of comment if you change the library hehehehe
 */
public function someMethod()
{
}

Defining classes instances in yaml or any file different than .php

Classes instances in non-PHP files are very problematic if you want to follow the execution line in your IDE and then you end up that some class is a dead end. This class is not a dead end as it seems to be, there is a code which instantiates it, but it is in yaml.
Wait, YAML ???
True, many time I see the declaration for classes which we have to define as a "service" for example, inside XML or yaml files.

All config inside yaml, XML, or any other file

Generally, config inside yaml files annoys me. I know it brings some readability for folks, but I always wonder, why not simply in php? No need to parse, no need to warm up.

Containers which "contains" anything

This one is wrong, at least from two perspectives. Perspective number one, the container return type is what? Containers can return on get method call (or similar name) any type, any object, or property. How do I know that in the name of God? How can I call any method on that object since I don't know its type?

public function testAction()
{
    $something = $this->container->get('hehehehe_you_newer_know_who_I_am');
}

The second problem is that container violates such a hard "Interface segregation principle" the famous "I" in SOLID principles. Your Controller or any other class which has access to the container knows too many things at this point.

Conclusion

Notations are comments, you can use it to specify some method properties, but do it with caution. If you try to find the usage of the class, defined in yaml, good luck! There is unnecessary parsing of yaml and XML configs. Controllers and other container aware objects know too much, but you don't have an idea what container returns.

When PHP Framework Sucks (5 Part Series)

1) When PHP Framework Sucks Series: PHP Frameworks overview 2) When PHP Framework Sucks Series: Business logic free Controllers 3) When PHP Framework Sucks Series: Framework topic on a job interview 4) When PHP Framework Sucks Series: Magic inside frameworks 5) When PHP Framework Sucks Series: How not to shape your app in the shape of the framework

Posted on Dec 24 '18 by:

damnjan profile

Damnjan Jovanovic

@damnjan

I had a startup and it failed ... I still believe it is one of the coolest things I've ever done

Discussion

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:

github.com/symfony/security-acl/bl...

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.

 

Hi!
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.

Saludos,

 

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.