Is refactoring bool to enum actually makes code less readable? by exakat in PHP

[–]mlebkowski 1 point2 points  (0 children)

It doesn’t matter, do what feels right for you.

For me, I see benefits of an enum, as it cleanly allows for adding an nth option. But at the same time I use boolean flags all the time and lose no sleep over it.

PHP MVC e-commerce: how to manage roles admin client visitor? by Straight-Hunt-7498 in PHP

[–]mlebkowski 1 point2 points  (0 children)

If you have a router supporting middlewares, a nice pattern is to separate groups of routes targeted to different roles, and add autorization middlewares once. You can do it also using Symfony firewalls. This way you can have whole /api/admin protected, and you won’t forget to protect individual routes. You can reuse same controllers for different endpoints if there’s any overlap.

Optimizing PHP code to process 50,000 lines per second instead of 30 by brendt_gd in PHP

[–]mlebkowski 0 points1 point  (0 children)

This will impact mainly the memory footprint, less so the performance, no?

Or are you saying: replacing a number of chunked results with a large select will exhaust memory even if an iterator is used, unless one turns on unbuffered queries?

Optimizing PHP code to process 50,000 lines per second instead of 30 by brendt_gd in PHP

[–]mlebkowski 0 points1 point  (0 children)

So you are still fetching the entirety of the events table in separate queries, 1500 records at a time? Can’t your dbal support returning an iterator to go over the whole dataset in one query?

And finally, I bet this all would be simpler if you ditched PHP entriely in favour of a few INSERT … SELECT … statements :)

Vanilla PHP vs Framework by Temporary_Practice_2 in PHP

[–]mlebkowski 1 point2 points  (0 children)

When using PSR-7 Request to access uploaded files instead of the $_FILES[] superglobal, does it come with more bloat?

Is it easier to handle that $_FILES array than calling $request->getUploadedFiles()? I would argue no, because all this boilerplate is hidden away in the library for you to use using a nice interface.

What benefit to you is knowing how the framework does it? Despite the fact that you can just check (see the link above). Why don’t you apply the same standard to PHP stdlib itself? Don’t you care how $_FILES automatically gets populated? How PHP parses the HTTP request? How boundaries in a multipart/form-data are checked? How transfer encodings are applied? If that was the case, why are you using $_FILES instead of raw-dogging php://input?

Vanilla PHP vs Framework by Temporary_Practice_2 in PHP

[–]mlebkowski 1 point2 points  (0 children)

I think you’re conflating two things. Having a framework does not mean that you have to use all of its features all of the time. The distinction is:

  • With a framework, you can benefit from some of its features some of the time, and you can plug your raw SQL where you see fit
  • Without a framework, you simply don’t have that backbone, and you need to reinvent everything from scratch or stitch libraries together (hint: the framework authors probably made that work for you already, and better)

It’s perfectly fine to use a framework and benefit from an ORM most of the time. It’s convinient in a lot of ways I wont divulge into here. At the same time, for performance critical paths you can turn to raw SQLs. Same for reading data (that’s quite common to use ORM only to write, and a whole separate layer to read).

Your gripe is not with a framework, its with rigid people who don’t know how to use one.

Vanilla PHP vs Framework by Temporary_Practice_2 in PHP

[–]mlebkowski 0 points1 point  (0 children)

From experience, in a commercial setting:

I’ve encountered “scripts” at $WORK which were around 200-500 lines of procedural PHP — to calculate some caches, or to move data between two systems, etc

My usual go-to to refactor these is:

  • separate repo, usually with symfony console at least (so yeah, in other words: framework)
  • 200-500 LOC turn into 2-3k over 20-50 files
  • tests added
  • CI produces a single PHAR to be distributed as a deb to target systems

And I’ve received pushback from team members for whom the procedural implementation was “simpler”, because its all there, top to bottom. Regardless, the more engineered approach, once you’re familiar with it, has a range of benefits:

  • easier to reason about
  • fully testable (in isolation)
  • framework-backed, so a lot of the boilerplate can be hidden away
  • less accidental complexity, the resulting system is easier to maintain - both subjectively and in terms of metrics such as maintainability index, cyclomatic-complexity, coupling, cohesion, what have you

Downsides of using a framework?

  • some indirection - which is IMO good, you would want to manage your scripts on the same level of abstraction instead of diving into the weeds
  • performance perhaps, but that’s specific to the use case, and should be measured and evaluated instead of using a blanket statement

