Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I agree with a lot of what this article says.

I've recently started a new application in PHP which has very strict time / performance requirements. I could build it in another language, but I'm most familiar with PHP, the thing would take me 4x as long to build in Rust.

The default frameworks (Laravel and Symfony) are very slow out of the box, and doctrine / propel are pretty slow as well.

I've instead built something entirely brand new using Swoole and an assembled set of very fast and very light Composer packages (namely Fastroute and PHP-DI) and everything else I'm building myself (compromising on reusability and full-featuredness in return for raw performance).

I've split the code in to two: one half is the framework, it's concerned with routing, dependency injection, models and hydration, event handling and responses. The other half is focussed on the business concerns: creating users, creating authentication token, performing searches.

There were a few challenges: moving from single row, single model queries to hydrating complex joined collections was a major multi-day challenge, but the end result is that when a request has all the data it needs cached, it responds in ~3ms, when it needs something from the database it responds in ~10ms.

I was apprehensive when I started, and a half decade ago this codebase would be an unmaintainable mess, but I'm finding it a joy to work in: there are few layers of abstraction, not a single line of code is wasted, and when something goes wrong there's no magic obfuscating the true problem.



This is one of the primary reasons most people hate PHP. Let me ask a simple question: What does the documentation for your homegrown framework look like? Having worked for several organizations that decided to roll their own framework in PHP, it is an absolute nightmare. No one else knows how it works or how to use it and it usually comes with no documentation. Furthermore, you don't get any updates or bug fixes unless you write them, taking time away from working on the project that is being held up by your homegrown solution. There are plenty of fast, low bloat, PHP frameworks to choose from: Phalcon, Slim, Luman and Silex just to name a few. I guarantee they all have better documentation and support. If you need some custom functionality, extend one of these existing solutions to suit your needs. Please, please stop reinventing the wheel for production code.


I agree with everything you've said, with the exception that it doesn't fit our usecase.

We run and maintain several Symfony applications, we're very familiar with frameworks, how they work, their strengths and weaknesses.

This was a carefully considered route:

We wanted to be writing PHP code with high performance and strict response time requirements, as such:

* Symfony was instantly ruled out.

* We knew we would struggle to do this with raw PHP but didn't want to retrain the team to learn a new language (our thinking being we would likely write something inferior and insecure).

* We considered Phalcon and Swoole and ultimately decided on Swoole as we found Phalcon a bit too opinionated. That being said, Swoole's documentation isn't great so we've had to internally document it. (We will probably push a few documentation PRs for swoole-src when we're done).

* We didn't trust a big framework to have zero memory leaks, which is a key issue for long running Swoole processes, since it's not a typical issue for the single-use PHP processes we are used to.

With regards to documentation, it's very well documented (with documentation explaining the reasoning behind each class, usage, pitfalls and how it fits in to the larger application, and documentation for the wider ergonomics of the application, debugging, performance considerations, etc), with a 96% test php-unit coverage for the Framework.

Your concerns are very well received and are concerns we have had from the beginning. Constant vigilance and a deep respect for my co-workers is what keeps this little project on the straight and narrow.


What were your constraints in performance? I'm surprised Laravel is considered slow.

Have you considered sticking to it and applying some caching layer on top of it?


It's part of a live video platform for a gaming company which needs extra information to be overlaid on top of their live stream (for the customer in-video frame performance is incredibly important: the stream run at 30fps (so a maximum of 33ms per frame) and the overlay information has to be generated in 11ms or less)

