106

Every day.

I am a PHP developer.

Yeah, "another PHP is awful" rant... no, not really.

It's just unsuitable for some ambitious projects, just like Ruby and Python are.

First of all, DO NOT EVER use Laravel for large enterprise applications. The same goes for RoR, Django, and other ActiveRecord MVCs.

They are all neat frameworks for writing a todo app, as a better-than-wordpress flexible blogging solution, even as a custom webshop.

Beyond 50k daily users, Active Record becomes hell due to it's lazy fat querying habits. At more than a million users... *depressed sigh*.

PHP is also completely unsuitable for projects beyond 5M lines of code in my opinion. At more than 25M lines... *another depressed sigh*.

You can let your devs read Clean Code and books about architecture patterns, you can teach them about SOLID & DRY, you can write thousands of tests... it doesn't matter.

PHP is scaffolding, it's made of bamboo and rope. It's not brick or concrete. You can build quickly, but it only scales up to a certain point before it breaks in multiple places.

Eventually you run into patterns where even 100% test coverage still doesn't guarantee shit, because the real-life edge cases are just too complex and numerous.

When you're working on a multi-party invoicing system with adapters for various tax codes, or an availability/planning system working across timezones, or systems which implement geographical routefinding coupled to traffic, event & weather prediction...

PHP, Python, Ruby, etc are just missing types.

Every day I run into bugs which could have been prevented if you could use ADTs in a generic way in PHP. PHP7 has pretty good typehints, and they prevent a lot of messy behavior, but they aren't composable. There is no way to tell PHP "this method accepts a Collection of Users", or "this methods returns maybe either an Apple or a Pear, and I want to force the caller to handle both Apple/Pear and null".

Well, you could do that, but it requires a lot of custom classes and trickery, and you have to rewrite the same logic if you want to typehint a "Collection of Departments" instead of "Collection of Users" -- i.e., it's not composable.

Probably the biggest issue is that languages with a (mostly) structural type system (Haskell, Rust, even C#/JVM languages to some degree, etc) are much slower to develop in for the "startup" era of a project, so you grab a weak, quick prototyping language to get started.

Then, when you reach a more grown up phase, you wish you had a better type system at your disposal...

Comments
  • 5
    What version are you talking about, as I can easily define in 7+ strict
  • 9
    @xewl

    7.2

    First of all there's Option/Maybe.

    In Java, Haskell, Swift, Rust, etc you can say: "This function returns a User... maybe".

    In PHP, you denote this with "function getUser(): ?User". This typehint does however not force the caller to handle the null option -- In other languages you would get an unwrappable & pattern-matchable Result type, or a Maybe monad, or whatever they chose to call it. You would have peace of mind that any consumer of your function handles all cases properly.

    Now try defining a Tree, or any other ADT.

    In Haskell you have type constructors: "data Tree a = EmptyTree | Node a (Tree a) (Tree a)" -- the "a" is a parameter, and can be any other TYPE. You can have a Tree of Users, a Tree of Cars, a Tree of Companies.

    In PHP, you can make a Collection class (Laravel and many other frameworks provide these), but there is no way to type-check that Collection other than by making separate classes for each subtype you expect to use.
  • 4
  • 6
    I don't agree that jvm languages are slower to develop in (Java, kotlin especially) but other than that all the ++ :)
  • 6
    FB runs on PHP (More of Hack). Instagram runs on Django.
  • 2
    Damn wtf even. o.O can't check if the collection is valid properly, or at least it looks like I can't
    (note: cellphone shit)

    if($BaseCollection instanceof Collection):
    $CheckColl = $BaseCollectionVar->every(function ($value, $key) {
    return instanceOf $value App\Model;
    });

    return $BaseCollection == $CheckColl;
    endif;

    throw exc...
  • 0
    @xewl Yes, that cellphone typing is shit, switched my instanceof already ... ffs
  • 2
    @XiovV I easily get to 1K at times, Millions would be fucked up though
  • 2
    @bittersweet for the case of Collection of Stuff, how about this:

    - use "array" as parameter type hinting;
    - in the constructor / setter use array_filter() on the provided argument holding the collection, with an anonymous function as callback, that does "return $item instanceof YourCollectionInterface;"

    So you've got your peace of mind about the collection items. You can take it a step further, throw an appropriate exception if the collection contains something unexpected.

    Regarding type hinting a return value representing a collection of items, wouldn't ": YourItemClass[]" do the trick?
  • 3
    Or better yet, ": YourItemInterface[]", if you want to return either Apples or Oranges, both implementing the FruitInterface, for the sake of example?

    Posted this as the previous comment got to old to edit
  • 1
    For laravel I agreed. 😨
  • 6
    Now that's a PHP rant I can agree on. Don't get me wrong. I love PHP, but there are some things still missing. Being able to give a function void as returntype only came in PHP 7.2 and we're still not able to have something like

    Class Foo {
    public function find(int $id): ?Bar { ... }

    public function find(Baz $id): ?Bar { return $this->find($id->getBarId()); }
    }

    Sure we could just decide on not giving the parameter a type and handle it in a if/else block within find, but still not optimal. Plus as stated before, we can't say we either expect Foo or Bar, unless they share a super class or interface (which makes sense in most cases to have, though), or check on a collection (or array) containing values of a certain type (unless we loop over the whole array in every function using it and throwing errors there...)
  • 3
    @yangshun Instagram, despite it's user base, is fairly simple. Facebook kind of proves my point... They had to invent their own PHP flavor, plus they actually do a lot of their smart stuff such as abuse detection in Haskell.
  • 3
    @wannabee

    : Type[ ] with the brackets is not valid PHP. It's valid for Docblocks, but the interpreter doesn't care about those, only the IDE and some tools.
  • 0
    @bittersweet oh, ok, i have to admit that i pulled that out of my theoretical ass, i have confused the phpdoc with return type hinting (I'm pretty new to php7+).

    But might work to hint in the @return phpdoc and hint the return type to : ?array. Or, wrap the collection in a specialized iterable object, which would have the current() method hinted to the YourItemInterface, to allow both Pear and Apple implementing the interface.

    Uh, my mind starts melting, it's hard to explain things in written form 😁
  • 1
    @wannabee Interfaces do not represent ADTs properly though. It doesn't stop anyone from returning a strawberry. You can use a lot of trickery with final classes and immutability checking in constructors (see phunkie/ADT), but the syntax becomes verbose.

    And that's just for an Either/Sum type.

    I feel the problem isn't even so much that it can't be done in PHP, but that a language like PHP doesn't require it.
  • 2
    @wannabee Type hints in PHP are always "one level deep".

    To see what depth means, check this:
    http://learnyouahaskell.com/making-...

    You can typehint something like "Either (int -> int) [(int,int)] -> Bool" — A function which takes Either a function which maps from int to int, or a list of int,int tuples, and returns a Boolean.

    In Haskell, symbols like ">" or "+" are not operators, they're functions. You can define a playing card type for example, which implements comparison functions, so you can define Ace as low or high, or you could use color + color to mix colors.

    You can include these typeclasses in signatures, to define a generic function which takes any type as long as it's sortable (orderable, it implements >, <, ==), foldable/reducable, or bounded.

    You can typehint a function which takes a "graph of humans where all edges must be of parent/child type", or a "voxel world containing only blocks of either air or dirt".

    Compared to that, "? Int[ ]" return type feels underwhelming.
  • 1
    @bittersweet hey, new stuff to learn! Thanks for that!

    You've just triggered me, I keep thinking of ways or even some hacks that may solve your issue. But i have a meeting in a bit, be back later
  • 2
    @XiovV Not 5M loc, rather 28M lines of PHP. That excludes 4M lines of python, and 2M lines of JS 😂

    It does... a lot. Scheduling employees, predicting tourist density in cities, determining which university studies should receive subsidies, checking where new houses should be built, making a guess at what wages are competitive, interpreting labor and tax laws to calculate incentives, etc.

    Just one use case: The system will starts training and recruiting bartenders based on festival scheduling and weather prediction — but also determine which teachers a college should hire based on high school exit exams. We use hundreds of APIs, and about a dozen internal databases.

    Most of the magic happens in Python/Tensorflow, but PHP is the fat bridge in between everything... too fat.
  • 3
    @bittersweet about facebook having to invent their own php, to be fair, they started in a pre php7 world and wanted to get a JIT in there too, while the php world focused on speed and memory footprint, so it kind of where different goals.
  • 2
    That "DO NOT EEVER use Laravel for large enterprise applications" reached my heart. I feel you.
  • 2
    @SauceBoss Yes of course there's caching, we use materialized views for statistics, redis, nested caching of views, etc.

    I'm not a big fan of caching as a blanket solution though, because it comes with a lot of maintenance and infra as well. You might get bugs because you lost your single truth, you get complains from users because they see phantom data.
  • 0
    That's a lot of bullshit strawmans, I've worked with plenty of large PHP sites. They're absolutely fine if your code isn't a pile of junk, stop cutting corners. Hell Facebook is made with PHP which is ridiculously huge.
  • 2
    @Shardj

    Facebook is not "made with PHP".

    Their chat is made with Erlang. Their post filtering/timeline selection runs off Haxe which is a DSL templating language in Haskell. They use C++ proxygen/boost a lot for serving content. Most of the backend was originally written in PHP but compiled to C++ using HipHop, later evolved into HHVM with the Hack PHP dialect.

    You know what Hack offers? Not just PHP7 typehints, but a bunch of strongly typed predefined ADTs and typechecked generics.

    Also, the comment "stop cutting corners"... PHP is like trying to cut a perfect circle without a drawing compass.

    I consider myself a "senior", I stick to a whole bunch of style rules, I have my IDE set up to catch most of the silly mistakes, we use CI/CD with a whole bunch of tooling.
  • 2
    @Shardj

    But eventually the pool of senior devs dries up, you hire a few "good enough" devs to develop a bunch of features. And even with perfect devs, if the language doesn't FORCE reasonably good code... it just is not going to happen. At least not at scale, and not reliable enough.

    Why do you think companies invested time into Typescript/Flow/Dart, replacing Python with Go, C with Rust, etc?

    I'm not saying JS, Python, PHP, Ruby, etc are *bad*.

    They're just kind of a fretsaw: great for small to medium projects, amazing for agility, but when you have to cut a straight line through a floorplate you're better off with a tablesaw.
  • 2
    @Shardj

    In my opinion, the solution is also "easy".

    Port away the most critical aspects of the system to safer languages, move the quickly evolving parts to more dynamic langauges, break up the PHP codebase into microservices, with each project team choosing their language & tools to suit their needs.

    ... Just have to convince others of this strategy.
  • 3
    To be honest, we could also talk about monolithical vs micro services. To get a startup, maybee even a medium to large business runnig, monoliths work great.
    If people don't know what they are doing with microservices something like the "bell" desaster happens...
    Deployed the right way however they are awesome. As an example, look at what infrastructure Netflix built (not saying this is how it should be done and every body should do it (this way)!)
  • 2
    @Wack

    Yeah indeed.

    I think the pain is in the switch.

    At some point you notice that many of the tools which gave you an edge as a startup, do not work when you grow up.

    And I do understand why companies don't like the idea of rebuilding/switching/porting... it costs valuable time, and if you get stuck in an in-between state you're fucked.

    Microservices are amazing for maintainability and scalability, but require a lot of extra infrastructure and devops work to keep everything running like clockwork.

    I do think it's a must to split things off the monolith though, start chipping chunks away.

    Even just within our PHP codebase I've started porting a lot of our code to separate repositories, to be installed through composer, as it forces us to rethink the responsibilities of a module, the reusability and architecture, and allows for much better parallelization of testing pipelines.
Add Comment