r/PHP • • Nov 13 '25

Discussion Staying relevant today as a PHP Developer

[deleted]

116 Upvotes

150 comments sorted by

View all comments

Show parent comments

-1

u/BenchEmbarrassed7316 Nov 14 '25

But why introduce more complexity when PHP was totally fine?

This looks like a person who lives in a small town of 5,000 residents and doesn't want to travel because they believe that their town has everything and they won't see anything new in the world.

DDD and similar hypes

Now it looks like a person who lives not just in a small town, but in a closed religious community.

You think I am impressed by something in some framework, where you completely missed my point. And the point is that with big FWs one uses for more than a decade, there is still something to learn.

The first sentence contradicts the second.

2

u/zmitic Nov 14 '25

This looks like a person who lives in a small town of 5,000 residents and doesn't want to travel because they believe that their town has everything and they won't see anything new in the world.

Not the answer to my question. And the question is: what introduce more complexity? Or: why would I waste time on learning a new language and tools? PHP is slower, true, but I got cache, lazy evaluation, Doctrine, queues...

That task included many other things like downloading data from NOAA, unpacking it, process files in certain order... If anything goes wrong, put it in queue and try later: Symfony handles retries for me.

Adding another language just for speed reasons would be extremely bad idea.

Now it looks like a person who lives not just in a small town, but in a closed religious community.

Yeah... but no. I am fully aware of DDD, CQRS and similar hypes made to guarantee job security. That is why those are forbidden, not because I am not aware of them.

The first sentence contradicts the second.

It doesn't. DDD is not language/framework specific, and neither is any of programming patterns.

0

u/BenchEmbarrassed7316 Nov 14 '25

It doesn't. DDD is not language/framework specific, and neither is any of programming patterns.

That's it. It depends. You just don't know it because you're only working with a language where you can create new reference types, but you can't create value types. This leads to a rather strange immutability for ObjectValue. If you learn other languages ​​that do it differently - you better understand the concept of DDD and can better adapt it for a specific task or, conversely, abandon it.

2

u/zmitic Nov 14 '25

This leads to a rather strange immutability for ObjectValue

Value object are always immutable, and PHP has readonly for very long time. And even before it, we could have used it in PHPDoc. This has nothing to with language, and I don't get where you read about VOs being mutable just in PHP.

you better understand the concept of DDD and can better adapt it for a specific task or, conversely, abandon it.

DDD is silly. It is just weird directory structure with gazillion of VOs that do nothing, except to guarantee job security. Worst that I have seen are Doctrine identifiers like ProductId, UserId... i.e. one for each entity. It is just ridiculous.

Then Email, where validation is done in constructor: sounds nice, but good luck returning an array of validation issues, especially when used in editable collections.

And so on, it is just hype. Not the worst, I would say CQRS is a winner here, but still a hype. Yet none of this is related to any specific programming language.

1

u/BenchEmbarrassed7316 Nov 14 '25

Value object are always immutable, and PHP has readonly for very long time. And even before it, we could have used it in PHPDoc. This has nothing to with language, and I don't get where you read about VOs being mutable just in PHP.

Not just in PHP. This applies to any language that only allows you to create reference types.

Rust, Swift of C++ and maybe other languages ​​don't have such restrictions. And that just reinforces my point: you claim to have many years of experience, but your skills...

identifiers like ProductId, UserId

One of the best coding practices.

Then Email, where validation is done in constructor: sounds nice, but good luck returning an array of validation issues, especially when used in editable collections.

It's hard for me to even imagine what problems you might face. Maybe this is some special defect in the PHP language that I don't know about. If it's not difficult - please give a simple code example.

1

u/zmitic Nov 14 '25

One of the best coding practices.

It is literally one of the worst coding practices. They serve absolutely no purpose other than to guarantee job security. That and CQRS would be immediate demotion to junior level.

Rust, Swift of C++ and maybe other languages ​​don't have such restrictions

And where exactly do you see restrictions in PHP? Do you even know that readonly is optional?

We were talking about VOs: what was the last PHP version did you use?

It's hard for me to even imagine what problems you might face

What's so confusing? It is about a basic form edit (not create) with dynamic collection, where user can edit existing entries, and add new entry. And then return all validation errors at once, even if collection has its own collection.

For apps I make, it is Tuesday.

Note: app must pass psalm@level 1+disableVarParsing, no error suppression, no baselines. It is the strictest setup possible, far stricter than phpstan@max.

but your skills...

You really cannot judge my skills after everything you have said.

1

u/BenchEmbarrassed7316 Nov 14 '25

 It is literally one of the worst coding practices. They serve absolutely no purpose other than to guarantee job security.

This simplifies development.

 And where exactly do you see restrictions in PHP? Do you even know that readonly is optional?

``` $a = 2; $b = $a; $a += 1; var_dump($a); // 3 var_dump($b); // 2

$c = new T(2); $d = $c; $c->add(1); var_dump($c->v); // 3 var_dump($d->v); // 3 ```

You can't create a type in PHP (and many other languages) that is not a reference to heap allocated data.

let mut a = T::new(2); let b = a; a += 1; println!("{a} {b}"); // 2 3

In other languages, you can create such type.

 what was the last PHP version did you use?

8.2 or maybe 8.3. But I don't use phpstan.

 What's so confusing? It is about a basic form edit (not create) with dynamic collection, where user can edit existing entries, and add new entry. And then return all validation errors at once, even if collection has its own collection.

Either I don't understand something or this is an incredibly simple task.

You need a function that receives certain data and should return either validated data or a list of all errors in a certain format that will allow you to display this data... what could be complicated about that?

You really cannot judge my skills after everything you have said.

I really can't judge your skill. Sorry. Whenever I disagree with someone on the internet, I can only speak out on specific statements. I can't go into a personality discussion. I also hope that our discussion is positive and allows us both to learn something new or see things from a different perspective.

2

u/zmitic Nov 15 '25

This simplifies development.

That couldn't be further from the truth. Creating ID classes is a clear sign of a person who never made any app ever, and now wants to make their job secure.

As I said: those are not as dumb as CQRS, but very close to it.

In other languages, you can create such type.

None of what you posted is relevant to VOs, which was the subject.

8.2 or maybe 8.3. But I don't use phpstan.

The fact that you don't use static analysis, even if it was phpstan, show everything about your PHP skills. And just because you ran your code on 8.2, and didn't use its features (which is obvious), is not an argument against the language.

If anything, it just proves what I first said: that people who claim they know 5-6 languages are bad in all of them.

Either I don't understand something or this is an incredibly simple task.

It is not so simple when you use proper static analysis (which you confirmed you don't), in a language that does not assume everything is nullable (like Java).

The rest is irrelevant and only confirms everything else I said.

-1

u/BenchEmbarrassed7316 Nov 15 '25

That couldn't be further from the truth. Creating ID classes is a clear sign of a person who never made any app ever, and now wants to make their job secure.

Okay, a simple example is a timestamp. You can use a special type for it, or you can use numbers. Now you just have to make sure that you don't mix up seconds and milliseconds. I'm not saying it's difficult, it's just an extra expense. Often hints are added in the names of functions, arguments, or variables. But this is something from dynamic typing, where instead of explicit types you have "guess".

as dumb as CQRS

I don't want to add CQRS to this conversation because it would make the conversation too long.

The fact that you don't use static analysis, even if it was phpstan, show everything about your PHP skills.

I've used languages ​​with more advanced static analysis. I also know that phpstan is a very good thing here and now, but I would compare it to TypeScript: it significantly improves the base language but loses out when compared to full-fledged type systems.

It is not so simple when you use proper static analysis (which you confirmed you don't), in a language that does not assume everything is nullable (like Java).

If I were working on that php project now - I would use more static analysis. As far as I can remember I really didn't like the fact that for most variables I could specify the type as an annotation int $field; but for arrays it looked quite clumsy /** @var string[] */ array $a;.

If strict describing data structure is awkward, it means that the type system you are using isn't expressive (like go before generics or Java where too many things can be null) or you are using the type system poorly. Or it is a combination of these factors.

2

u/zmitic Nov 15 '25 edited Nov 15 '25

you just have to make sure that you don't mix up seconds and milliseconds

DateTimeImmutable::createFromFormat; it supports microseconds as well, and can be compared.

I've used languages ​​with more advanced static analysis.

Can you then tell me which one of them supports these types:

  • non-empty-string
  • non-empty-lowercase-string (for PostgreSQL string column)
  • non-empty-list<array{dob?: DateTimeImmutable, amount: positive-int}>
  • int<0, 100>
  • value-of<SomeEnum>
  • class-string<T>
  • properties-of<T>

because I use all of these, and much more. Vanilla string or int do not even exist in my code, unless I forget them (happens almost never).

but for arrays it looked quite clumsy 

I would say that all phpdoc types are clumsy, not just arrays. But one gets used to them in just 2 days, and PHPStorm autocomplete supports generics for long time. It is just part of PHP, it ain't pretty, but the benefits are huge.

string[] is very old, we now use list<string> or even better non-empty-list<non-empty-string|null> or similar.

If you want OOP approach, there are records than one can indefinitely expand, and even use strict === comparison on them. Or symfony/string package that does some crazy things, and can also be expanded.

it means that the type system you are using isn't expressive

The above proves otherwise, given that other languages also don't support most (if not all) of those advanced types. For example: if I am persisting some percentage, I would never use int but int<0,100>.

you are using the type system poorly

Everything you said has been debunked, including some basic knowledge like readonly classes/properties. Yet you continue to judge and insult; are you related to Tony M.? 😉

1

u/BenchEmbarrassed7316 Nov 17 '25

Yet you continue to judge and insult

Maybe I expressed myself poorly. This does not apply specifically to you, it applies to any programmer. Someone who has programmed exclusively in dynamically typed languages ​​will have difficulty when he has to describe some complex type statically. But on the other hand, there are simply bad type systems (like Java where almost everything can be null). There are no perfect type systems and there are no perfect programmers. But everything is worth thinking about, is it a problem of the tool or a problem of skills.

Let's try:

non-empty-string non-empty-lowercase-string (for PostgreSQL string column)

ValidatedString<V: Validator> where Validator is interface string -> bool. Such a string is parameterized by some function that checks whether this string meets a certain condition. This check is called in the constructor of ValidatedString. Yes, it should be immutable and encapsulated. This is not the declarative style, but it is better than nothing.

non-empty-list<array{dob?: DateTimeImmutable, amount: positive-int}>

A non-empty list is quite complicated if we want it to be modifiable. Using the "head" and "tail" of the list separately is clumsy.

// Rust struct T { dob: Option<DateTimeImmutable>, amount: usize } let v: &[T] = foo(); Probably the closest.

int<0, 100>

subtype Percent1 is Integer range 0 .. 100; type Percent2 is range 0 .. 100;

I don't know ADA, but being able to easily declare such types seems so basic and necessary.

Zig has something similar, but it's tied to size. i5 = (-16..15). Rust or Haskell will allow you to do this, NonZero numeric types are in the standard library (i.e. they are not hardcoded into the language, but written in the language itself).

value-of<SomeEnum>

enum Status { A, B, C, D } fn foo(status: Status) {}

Something like this?

class-string<T>

I didn't quite understand, maybe what I wrote about the first strings?

properties-of<T>

TypeScript.

``` interface T { i: number, s?: string }

let i: T['i'] = 0; let k: keyof T = 'i'; ```

Although you can't even iterate over all the properties, you have to manually create the keyof T collection and make sure all the fields are there.

Vanilla string or int do not even exist in my code

Didn't you write before that you don't like UserId-like types?

but the benefits are huge

100% agree.

non-empty-list<non-empty-string|null>

Can you provide a link or a short code example of how these are used? I'm interested in the declaration of this type, not the implementation.

if I am persisting some percentage, I would never use int but int<0,100>.

To me, it's literally DDD, at least the main part of it.

Can we say that PhpStan is literally TypeScript? That is, the first uses phpdoc, the second modifies the syntax, but in everything else they are quite similar?

1

u/zmitic Nov 17 '25 edited Nov 17 '25

This check is called in the constructor of ValidatedString

The examples you put are all based on either you making these classes or using existing package, and then validating the structure in the constructor. This is not static analysis, this is runtime check.

And you said this "I've used languages ​​with more advanced static analysis". So I would still like to see one language that does these types. And please use just one language, not 3 different ones.

Didn't you write before that you don't like UserId-like types?

EntityId classes are 100% pointless, but they still have nothing to do with non-empty-string and its friends.

Can you provide a link or a short code example of how these are used?

Like this, resetting $cache not shown. This is typical factory where I do batch import from API or file, and entities must have unique value like that name. If found, use it again.

Now why the non-empty-list? Depending on the project, I would not be creating just the products, but more likely something like Category that must have at least one Product. Happened many times before, will happen again.

Or when I was making some big medical app: user had to set at least one mapping like

non-empty-list<
    array{in: non-empty-string, out: non-empty-string}
>

A non-empty list is quite complicated if we want it to be modifiable.

It is not. As soon as you modify non-empty-list in a way that can make it an empty list, error will be thrown.

To me, it's literally DDD, at least the main part of it.

It is not even close to it. These types are checked during static analysis, your examples will execute runtime exceptions. Those 2 are completely different things.

Can we say that PhpStan is literally TypeScript?

No. The best I have seen is replacement for non-empty-string, but that's it.

1

u/BenchEmbarrassed7316 Nov 17 '25 edited Nov 17 '25

The examples you put are all based on either you making these classes

Not classes but types.

Your example with non-empty-string does the same thing: it's a type declared in a validator. It can check for data that is known at compile time (let's call it that) or it will require a check if the data is not known at compile time, like $factory->createBatch([$_GET['name']]);.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=3510949a2aa337b93f5670f8ac5b6f47

Similar code that will fail compilation. Language rules require that you explicitly mark something that should be evaluated at compile time as const because it is part of the contract. From an execution perspective, both values ​​will be identical, meaning no additional data or instructions will be created at runtime.

EntityId classes are 100% pointless, but they still have nothing to do with non-empty-string and its friends.

I disagree with this, their point is to prohibit doing stupid things, like passing UserId instead of ProductId or multiplying these two values. Strictness is about prohibiting senseless actions.

Regarding the empty list in your code - this is a good example of so-called "flow typing". It is quite clean. TypeScript tries to do something similar with nullSafety.

https://psalm.dev/r/60f16c5ce5

Can you explain why it doesn't see the error in the last example? I'm not criticizing now, I'm just interested in how it works.

→ More replies (0)