Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@dereuromark
Copy link
Member

Replaces #18846 and pulls it into 5.next as BC solution for the time being.
We have then time to think about a 6.x approach/alternative.

But it would be nice if we could already clean this up now, to avoid those PHPStan issues that need silencing or hacking then:

  67     Parameter #2 $options of method                                        
         App\View\Widget\DateWidget::formatDateTime() expects array<string, mixed>, 
         array<string, string>|string|null given.        

Added new methods and deprecated the old one
- addClassToArray() - Type-safe method for adding classes to attribute arrays
- mergeClasses() - Focused method for merging class lists
- addClass() - Deprecated (5.3.0, removal in 6.0) with delegation to new methods

@dereuromark dereuromark added this to the 5.3.0 milestone Oct 26, 2025
@othercorey
Copy link
Member

@albertcansado Would you like to comment on this iteration?

@dereuromark
Copy link
Member Author

@othercorey Did you ping the right persons?

@othercorey
Copy link
Member

Isn't that who commented on the previous PR? Don't know who else has an opinion.

@dereuromark dereuromark requested a review from markstory October 28, 2025 02:56
*
* @param array<string>|string $existing The existing class(es).
* @param array<string>|string $new The new class(es) to merge.
* @return array<string> A unique array of merged classes.
Copy link
Member

@ADmad ADmad Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the old method return string if the 1st arg is string? If so we can retain the same. We can use conditional return type based on argument type which keeps the return type deterministic for static analyzers.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, thats why the old code needed (array) casting.
These methods indeed are the 6.x code I recommended moving forward.
So those are already "clean" from BC requirements.
For the backport you are right, we probably should consider more BC approach.

Feel free to add a commit, not quite sure how that conditional works.

@albertcansado
Copy link
Contributor

Hello @othercorey thanks for the mention!

What I can contribute is my own experience/usage, just another one — I’ve replaced those methods with the one I mentioned in the other PR (classnames) because it gives me more flexibility and lets me make inline conditional declarations. It saves me from having to write N if statements and call the method to perform the merge (for example). I can declare and merge at the same time.

Also, since it returns a string, I can use it outside of StringTemplate or expect it to be rendered with the formatAttributes method. An example:

<div class="<?= $this->classnames('foo', ['is-open' => $items->count() > 1, 'is-disabled' => !$items->count()]) ?>"></div>

But as I said, it’s just a preference

Comment on lines 348 to 350
// Test merging two arrays
$result = $this->template->mergeClasses(['existing'], ['new']);
$this->assertSame(['existing', 'new'], $result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding two new methods? We should pick one way to do this going forward that provides a more typesafe interface that handles several usage scenarios. I think drawing inspiration from classnames like @albertcansado suggested is an example of an ergonomic interface that could be hard to replicate exactly in typed PHP, but perhaps we can get close. Their docs have the following scenarios outlined:

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

In PHP I could see this constrained to:

function addClassnames(string $class, array ...$args): string

We can't give much type safety for args but I think that is on purpose. I think it is important to remember that most of the time classname manipulation is done to feed into another helper. Making it easy to dynamically generate class lists from a map of application state is an important scenario for server rendered HTML.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR above states the reason for the new IMO correct naming: They reflect what those separate methods now actually do, instead of the god method from before.

The change is fully automated via rector - and also fully BC.

If you have an alternative, go ahead. I don't see a good one at this point, though, that would replace the bad method with smaller ones, while keeping the upgrade efforts minimal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding two new methods?

Currently if you pass addClass() an array as the 1st argument, it treat it as a full arguments array and modifies $input['class'] and returns the array. How will we provide rector based automated updating if we only add new method which returns string?

That said instead of adding 2 new methods we can deprecate the ability to pass string as 1st argument to existing addClass() and also deprecate the ability to return a non-array value. So going future it would only deal with modifying a complete attributes array.

Then we add only 1 new method classNames()/addClassNames() would handles the actual CSS list merging which required API.

@dereuromark
Copy link
Member Author

I adjusted it per review.

Final API:

  • addClass() - Works with attributes arrays (deprecated for non-array usage)
  • addClassNames() - Single new method for CSS class merging

I dont like it compared to the API proposed, it is less speaking IMO and more confusing long term.
But if that at least solves the issues for now, fair enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants