Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mikelutz created an issue. See original summary.

mikelutz’s picture

Berdir’s picture

Reroll and adding deprecated to the entity.manager service to see what happens if we do this.

+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityFormTest.php
@@ -23,11 +23,7 @@ public function testEntityManagerDeprecation() {
     $time = $this->prophesize(TimeInterface::class)->reveal();
     $form = new ContentEntityForm($entity_manager, $entity_type_bundle_info, $time);
-
-    $reflected_form = new \ReflectionClass($form);
-    $entity_manager_property = $reflected_form->getProperty('entityManager');
-    $entity_manager_property->setAccessible(TRUE);
-    $this->assertTrue($entity_manager_property->getValue($form) === $entity_manager);
+    $this->assertTrue($form->entityManager === $entity_manager);
   }

I'm not really sold yet on the whole private thing here. If we do this then we should IMHO just clean it up properly like everything else, drop the property and rely on the trait.

Specifically, this doesn't test anymore what it used to test, the BC layer in the constructor. Setting the private property there doesn't do anything anymore and is not what is tested here, so we can just as well drop it entirely there and adopt a standard construct BC layer. This was the first such constructor change that we did around entity manager.

mikelutz’s picture

The reason the trait doesn't work in this case is because the trait would break the behavior of the setEntityManager() injector. The trait will always get the service from the container, so the injector would no longer do anything. In normal mode, this is probably fine, but if someone injects a mock using that method or is testing something in a unit test, the test would break.

The reason for moving it to private is specifically so the constructor BC injection layer does still work. While contentEntityForm::__construct triggers it's own error, the whole magic method is there to make sure the error is triggered anytime a subclass tries to set $this->entityForm directly. You can see it in the test you posted. === is a strict comparison, same as assertSame(). $form->entityManager refers to the exact same prophecy instance that was passed to the constructor. Because the member is private, when the subclass tries to set it, it gets funneled through the parent's __set method, and gets set anyway, we just have the opportunity to trigger an error.

In fact it might be more clear if we do remove the member and create a new private $privateEntityManager member to hold the value.

The point is that the added work here is about doing what the trait does in a similar way, but also allowing setEntityManager() to continue working, which we couldn't do if we used the trait.

The only real way this breaks is if a subclass implements their own __set or __get and doesn't pass through to the parent.

mikelutz’s picture

Berdir’s picture

Thanks for the explanation, makes sense now what it does.

I think it comes down to whether this is something we care about:

The reason the trait doesn't work in this case is because the trait would break the behavior of the setEntityManager() injector. The trait will always get the service from the container, so the injector would no longer do anything. In normal mode, this is probably fine, but if someone injects a mock using that method or is testing something in a unit test, the test would break.

Do we care about such IMHO rare cases that would AFAICS only break (unit) tests which would quite likely already be broken as we ourself on longer use $this->entityManager.

Or are we just going to say that code needs to be updated anyway and adding that logic to support it isn't worth it. IMHO, it's not :)

alexpott’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -429,6 +456,7 @@ public function setModuleHandler(ModuleHandlerInterface $module_handler) {
     $this->entityManager = $entity_manager;

Shouldn't this set the private entity manager now? ie. $this->privateEntityManager? Or we want this to invoke the magic __set()? I guess it doesn't matter. Looks odd though.

Re the trait vs private discussion - I'm assuming that @Berdir is suggesting we use \Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait and @mikelutz is pointing out that this would break the \Drupal\Core\Entity\EntityForm::setEntityManager() method.

At this point I'm not sure it really matters either way. I think on balance I'd err towards @mikelutz's solution because we're adhering better to the BC promise. Therefore going to rtbc.

mikelutz’s picture

Assigned: Unassigned » mikelutz
Status: Reviewed & tested by the community » Needs work

Yes, that should set the private entity manager.. There's no need to trigger both errors by running through __set.

Also, the deprecation messages need updating. I originally wrote this before the format was finalized.

Updated patch incoming.

mikelutz’s picture

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

Looks good. I should have spotted the message formats. Nice work.

mikelutz’s picture

Assigned: mikelutz » Unassigned

Thanks!

larowlan’s picture

larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed 668e08a and pushed to 8.8.x. Thanks!

  • larowlan committed 668e08a on 8.8.x
    Issue #3052233 by mikelutz, Berdir, alexpott: Properly deprecate...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

joachim’s picture

Unfortunately, this patch breaks any subclasses that are lazy about declaring class properties: #3094913: EntityForm's use of magic setter/getter breaks any subclasses that don't declare a property.

joelpittet’s picture

@joachim Same here #3097934: Fatal error on admin page

I'm wondering if @mikelutz for BC it should create an array property for default getters and setters that are used?