Frameworks such as Symfony (7.2 at the time of writing) or Laravel are highly customizable and encourage good practices, regardless of your experience and skills.
However, you may still introduce design, security, or performance issues.
❌ This one is a classic but still heavily used by developers:
class LuckyController extends AbstractController { public function index() { $myDependency = $this->container->get(MyDependencyInterface::class); // }
It's possible because the parent AbstractController defines $container as protected:
protected ContainerInterface $container;
Source: Symfony - GitHub
While it does work, it's a bad practice for various reasons:
✅ Use dependency injection in the constructor instead:
class LuckyController extends AbstractController { public function __construct(private MyDependencyInterface $myDependency) {}
Eloquent allows writing SQL queries quite conveniently.
Instead of writing SQL queries directly, developers can use PHP wrappers to interact with database entities.
It also uses SQL bindings behind the scene, so you get injection protection for free, even with untrusted inputs:
User::where('email', $request->input('email'))->get();
❌ However, when you use helpers like whereRaw, you may introduce vulnerabilities:
User::whereRaw('email = "'. $request->input('email'). '"')->get();
✅ At least, always use SQL bindings:
User::whereRaw('email = :email', ['email' => $request->input('email')])->get();
N.B.: the above example does not make sense, but it keeps things simple. In real-world use cases, you may need whereRaw for optimization purposes or to implement very specific where conditions.
With CSRF attacks, hackers force the end users to execute unwanted actions on an app in which they're currently authenticated.
Laravel has a built-in mechanism to guard against such scenario.
Roughly speaking, it adds a token (hidden field) to be sent along with your requests, so you can verify that "the authenticated user is the person actually making the requests to the application."
Fair enough.
❌ However, some apps skip this implementation.
✅ Whether you should use the built-in middleware is not relevant here, but ensure your app is secured against CSRF attacks.
You may read this page for more details about the implementation in Laravel.
Please secure AJAX requests too.
Eloquent allows eager/lazy loading, and supports various optimizations, such as query caching, indexing, or batch processing.
However, it does not prevent all performance issues, especially on large datasets.
❌ It's not uncommon to see such loops:
class LuckyController extends AbstractController { public function index() { $myDependency = $this->container->get(MyDependencyInterface::class); // }
But it can lead to memory issues.
✅ When possible, a better approach would leverage Laravel collections and helpers like chunk:
protected ContainerInterface $container;
Check the documentation for more details.
N.B.: It works, but don't forget those helpers are only meant to ease the implementation, so you have to monitor slow queries and other bottlenecks.
As the documentation says:
useful objects are called services and each service lives inside a very special object called the service container. The container allows you to centralize the way objects are constructed. It makes your life easier, promotes a strong architecture and is super fast!
In other words, you will write custom services to handle specific responsibilities for your application.
❌ The documentation is right. It aims to promote a strong architecture, but it's not uncommon to read services that break the Single Responsibility Principle (SRP):
class LuckyController extends AbstractController { public function __construct(private MyDependencyInterface $myDependency) {}
✅ You must respect that principle. It will be easier to test and maintain.
❌ With Symfony, you can use $container->get('service_id') to call any public service.
We saw earlier that calling the $container like that is considered as a bad practice.
You may not resist the temptation to make all services public, so you can retrieve them by their ID almost everywhere in the project.
Don't do that.
✅ Instead, keep most custom services private and use dependency injection.
Hopefully, you will avoid what I like to call "latent errors" or "hidden errors" with frameworks.
If you don't know how to implement it, trust the framework, but be aware some mechanisms may not be enabled by default.
The above is the detailed content of PHP Frameworks: hidden errors to avoid. For more information, please follow other related articles on the PHP Chinese website!