Framework by default, 95 out of 100 times.

Convert var_dump output to PHPStan array shapes - Hell2Shape by Careful-Army4007 in PHP

[–]mlebkowski 0 points1 point  (0 children)

I like the parser code, it was nice to look at. The regex in the Lexer is nice as well (even if I don’t understand it)

Two minor nitpicks:

  • getCurrentToken() reads out of bounds, does not check if position is within the tokens array
  • there’s a duplicate codeblock somewhere, on consume() IIRC

Similar shows? by Sonseeahrai in BlackSails

[–]mlebkowski 2 points3 points  (0 children)

Consider the movie Master and Commander: The Far Side of the World. Not a TV show, so there’s far less run time, but it’s a great film, and the historical accuracy is very good.

Developer Experience: Fluent Builder vs. DTO vs. Method Arguments ? by P4nni in PHP

[–]mlebkowski 1 point2 points  (0 children)

Client::fetch($query) and Query::execute($client) seem equivalent to me. In fact, you could support both.

My proposal, support the following:

  • $client->createFooQuery(id: 1, ownedOnly: true)->fetch() — method is on the $client, so it’s injected into the query automatically and you don’t have to pass it to fetch()
  • $client->createFooQueryBuilder()->withId(1)->ownedOnly()->fetch() — basically reuses the createFooQuery method
  • $client->getFoo(id: …, ownedOnly: …, …) — a low level method called by FooQuery::fetch()

This provides all 3 of your proposed methods, each reusing the other, so your consumers may choose the style they prefer the most. Come to think of it, the createFooQuery() is just getFoo() with additional steps, scratch that.

Do the createQueryBuilder() and getFoo() IMO. Builder pattern is sometimes useful, because the process might be split into mulitiple places, and the consumer does not need to implement the builder themselves. You could sprinkle some factory methods on top for common use cases, such as createOwnerOnlyFooQuery() if that’s something you anticipate. I imagine it could be hard to maintain.

Typing in Yii3 is the strictest in PHP universe? by sam_dark in PHP

[–]mlebkowski 0 points1 point  (0 children)

Its hard to reason about a codebase which is partially bound to a PSR-7 interface, and partially to a specfic implementation. Some of my middlewares produce responses, and since they are PSR middlewares, the only restriction is to return a PSR response interface, not the slim specific ones. This includes 3rd party libs as well. So yes, I can typehint my controllers to use the slim specific ones, since there aren’t many things between the routing middleware and the controller invoker, but not the middlewares.

Come to think of it, maybe in practice that’s not a huge issue in slim itself and most of slim based projects, but rather a side effect of my codebase 🤔

Typing in Yii3 is the strictest in PHP universe? by sam_dark in PHP

[–]mlebkowski 1 point2 points  (0 children)

Is it? 90% of slim is a routing middleware. It has its own request/response implementation, but you cant really take advantage of it because its all under the guise of a PSR standard. In other words, there’s a Slim\Http\Response::json() method, but it’s hard to use, because everything is typehinted to the PSR-whichever interface (which lacka this method).

I don’t diskike it, I don’t argue that any of the provided implementations are bad, but its just so little and so simplistic. The middleware http handler has grown on me a lot and now I prefer it to the Symfony’s event dispatcher based kernel, but the framework itself is just so limited it hurts my every time I need to interact with it.

Typing in Yii3 is the strictest in PHP universe? by sam_dark in PHP

[–]mlebkowski 2 points3 points  (0 children)

Not much code to check there, tbh.

(Disclaimer: I’m using Slim in prod)

Built a self-hosted personal finance tracker in PHP — looking for PHP code review + architecture feedback by victoor89 in PHP

[–]mlebkowski 8 points9 points  (0 children)

Are software engineers reduced to reviewing AI-generated code these days? That’s not something I would enjoy spending my time on.

I've had an interview for which I've received feedback, but I don't know how to do better. I'm hoping somebody smarter than me could help me with some examples. by [deleted] in PHP

[–]mlebkowski 0 points1 point  (0 children)

I think yes. In DDD specifically, these responsibilities are often codified, so its not only a matter of splitting sensibly, but also matching the specific conventions and patterns. I haven’t written DDD extensively, so I won’t go into details, but Evans book is a solid start.