We did originally consider writing it in Rust (it would have been my personal preference) but the consensus was that since the rest of the tooling for this customer is in PHP we didn't want to add a skillset they didn't have internally if they were to move away from us in the future (we aren't big on needless vendor lock-in and prefer to compete on quality and service).

We experimented with PHP7.4 and Symfony / Laravel and found that we didn't have the performance leeway we needed, even with preloading / opcache.

We opted for Either Swoole or Phalcon, tried them both and found that Swoole was more to our liking and was somewhat more performant than Phalcon in our real-world testing.

We are exposing Swoole directly to them within their VPC, so we were able to do away with NGINX as a reverse proxy as well and have Swoole handle the HTTP request phase and HTTPS. We were able to make heavy use of Swoole\Table to save superfluous trips to Redis and run a Redis synchronisation process in a coroutine thread when the concurrent number of network tasks drops below 16.

Our end service response times (including network overhead) are around here:

- Mean response time: 4ms

- Fastest: 2ms

- 99th percentile: 9ms

- 999th permille: 10ms

One unforeseen side effect is that it has a crazy amount of scalability built in by default, the 8core machine the process runs on can easily scale up to 15,000 concurrent requests (as in 15,000 requests per second) without going over 15ms for response times, and can scale up to 45,000 concurrent requests before things start to "break".


Also: Our issues with Laravel / Symfony weren't just performance, but also statefulness and memory leaks. Circular references, etc, which persist throughout multiple network requests. In a normal PHP flow using php-fpm or apache-php-cgi this isn't an issue: The PHP process is created, used and thrown away. For a Swoole setup, these memory leaks quickly get out of control and cause performance issues, instability and very occasionally superfluous garbage collection runs.


I would actually argue there's a good chance the person you're replying to is "doing it right":

- they're using Composer packages

- they're using the fairly well-documented, popular Fastroute for routing

- they're using the well-documented, PSR-11-compliant PHP-DI dependency injection container

- they're using the well-documented Swoole for async programming

In other words, they're rolling their own framework not from scratch, but on top of well-understood components that largely follow -- and largely require programs that use them to follow -- what's considered to be "best practice" in the PHP world.

I get that there's a lot of crap PHP out there, historically, and that frameworks have helped dramtatically reduce that. But it turns out you don't actually have to start with a framework in order to write well-structured, well-commented PHP code -- you just have to have a commitment to doing it.

I'm working on my own PHP microproject right now and I'm pretty flagrantly violating some "PHP the Right Way" conventions; the project doesn't even Composer and -- horror of horrors -- the autoloader isn't technically compliant with PSR-4. I'll pause for everyone to recover from the fainting spell. I don't think anyone else is going to ever have to maintain this -- but I'm also pretty sure that when I'm finished, it will actually be more understandable and maintainable than some Symfony and Laravel projects I've worked on in the past. It's not the fault of those frameworks that people build inscrutable undocumented modules on top of them, but it still happens.

And frankly, I understand why someone can appreciate Symfony and Laravel and friends and still say, "Wow, that's... just... a whole lot." (If you initialize a new Symfony 5 project, before you have written a single line of your own, your project is already at around 160K lines of code.) And they can look at microframeworks, especially modern we-must-comply-with-all-PHP-FIG-mandate ones, and say, "That isn't a whole lot, yet it kind of feels weirdly heavyweight for what comparatively little it does."

> Please stop reinventing the wheel...

You learn a lot by reinventing a wheel. Sometimes what you learn is that a lot of the existing wheels are actually monster truck tires, tank treads, or jet skis.


Friend of mine. Slim framework not updated anymore. Cannot make it work in recent php versions, so a fragment of a big corp needs to be maintained and isolated. I know, happens everywhere


If you mean the framework named Slim, its last point release, according to their web site, was less than a month ago, and it says that the minimum PHP version requirement is 7.2. It's possible your friend isn't able to upgrade the application to a supported version of Slim?


Ummm, it as Sylex, not Slim, that got discontinued


Yeah, it could be the case and then I was wrong about it being discontinued


> I was apprehensive when I started, and a half decade ago this codebase would be an unmaintainable mess, but I'm finding it a joy to work in: there are few layers of abstraction, not a single line of code is wasted, and when something goes wrong there's no magic obfuscating the true problem.

I've "rolled my own" quite a few times this way, with web frameworks and ORMs/query engines, and while I agree it feel that way, I would add the caveat it feels that way at first. Eventually, whether that's 6 months or 6 years, you end up cursing the framework because either you're spending a significant chunk of time implementing features that you didn't need but now do, and would have either been included in a framework when you started, or you would have just been able to upgrade your framework to a newer version to get it.

Unless the project has a very tight scope, the eventual drift in needs causes friction when it comes up against the very targeted capabilities of what you designed for yourself because it only implemented what you needed when you wrote it.

Writing a web framework is useful and fun, as is writing an ORM. At least for the first 75% of features. People should do it, if for no other reason it makes assessing other ones for whether they are suitable for your needs much more easy and accurate as your knowledge increases. But 5 years from now when you (or whoever inherits it) are realizing there's a security issue you need to patch in your home rolled framework, and you aren't quite done patching the security or performance problem that was found last week, you might reassess how good of a choice rolling your own was.

Symfony and Laraval might be slow, but there's a slew of other PHP frameworks to choose from that range from full stack to micro frameworks[1], and those probably deserve a look for anyone in the same position considering the same move. Maybe rolling your own still comes out looking good, but it's not something I think should be attempted in production without quite a bit of consideration as to the eventual implications.

1: https://www.techempower.com/benchmarks/#section=data-r18&hw=...


When I did PHP, I did my own frameworks too.

PHP had a strong re-invent culture, probably because it didn't have a package manager back in the days.

When I started with Node.js and NPM I often just installed something well known.

A few years ago I had the feeling things would get a bit out of hand, so I only installed packages I couldn't code myself, because the lack of domain knowledge or time.


Awesome! Is there a design architecture that you find useful for organizing your code? I noticed you split the code a bit into business vs other. I recently stumbled upon Clean Architecture that helps me make sense of things. Programming isn't my primary field so the more structure in a project, the better (within reason).


I found it most useful to follow what people are already used to, which is to say that it follows the principles laid out by Symfony.

The code for the framework is its own composer library which is pulled in to the vendor directory, the code for the application sits within src/{BundleNamespace}.

If there's a service, it's a service, if there's a controller, it's in Controller, etc.

The aim is to maximise ergonomics and familiarity while minimising surprises. If a developer can pick up your microframework and be instantly writing code then you've done your job right.


>I've instead built something entirely brand new using Swoole and an assembled set of very fast and very light Composer packages (namely Fastroute and PHP-DI) and everything else I'm building myself (compromising on reusability and full-featuredness in return for raw performance).

Is it anything like this?

https://github.com/PatrickLouys/no-framework-tutorial


Yes! I haven't seen this before but it's incredibly similar to what I've ended up with.

There are some superficial differences (we don't have Twig as it's serving as an API, etc) but it mostly follows what the repository you posted lays out.


Have you looked at phalcon at all for PHP? Since it’s written in C it’s much faster than even compiled PHP.

https://phalcon.io/en-us


We did, we had a play with Swoole and Phalcon when we were in the RFC stage of our project.

We found Phalcon to be a bit too opinionated in how to do things, and we fell in love with how Swoole has implemented Coroutines and (practically) free context switching between long running asynchronous processes and short running network requests.

Looking back, I do actually wish we had chosen Phalcon (the documentation is a lot better than Swoole's, which is lacklustre to say the least, and occasionally intersperses random characters in Simplified Chinese halfway through sentences), but we've been happy with Swoole's Table implementation which allows us to share memory between network request Coroutines and hit the Redis cache a whole lot less.





Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: