Problem/Motivation

Drupal has arrays.

Drupal has LOTS of arrays.

Arrays are, sometimes, quite painful to manipulate and test.

We need a base object that we can start creating other strongly typed objects on that do nothing but manipulate and extract information from arrays (at their core, no pun intended):

Additional use-cases for existing issues:

Unfortunately, the SPL \ArrayObject isn't really all that "feature rich" with all the various tasks that we commonly do with arrays.

Proposed resolution

Implement our own custom base ArrayObject class, similar to something that resembles:

https://cgit.drupalcode.org/plus/tree/src/Utility/ArrayObject.php?h=8.x-4.x

Remaining tasks

  • Create patch
  • Create tests

User interface changes

None

API changes

Utility/API addition

Data model changes

None

Comments

markcarver created an issue. See original summary.

markhalliwell’s picture

Issue summary: View changes
markhalliwell’s picture

Component: render system » base system

Wrong component.

markhalliwell’s picture

Issue summary: View changes
dawehner’s picture

While I get the point of it (consistency) I think the better approach is to not expose more array like objects, but rather remove some of them and deprecate the array API, and replace them by proper domain objects.

markhalliwell’s picture

I understand the end goal.

However, I think this is a necessary intermediate step considering how much [BC] code (that uses arrays) we'll need to support in 8.x.

It'll also give us an idea of what actually still needs to remain as arrays and what can be converted into proper domain objects in the future for 9.x.

That being said, I think some things will always remain an array (like Attachments, Attributes or Variables) as they are simply a collection of things that can be constructed in any number of arbitrary ways. There really isn't any way around that.

This will simply give us a better foundation on how to implement and test them.

It's also a way to help reduce duplication of code for common tasks that we do with these objects in any given area of code.

Suffice it to say, simply saying "we should move away from arrays", while an admirable goal, isn't always appropriate or feasible given the current state of a lot of the current code.

dawehner’s picture

Sure I get that. The problem though is: when we put out a common base class like this, people will start to use it more and more.

I think duplicate code is less of an issue, especially it is less of an issue when we talk about deprecated code.

markhalliwell’s picture

I'm fine with marking this as @internal with a big note saying something to the effect of "make sure you actually need this before using it, the primary goal in core is to move away from Arrays... etc, etc, etc".

Granted that won't exactly help with the "everybody using it" thing, but it certainly would eliminate any responsibility if/when we decide to deprecate/remove it (which I seriously doubt will happen, at least not for a very long time).

The main purpose of this issue is to get something in core that can help standardize a lot of our theme system internals into something that is a little more consistent and easy to work with. Not to mention easy for DX/FX purposes.

We need this baby step.

markhalliwell’s picture

Issue summary: View changes

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

voleger’s picture

So what is the next step of this issue? Should we start from the definition of an interface or what?

markhalliwell’s picture

This is still semi in the discovery phase as to exactly what is needed.

We'll need to know what array-like objects will need to ensure they can be extended from this base object easily enough.

For now, this is mostly just a placeholder. I'll provide a patch to get this started once I'm confident we're ready to proceed.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

kim.pepper’s picture

Version: 9.5.x-dev » 10.0.x-dev
Status: Active » Needs review
StatusFileSize
new4.49 KB

I found a need for this in #3292759: Create getters and setters for dynamic Extension properties so wrote up an implementation.

andypost’s picture

Assigned: markhalliwell » Unassigned
Issue tags: +PHP 8.2

It looks great and fixes array object for PHP 8.2

Waiting for tests and looks rtbc

kim.pepper’s picture

Assigned: Unassigned » amber himes matz
StatusFileSize
new5.06 KB
new1.51 KB

I've added get/set methods for more OO property access.

kim.pepper’s picture

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Seems to have 8.2 failures. Should that be hold up or no?

voleger’s picture

Status: Needs work » Needs review

No, these were unrelated issues during the test run. The patch contains new additions only.

  1. +++ b/core/lib/Drupal/Component/Utility/ArrayObject.php
    @@ -0,0 +1,100 @@
    +  public function set(mixed $key, mixed $value): void {
    ...
    +  public function offsetSet(mixed $offset, mixed $value): void {
    

    I suggest using union type argument declarations:
    `?string|?int` for `$key` argument

  2. +++ b/core/lib/Drupal/Component/Utility/ArrayObject.php
    @@ -0,0 +1,100 @@
    +  public function offsetExists(mixed $offset): bool {
    ...
    +  public function offsetGet(mixed $offset): mixed {
    ...
    +  public function offsetUnset(mixed $offset): void {
    

    I suggest using a union type argument declaration:
    `string|int`

Also, why not introduce the method chaining support for setter methods?

kim.pepper’s picture

Version: 10.0.x-dev » 10.1.x-dev
StatusFileSize
new5.2 KB
new1.68 KB

Thanks for kicking this issue along!

#25

  1. `?string|?int` is not valid as you can't mix union types with nullable types. Added string|int $key for get and set methods instead.
  2. We cannot change the method signatures for methods implementing the \ArrayAccess interface.

Added a fluid setter and bumped to 10.1.x

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Change looks like a good consistentcy addition

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs framework manager review

Are any of the issues that reference this one planning to use it? There's no indication as such in the issue summary. Without a concrete use-case, it won't help with conversions. For example the builder pattern issue provides a class that creates a render array, it doesn't object-ify the render array itself, so it won't help there.

Additionally, if we added something like this, I don't think we should call it ArrayObject because that already exists in SPL.

kim.pepper’s picture

I came here from #2024043-53: Add Module, Theme, Profile, and Extension value objects where @larowlan suggested this. I think we need a base object that can support the existing arrays that are currently a dumping ground.

Maybe we can call this BaseArrayObject to make that more clear?

kim.pepper’s picture

Assigned: kim.pepper » Unassigned

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

donquixote’s picture

I am skeptical about the use of ArrayAccess objects.
I also don't like these grand base classes, which tend to give their children more capabilities than they need.
E.g. would the extension info classes really need getIterator()?

I would want to see examples how this can improve DX elsewhere - see also my comment in #2024043: Add Module, Theme, Profile, and Extension value objects.
We can create proof-of-concept branches that show how this would be used.
(test cases alone are not sufficient for this)
So +1 to @catch in #28.

I did recently post a proposal involving ArrayAccess in slack, but this was for a very niche and controversial case to allow $cache[$cid] ??= ..., very different from what we do here.

Btw, plain php arrays _do_ have some benefits over general-purpose ArrayAccess objects:
A php array is passed by value, and is therefore in a way "immutable": If you pass it around to other functions, your own copy of the array won't be manipulated. The same cannot be said about mutable ArrayAccess objects.
Also there are operations and functions for arrays that don't work with ArrayAccess objects.

The DX with arrays can be improved with phpstan/psalm advanced types.

I do like value objects, but these work best with a known and limited list of properties.
Or, if we do want to support custom properties in addition to known properties, for the custom ones we can simply have ->get($key) instead of [$key].
This way, when reading the code, we know it is an object.

The only other reason for ArrayAccess might be for BC support, as a smooth replacement for arrays.
But this is already limited if the code we want to support uses array type hints, or things like preg_grep() etc.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.