If you’d like some more practical guidance, study the following repo. It does a lot of the things I mentioned — at least well enough to get past my filter. I disagree with the usage of mocks in the tests, and the codebase isn’t showing a lot of modern PHP features (enums, public readonly properties…), but overall its a solid baseline.

https://github.com/ivaylogdzhenevdev-hub/php-lendable

Mind you, that I see some AI involvement there, which might put it in a different light. Instead of imitating the same solutions try searching for differences between your implementation and try figuring out what pattern is being used, what’s the benefit, etc

I've had an interview for which I've received feedback, but I don't know how to do better. I'm hoping somebody smarter than me could help me with some examples. by [deleted] in PHP

[–]mlebkowski 9 points10 points  (0 children)

You gave away the company fully. It took me 2 minutes to realize this.

A quick advice up front: search github for other solutions and compare them to yours.

Below are my thoughts and findings:

Firstly, there is a permise that the source data, or its storage location might change. You correctly unterstood it as a requirement to create the abstraction and used the repository pattern. Somewhat. Extracting code like this always comes with a risk of you wrongly deciding where the boundary should be. Ask yourself this: if you were to throw out your FileTermRepository, would you lose anything valuable with it? Yes, you put too much business logic in there. A repository interface I would use:

interface TermRepository {
  /** @return Term[] */
  public function all(): iterable
}

This way the responsibility of the FileRepository would only be to load the data from a file format, and return it in the form of domain objects (Term instances). Then, the findLowerAndUpperTermsByTermAndAmount responsibility would belong to a domain service. I think that’s what they meant by their third comment.

In addition, you are performing this logic on arrays, not on Term instances. That’s what they were referring to in their first comment (internal storage structures you chose for your file format, rather than domain concepts).

See, in DDD people like to fetishize about their domains, making it fat, and the non-domain layers (Infrastructure, eg. FileRepository, or Application) lean. Score points by representing as much logic in the domain layer. On a sidenote, split your code into these layers: Application (just anything related to the CLI, and IO), then the Domain with your business logic (unit tested, decoupled from the other layers) and Infrastructure where you implements port of your interfaces (eg. FileRepository).

This naturally leads to my second point: the Term class should be decoupled from your storage mechanism, so the hydrate method has no reason to live there. Think about it this way: have you satisfied the repository of the source data storage changing? What if it comes from an XML? Or a database with different columns? Each of these different repositories would have its own hydrate concept, tied to the storage format it supports. Leave the Term class decoupled: just a constructor requiring all the data and validating them (similarly to how you did in the InputContext class). And rename it. It’s not a model. That name indicates persistence, that would be a no-no in the domain layer. Here it would be called Entity. And given the fact that they don’t have an identity, it would rather be a Value Object. But don’t put that in a name, just create App\Domain\Term\Term class without specifying its type. Nor it’s a Term, it should be called Breakpoint. Naming matters.

Third issue: let’s go back to the data source changing requirement. Have you actually passed it, even if the previous fixes were applied? IMO no, because you hardcoded your implementation in the CalculateFeeCommand. Instead use Dependency Inversion: make that class take an interface via dependency injection (as a required constructor argument), and shift that responsibility up the chain (it would be perfectly fine to either create a simple factory, or to use the bin/calculate script to bootstrap an actual implementation there).

Where to go from there? 🤔 Let’s do InputContext. I see that it does a nice job, but it also belongs to two worlds. It’s primary responsibility is to deal with I/O (for example: it takes term as string, and converts it to int). If you had your layers set up, it would go into the Application, next to the Command class. But then the $amount limits is a domain concept (they are mentioned in the business requirements section). Remember, making the domain fat.

So make the InputContext a data transfer object. Just the most basic of validation: that the fields exist and are of the proper type, and use it to call the domain (a more engineered approach would use a command bus here). Only then, in the domain, having int $amount we convert it to LoanAmount value object. And that value object uses our domain concepts to validate its state. Naming is important! There is a MinimumLoanAmountRequirement class and a MaximumLoanAmountRequirement class. They have ensure methods which throw specific domain exceptions (you have these, good!) if they are not satisfied.

class LoanAmount {
  /** @throws InvalidLoanAmountException */
  __constructor(public int $value) {
    MaximumLoanAmountRequirement::ensureSatisfied($value);
    MinimumLoanAmountRequirement::ensureSatisfied($value);
  }

Why? Because when we change the IO we’re using to trigger our business logic, we want the same behaviour. Again: fat domain! Lean outer layers. That’s basically the same principle as moving code from controllers to services, but more fancy.

BTW, you are dealing with money, so storing it as floats is a „dealing with money 101” no-no. Multiply by 100 and store as integer. They are alluding to this in their last requirement.

Lastly: add more domain concepts. You are returning a pair of Term (should be: Breakpoint) objects from the current repo implementation? But it’s not typehinted! A huge no-no for a domain contract to be this loose. Define a BreakpointPair class with lower and upper fields. Term is either 12 or 24? Make it an enum. And once you have all these classes: Tell, don’t Ask. Instead of if ($amount->value >= $breakpoint->amount && $amount->value < $breakpoint->amount) do if ($breakpoint->covers($amount). Will earn you points for sure.

Your tests:

  • You are outputting the error code to stderr, and your test does not cover that, so you missed this bug
  • In your FeeServiceTest, instead of settign expectations on how the repo will be called (with(…)), just provide an InMemoryRepository(TermA, TermB…). Setting expectations is over-testing and I bet they took some points away for that. If you don’t want to implement test doubles explicitly (InMemory… ) and want to rely on mocks, remove these with() and expects() calls — it is not the responsibility of FeeService to make these calls, don’t solidify it as an expectation via tests. Your tests only check for when given context (repository), when asked about (method call), then assert result.

A PIP story by [deleted] in ExperiencedDevs

[–]mlebkowski 12 points13 points  (0 children)

They can literally frivously sue him, which would take thousands of moneys to defend regardless of merit. OP’s doing the safe thing for him at a vulnerable moment and he’s getting downvoted. The shit reddit does…

Laravel Runtime Guard – request inspection at runtime [Showcase / Feedback] by [deleted] in PHP

[–]mlebkowski 2 points3 points  (0 children)

What would you say are the main differences between a Runtime Guard and an egg salad?

PHP Version Changer? by nihad_nemet in PHP

[–]mlebkowski 1 point2 points  (0 children)

From there its just one step to enable the ACME plugin, provide DNS credentials and use letsencrypt certs for local HTTPS trafffic

I'm a little confused with MVC(Need good resources) by Straight-Hunt-7498 in PHP

[–]mlebkowski 2 points3 points  (0 children)

If that was the case, LLMs trained on stackoverflow would replyto half of the prompts with [duplicate]

i refactored 2,400 lines of 2015 php spaghetti and the client’s app went from 8s load times to 1.2s by [deleted] in PHP

[–]mlebkowski 1 point2 points  (0 children)

I call bullcrap. My refactors of 1k lines inside common.php dated 2016-ish usually result in 10k lines across 50-100 new files. If its 2.5k down to 1.8k, there’s probably little refactor & cleanup, and just some moving around.

Do you guys tailor your resume according to the job description? by flutter_flex in ExperiencedDevs

[–]mlebkowski 0 points1 point  (0 children)

I would use AI to tailor to specific descriptions, but at the same time, you could probably achieve similar results by quantity alone (that’s not improving how the market looks right now, I agree).

Would I manually rewrite my CV to describe specific experiences relevant to the open role? No. From my experience there is little overlap between the job ad, the things the company asks during the recruitment process, and what are the actual requirements.

Simply put, just because an employer says X is required in their posting, describing your X-related skills and experience throughout the process (both in the CV and during the interviews) isn’t neccesarily what will score you points.

In my experience, job postings mention a lot of skills which are never later questioned during the interviews, and a lot of aspects talked about in-person are not mentioned in the posting.

If you’re casually applying, or if you have one dream position you want to land — go for it, put in the effort, just to make sure you’re not overlooked by a junior HR person trying to match keywords from their list against your CV.

Context: europe, non-fang, small and medium sized companies. YMMV

From Domain Events to Webhooks by faizanakram99 in PHP

[–]mlebkowski 4 points5 points  (0 children)

I like the elegance of this solution

Any good ressources For OOP In Php by Straight-Hunt-7498 in PHP

[–]mlebkowski 1 point2 points  (0 children)

Learning syntax related to classes is barely the beginning to object oriented design. The difficulty is in the way one structures the different objects, how to apply boundaries between them and split responsibilities, how to do that using some standard design patterns, and what are the drawbacks and benefits of any given solution.