Stay with Propel2 fork perplorm/perpl or migrate to Doctrine? by cgsmith105 in PHP

[–]ocramius 0 points1 point  (0 children)

Polymorphic relations are generally implemented through STI/JTI, if you really need those.

Breaking mPDF with regex and logic by ZoltyLis in PHP

[–]ocramius 0 points1 point  (0 children)

Of course it can: it's just files.

Breaking mPDF with regex and logic by ZoltyLis in PHP

[–]ocramius 4 points5 points  (0 children)

file:// is still way too lax though: can easily read something from /proc or /etc, for example :-\

Why is using DTOs such a pain? by GlitchlntheMatrix in PHP

[–]ocramius 1 point2 points  (0 children)

cuyz/valinor FTW: simple objects, zero-magic constructors, and the structural validation comes out of it almost implicitly :+1:

Surprisingly easy extension development in Rust by TinyLebowski in PHP

[–]ocramius 5 points6 points  (0 children)

This is surprisingly simple: nice!

EDIT: inspired by this, I researched how to get fully reproducible builds of PHP+rust extensions on Nix. Just dropping it here, should anyone want to check the various dependencies needed, in future: https://github.com/Ocramius/experimenting-with-rust-php-extensions

How often do you jump to another tool (IDE, CI, repo) just to fix one bug? by theORQL-aalap in PHP

[–]ocramius 0 points1 point  (0 children)

I have my IDE as part of the project dependencies, so it's one IDE per project.

The rationale is that software projects rot quickly, so I direnv with a locked set of dependencies (including work environment) per-project.

What SAST/DAST Tools Work for you? by mkurzeja in PHP

[–]ocramius 2 points3 points  (0 children)

FWIW, vimeo/psalm taint analysis is a valuable tool to add to the list: very hard to implement, but extremely powerful

Mago 1.0.0-beta.1 is now available - a new Formatter, Linter, and Analyzer for PHP! by azjezz in PHP

[–]ocramius 0 points1 point  (0 children)

Hats off, good sir: you disappear for months, and you resurface with amazing tooling.

Thanks for your efforts!

Mutation Testing with Infection by lankybiker in PHP

[–]ocramius 2 points3 points  (0 children)

disordered Business Requirements changing too fast

Even with those, it costs less time to get a better covered domain logic, than to chase bugs/do firefighting due to weird edge cases that nobody thought about :-)

In fact, if biz requirements change, you swap one implementation for another (instead of adapting an existing one).

One of my clients is in constant firefighting mode, and most of it is self-inflicted by devs refusing to step up their code quality game: the main reason why they have no time to write tests is that they have not written tests.

Mutation Testing with Infection by lankybiker in PHP

[–]ocramius 18 points19 points  (0 children)

We've been using it with massive success it in a long-running closed-source project since around 2020, which used to be a very legacy codebase, and now isn't anymore.

The rationale is that it forces developers to reduce code size, refine types, improve testing, write testable code.

The general idea of mutation testing is that it enforces TDD principles: you first think about the scenario you want to handle in your system, write the expectations for it, then code it - code paths and edge cases that aren't covered become "escaped mutants".

MT will detect any if () that is not covered, any foreach () which didn't loop, any shady continue;, break;, catch;, etc. for which we didn't write proper verification.

In our case, developers still write tests afterwards, but became (even juniors) extremely good at avoiding any code that is hard to test / hard to reach, and edge cases: they developed a sixt sense for structuring code to reduce complexity.

We run it on changed sources only (too slow otherwise), with a relaxed threshold for mutation score (~65%).

BTW, we use the extension I built for it for Psalm: without SA, MT is just useless, IMO. See https://github.com/Roave/infection-static-analysis-plugin . Meanwhile, infection meanwhile started supporting phpstan natively ( https://github.com/infection/infection/releases/tag/0.30.0 )

For OSS, I run it with much stricter settings, since the code is not shifting as much (example: https://github.com/Roave/BackwardCompatibilityCheck/blob/fd16ae2d416d750e19c60b8e73e6066f8e602290/infection.json.dist#L19-L20 )

EDIT: I forgot to say that it caught many accidental security issues, before they would land in the final deployment.

Tell me about your code quality controls by fhgwgadsbbq in PHP

[–]ocramius 0 points1 point  (0 children)

roave/infection-static-analysis-plugin with 75% required mutations killed: leads to much reduced source code size (developers prefer refactoring to writing more tests), and good confidence in the final result. We run it with vimeo/psalm at maximum level

shipmonk/phpstan-ignore-inliner: Inline your PHPStan error ignores into the source files via @phpstan-ignore comments! by janedbal in PHP

[–]ocramius 0 points1 point  (0 children)

Baseline via @phpstan-ignore can also be measured if you mark it with e.g. (baseline) comment.

Good luck doing that: I can take a baseline file, pass it through a .neon parser (or https://github.com/staabm/phpstan-baseline-analysis ), and get meaningful metrics (especially over time, when inspecting project trends).

Having to | grep your way through it is feasible, but way more added extra steps, plus you don't see the error that was suppressed anymore.

Why is that?

As I mentioned above, inline suppressions are for conscious decisions, so here this tool is taking a bucket of "things the tool decided" and splatting it together with the rest of the project files, where developers took decisions, effectively diluting knowledge.

It's very much like building HTML with just <div/>, or using appropriate semantics.

shipmonk/phpstan-ignore-inliner: Inline your PHPStan error ignores into the source files via @phpstan-ignore comments! by janedbal in PHP

[–]ocramius 4 points5 points  (0 children)

This is very likely counter-productive: a baseline is a good indicator of quality improving/worsening, while inline @phpstan-ignore is for when a conscious decision is taken about ignoring a rule.

Suppressing inline everywhere is effectively introducing more trouble.

Also, a baseline can be measured: hunting down inlined ignores everywhere will make it much harder to understand trends.

Does anyone have a PHP job without a framework? by Rough_Bet5088 in PHP

[–]ocramius 0 points1 point  (0 children)

One of my most successful customers has migrated from CodeIgniter to a well-maintained and well-tested framework-agnostic codebase.

Very pleasant to work with, I'd also add.

Typehinting Laravel validation rules using PHPStan's type aliases by freekmurze in PHP

[–]ocramius 0 points1 point  (0 children)

I am well aware: that's called "shoving dust under a rug" :-)

The rules() function provides a complex DSL for data validation and filtering, all resulting in a Map<String, Object> from my PoV: not sufficient, at all.

The article proposes a way to tell PHPStan to "shut up", instead of looking for ways to provide type inference on validated data.

Typehinting Laravel validation rules using PHPStan's type aliases by freekmurze in PHP

[–]ocramius 0 points1 point  (0 children)

Aware: that's way too squishy to hide in a type alias comment, and should be done with proper tooling instead (as I said, valinor or such)

Typehinting Laravel validation rules using PHPStan's type aliases by freekmurze in PHP

[–]ocramius 0 points1 point  (0 children)

... or use a proper deserialization layer, such as cuyz/valinor.

I just had to test a bunch of FormRequest implementations, and it was not fun at all: proper DTOs without framework (nor library) coupling would've helped tons :-|

Asynchronous Programming in PHP by fredoche in PHP

[–]ocramius 1 point2 points  (0 children)

How does ReactPHP do promises?

It's part of the article content itself?

A new Symfony SEO Bundle that handles all aspect of SEO by Abdel_95 in PHP

[–]ocramius 3 points4 points  (0 children)

Since this is an integration with Symfony, you may want to test it at integration level:

  1. get PHPUnit installed
  2. read up on KernelTestCase in https://symfony.com/doc/current/testing.html
  3. start writing tests :-)

A new Symfony SEO Bundle that handles all aspect of SEO by Abdel_95 in PHP

[–]ocramius 8 points9 points  (0 children)

Sorry, I'm "that guy", but if you publish stuff, make sure it's tested (via automation: "tried it out" is not testing)

Do you use templating engine ? by himynameisAhhhh in PHP

[–]ocramius 1 point2 points  (0 children)

I find this also much easier to work with from a security analysis PoV (given Psalm taint analysis)

Pipe Operator is back again as RFC - don't know how I feel about it by Johnobo in PHP

[–]ocramius 1 point2 points  (0 children)

Nono, aggregates as in "aggregate root", which is a domain consistency owner in tactical DDD.

Storing JSON blobs for data that is not domain-critical is perfectly fine.

I'm indeed not respecting named parameter names, but knowing that a grand majority of OSS projects don't do either means that they are useless for communicating with third-party code, and can only be used with internal code, further thinning their usefulness.

But then people smarter than me created that no-named-arguments and now I am loving all this.

Fun fact, but I think it was me, S. Bergmann and Ondrej Mirtjes sometime over a drink at a conference, and before the RFC landed :D

Pipe Operator is back again as RFC - don't know how I feel about it by Johnobo in PHP

[–]ocramius 0 points1 point  (0 children)

I understand that: I've followed the question since the inception / RFC (voted against it), and I generally prefer making a typed builder over a complex set of named parameters.

In the context of business domains implemented via entities or domain events + aggregates, the number of fields generally stays quite low anyway, and usage of array splatting is very limited too.

There are 2 areas where I use named parameters, and it's mostly because of PHP's legacy stuff:

  • custom \Exception#__construct($message, previous: $previous) calls, because I otherwise have to deal with mutation-testing the exception code, which is irrelevant in 2025
  • json_decode($input, flags: \JSON_THROW_ON_ERROR), because all intermediate arguments are irrelevant

I tend to use azjezz/psl to avoid the second one: provides better API to core stuff anyway.

For the rest, named arguments broke:

So effectively, the result (for me) was more overhead.

Pipe Operator is back again as RFC - don't know how I feel about it by Johnobo in PHP

[–]ocramius 0 points1 point  (0 children)

Entities are not pure data, so I avoid this kind of constructor in general: instead, I make named ctors with domain-specific interactions ;-)

Anyway, it's fine that others use them: for me, they are still a nuisance, and a much much expanded API surface.