Problem/Motivation

While working on #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method, I encountered fatal PHP errors when using Vocabulary config entities.

When writing tests that involves Vocabulary config entities, this is what you get:

1) Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyJsonAnonTest::testGet
Exception: Notice: Use of undefined constant TAXONOMY_HIERARCHY_DISABLED - assumed 'TAXONOMY_HIERARCHY_DISABLED'
Drupal\Core\Cache\DatabaseBackend->prepareItem()() (Line: 138)


/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php:44
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/FulfilledPromise.php:39
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/TaskQueue.php:61
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:246
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:223
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:266
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:225
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:62
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:129
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:87
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127

If you then define it, like so:

namespace Drupal\taxonomy\Entity;

if (!defined('TAXONOMY_HIERARCHY_DISABLED')) {
  define('TAXONOMY_HIERARCHY_DISABLED', 0);
}

Then it still doesn't work:


1) Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyJsonAnonTest::testGet
Constant TAXONOMY_HIERARCHY_DISABLED already defined

/Users/wim.leers/Work/drupal-tres/core/modules/taxonomy/taxonomy.module:23
/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Extension/Extension.php:140
/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Extension/ModuleHandler.php:127
/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Extension/ModuleInstaller.php:182
/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php:83
/Users/wim.leers/Work/drupal-tres/core/tests/Drupal/Tests/BrowserTestBase.php:1138
/Users/wim.leers/Work/drupal-tres/core/tests/Drupal/Tests/BrowserTestBase.php:470
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/ResourceTestBase.php:94
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:51

The root cause: #1369200: Define constants for taxonomy hierarchy converted hardcoded numbers (0/1/2) all over the place to constants (yay!) but put them in taxonomy.module rather than on VocabularyInterface (sadpanda). This means that for one to use \Drupal\taxonomy\Entity\Vocabulary, taxonomy.module must also be loaded.

Proposed resolution

Move the constants from taxonomy.module to VocabularyInterface.

Remaining tasks

TBD

User interface changes

None.

API changes

The constants move.

Data model changes

None.

Comments

Wim Leers created an issue. See original summary.

wim leers’s picture

Issue summary: View changes
klausi’s picture

we cannot change the constant in drupal 8 for compatibility reasons. You need to require_once taxonomy.module in the setUp() method of your unit test instead.

Should we close this as won't fix or just move it to Drupal 9?

wim leers’s picture

You need to require_once taxonomy.module in the setUp() method of your unit test instead.

That is what I'm doing.


And @tim.plunkett suggested I do

  public static function setUpBeforeClass() {
    parent::setUpBeforeClass();
    require_once static::getDrupalRoot() . '/core/modules/taxonomy/taxonomy.module';
  }

just like in http://cgit.drupalcode.org/drupal/diff/core/modules/block/tests/src/Kern....

But that also doesn't work; it results in the same errors as above.


I'm starting to suspect somehow the test is resulting in a separate PHP environment. I don't know the internals of BrowserTestBase well enough to know that for sure though, but that's the only explanation I can think of atm.

Continuing to debug this…

wim leers’s picture

I did lots more digging.

This was hinting in the direction of PHPUnit:

Exception: Notice: Use of undefined constant TAXONOMY_HIERARCHY_DISABLED - assumed 'TAXONOMY_HIERARCHY_DISABLED'
Drupal\Core\Cache\DatabaseBackend->prepareItem()() (Line: 138)


/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php:44
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/FulfilledPromise.php:39
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/TaskQueue.php:61
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:246
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:223
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:266
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:225
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:62
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:129
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:87
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127

So I looked at TestHttpClientMiddleware.php:44. It's doing this:

                    throw new \Exception($parameters[1] . ': ' . $parameters[0] . "\n" . Error::formatBacktrace([$parameters[2]]));

This is where that output is coming from (Exception: Notice: Use of undefined constant TAXONOMY_HIERARCHY_DISABLED - assumed 'TAXONOMY_HIERARCHY_DISABLED' […])!

So this is dealing with the case of there being a fatal error, or a notice — or in other words: PHP engine output.

By putting different code there, to return early, reveals that it's PHPUnit's Php.php, line 107, that is sending this back in the first place. This lead me to the following PHPUnit issues, which were simply closed without being solved:

  1. https://github.com/sebastianbergmann/phpunit/issues/1571
  2. https://github.com/sebastianbergmann/phpunit/issues/314

The latter was even commented on in great detail by our very own sun!


Both of these PHPUnit issues indicate that the problem is related to global state being preserved or not. BrowserTestBase does this:

  protected $runTestInSeparateProcess = TRUE;
  protected $preserveGlobalState = FALSE;

This also explains why the recommendations by klausi & tim.plunkett don't have the desired effect.


Still no solution yet, continuing my debugging…

wim leers’s picture

Assigned: Unassigned » wim leers

I've found the root cause. You won't believe this. This is both hilarious and sad. But it's definitely epic.

Stay tuned for the full explanation.

wim leers’s picture

Debug process (a successful one, after many attempts)

I personally found it very very tricky to debug this PHP notice at Drupal\Core\Cache\DatabaseBackend->prepareItem()() (Line: 138), because it was happening inside a request made by Guzzle, which made it impossible for me to step through it with a debugger. Worse, it was also very difficult to be able to add meaningful debug output, because it was happening in a place that got many dozens of function calls, and I couldn't figure out which call was the problem.

In the end, I settled on this:

Original code
      $cache->data = unserialize($cache->data);
Modified code (for debugging)
      set_error_handler(function ($errno, $errstr, $errfile, $errline) {
        throw new \ErrorException($errstr, $errno, $errno, $errfile, $errline);
      });
      try {
        $cache->data = unserialize($cache->data);
      }
      catch (\ErrorException $e) {
        throw new \Exception('When unserializing the data for cache item "' . $cache->cid . "\", this problem occurred: \n" . $e->getMessage() . "\n\nStored data:" . $cache->data);
      }
      restore_error_handler();
Pattern
set_error_handler(/* function that converts everything, including notices, to ErrorExceptions */)
PROBLEMATIC CODE
restore_error_handler()

This allowed me to see what exactly was happening, with useful debug output:

Before
1) Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyJsonAnonTest::testGet
Exception: Notice: Use of undefined constant TAXONOMY_HIERARCHY_DISABLED - assumed 'TAXONOMY_HIERARCHY_DISABLED'
Drupal\Core\Cache\DatabaseBackend->prepareItem()() (Line: 138)


/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php:44
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/FulfilledPromise.php:39
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/TaskQueue.php:61
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:246
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:223
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:266
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:225
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:62
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:129
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:87
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127
After
1) Drupal\Tests\rest\Functional\EntityResource\Vocabulary\VocabularyJsonAnonTest::testGet
Exception: Exception: When unserializing the data for cache item "http://localhost/tres/entity/taxonomy_vocabulary/camelids?_format=json:json", this problem occurred: 
Use of undefined constant TAXONOMY_HIERARCHY_DISABLED - assumed 'TAXONOMY_HIERARCHY_DISABLED'

Stored data:O:28:"Drupal\rest\ResourceResponse":8:{s:7:"headers";O:50:"Symfony\Component\HttpFoundation\ResponseHeaderBag":5:{s:23:"*computedCacheControl";a:3:{s:15:"must-revalidate";b:1;s:8:"no-cache";b:1;s:7:"private";b:1;}s:10:"*cookies";a:0:{}s:14:"*headerNames";a:12:{s:13:"cache-control";s:13:"Cache-Control";s:12:"content-type";s:12:"Content-Type";s:22:"x-drupal-dynamic-cache";s:22:"X-Drupal-Dynamic-Cache";s:15:"x-ua-compatible";s:15:"X-UA-Compatible";s:16:"content-language";s:16:"Content-language";s:22:"x-content-type-options";s:22:"X-Content-Type-Options";s:15:"x-frame-options";s:15:"X-Frame-Options";s:19:"x-drupal-cache-tags";s:19:"X-Drupal-Cache-Tags";s:23:"x-drupal-cache-contexts";s:23:"X-Drupal-Cache-Contexts";s:7:"expires";s:7:"Expires";s:4:"vary";s:4:"Vary";s:11:"x-generator";s:11:"X-Generator";}s:10:"*headers";a:12:{s:13:"cache-control";a:1:{i:0;s:34:"must-revalidate, no-cache, private";}s:12:"content-type";a:1:{i:0;s:16:"application/json";}s:22:"x-drupal-dynamic-cache";a:1:{i:0;s:4:"MISS";}s:15:"x-ua-compatible";a:1:{i:0;s:7:"IE=edge";}s:16:"content-language";a:1:{i:0;s:2:"en";}s:22:"x-content-type-options";a:1:{i:0;s:7:"nosniff";}s:15:"x-frame-options";a:1:{i:0;s:10:"SAMEORIGIN";}s:19:"x-drupal-cache-tags";a:1:{i:0;s:110:"config:rest.resource.entity.taxonomy_vocabulary config:taxonomy.vocabulary.camelids config:user.role.anonymous";}s:23:"x-drupal-cache-contexts";a:1:{i:0;s:16:"user.permissions";}s:7:"expires";a:1:{i:0;s:29:"Sun, 19 Nov 1978 05:00:00 GMT";}s:4:"vary";a:0:{}s:11:"x-generator";a:1:{i:0;s:33:"Drupal 8 (https://www.drupal.org)";}}s:15:"*cacheControl";a:2:{s:15:"must-revalidate";b:1;s:8:"no-cache";b:1;}}s:10:"*content";s:174:"{"uuid":"ae28bfe4-d015-47e9-b179-bc4bdae041de","langcode":"en","status":true,"dependencies":[],"name":"Camelids","vid":"camelids","description":null,"hierarchy":0,"weight":0}";s:10:"*version";s:3:"1.0";s:13:"*statusCode";i:200;s:13:"*statusText";s:2:"OK";s:10:"*charset";N;s:23:"*cacheabilityMetadata";O:35:"Drupal\Core\Cache\CacheableMetadata":3:{s:16:"*cacheContexts";a:1:{i:0;s:16:"user.permissions";}s:12:"*cacheTags";a:3:{i:0;s:47:"config:rest.resource.entity.taxonomy_vocabulary";i:1;s:35:"config:taxonomy.vocabulary.camelids";i:2;s:26:"config:user.role.anonymous";}s:14:"*cacheMaxAge";i:-1;}s:15:"*responseData";O:33:"Drupal\taxonomy\Entity\Vocabulary":20:{s:6:"*vid";s:8:"camelids";s:7:"*name";s:8:"Camelids";s:14:"*description";N;s:12:"*hierarchy";i:0;s:9:"*weight";i:0;s:13:"*originalId";s:8:"camelids";s:9:"*status";b:1;s:7:"*uuid";s:36:"ae28bfe4-d015-47e9-b179-bc4bdae041de";s:11:"*langcode";s:2:"en";s:23:"*third_party_settings";a:0:{}s:8:"*_core";a:0:{}s:14:"*trustedData";b:0;s:15:"*entityTypeId";s:19:"taxonomy_vocabulary";s:15:"*enforceIsNew";N;s:12:"*typedData";N;s:16:"*cacheContexts";a:0:{}s:12:"*cacheTags";a:0:{}s:14:"*cacheMaxAge";i:-1;s:14:"*_serviceIds";a:0:{}s:15:"*dependencies";a:0:{}}}
Drupal\Core\Cache\DatabaseBackend->prepareItem()() (Line: 155)


/Users/wim.leers/Work/drupal-tres/core/lib/Drupal/Core/Test/HttpClientMiddleware/TestHttpClientMiddleware.php:51
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/FulfilledPromise.php:39
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/TaskQueue.php:61
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:246
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:223
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:266
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:225
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/promises/src/Promise.php:62
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:129
/Users/wim.leers/Work/drupal-tres/vendor/guzzlehttp/guzzle/src/Client.php:87
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127
/Users/wim.leers/Work/drupal-tres/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php:127

Analysis

So, unserialization of a Vocabulary entity is occurring and failing not inside the test, but as part of a Page Cache hit! ResourceResponse apparently contains a serialized Vocabulary entity, and it cannot be unserialized, so BAM, everything explodes.

The root cause is simple: the PageCache middleware runs before the KernelPreHandle middleware. The latter is what loads modules. So: taxonomy.module has not yet been loaded by the time that we try to unserialize a serialized, cached Vocabulary entity that uses a constant defined in taxonomy.module.

Solution: still move TAXONOMY_HIERARCHY_* constants to VocabularyInterface, but keep the existing constants, and keep them in sync. This ensures BC is not broken for code that is doing things like if ($vocabulary->getHierarchy() === TAXONOMY_HIERARCHY_*. But then all other code must use VocabularyInterface::HIERARCHY_*.

To convince yourself this works, try this:

<?php

const TAXONOMY_HIERARCHY_DISABLED = 0;

interface VocabularyInterface {
  const HIERARCHY_DISABLED = 0;
}

var_dump(TAXONOMY_HIERARCHY_DISABLED === VocabularyInterface::HIERARCHY_DISABLED);

See https://3v4l.org/K3LRM to see the results of that on all PHP versions. It works.

Performance

Coincidentally, this revealed that ResourceResponse still contains the data it must serialize! And so, whenever (Dynamic) Page Cache retrieves its cache items, the cache system unserializes the response, and so it must also unserialize ResourceResponse::$responseData.

Fixing this leads to a 60% performance boost (for anonymous REST responses). See #2807501: ResourceResponse::$responseData is serialized, but is never used: unserializing it on every Page Cache hit is a huge performance penalty.

Patch

Attached patch includes test coverage that shows the problem.

Status: Needs review » Needs work

The last submitted patch, 7: 2807263-7.patch, failed testing.

wim leers’s picture

Version: 8.3.x-dev » 8.2.x-dev
Status: Needs work » Needs review
Issue tags: +Needs change record
StatusFileSize
new13.74 KB
new18.71 KB

And here's the fix.

catch independently proposed the same solution yesterday in IRC:

16:10:00 <catch> WimLeers: duplicate the constants to vocabularyinterface and deprecate the old ones?

Moving to 8.2.x, because this is breaking REST and blocking #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method, which is necessary for REST to become truly stable/reliable in 8.2.x.

wim leers’s picture

Issue tags: +blocker
wim leers’s picture

klausi’s picture

Status: Needs review » Needs work

Looks good, just some minor notpicks:

  1. +++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
    @@ -0,0 +1,49 @@
    +  /**
    +   * Modules to enable.
    +   *
    +   * @var array
    +   */
    

    should be {@inheritdoc}

  2. +++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
    @@ -0,0 +1,49 @@
    +    $url = Url::fromRoute('vocabulary_serialization_test', ['taxonomy_vocabulary' => 'test']);
    +
    +    $this->drupalGet($url);
    

    this makes the test case very hard to read. Keep it simple: it is perfectly fine to hard-code the path in a test case in drupalGet().

  3. +++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
    @@ -0,0 +1,49 @@
    +    $this->assertSame('MISS', $this->getSession()->getResponseHeader('X-Drupal-Cache'));
    

    use ->responseHeaderEquals() instead.

  4. +++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
    @@ -0,0 +1,49 @@
    +    $this->assertSame('HIT', $this->getSession()->getResponseHeader('X-Drupal-Cache'));
    

    ->responseHeaderEquals()

And then we need the change record draft. After that I would RTBC it :)

We should probably open a follow-up to deprecate all constants in all modules. Otherwise we might run into this problem again.

wim leers’s picture

wim leers’s picture

Status: Needs work » Needs review
StatusFileSize
new1.78 KB
new18.67 KB

Addressed everything in #14. Thanks for responseHeaderEquals(), I didn't know that one!

wim leers’s picture

klausi’s picture

Status: Needs review » Reviewed & tested by the community

CR looks good and contains instructions for contrib maintainers. I verified manually that the old constants are not used anymore.

I think we can ignore the missing doc blocks on the classes/methods of the test module, nothing useful to say there I guess?

5 stars, would RTBC again.

klausi’s picture

Issue tags: +Dublin2016
dawehner’s picture

To be honest we should really start with a more generic issue, I'm working on one

dawehner’s picture

+++ b/core/modules/taxonomy/taxonomy.module
@@ -19,16 +19,25 @@
 /**
  * Denotes that no term in the vocabulary has a parent.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ *   \Drupal\taxonomy\VocabularyInterface::HIERARCHY_DISABLED instead.
  */
 const TAXONOMY_HIERARCHY_DISABLED = 0;
 
 /**
  * Denotes that one or more terms in the vocabulary has a single parent.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ *   \Drupal\taxonomy\VocabularyInterface::HIERARCHY_SINGLE instead.
  */
 const TAXONOMY_HIERARCHY_SINGLE = 1;
 
 /**
  * Denotes that one or more terms in the vocabulary have multiple parents.
+ *
+ * @deprecated in Drupal 8.2.x and will be removed before 9.0.0. Use
+ *   \Drupal\taxonomy\VocabularyInterface::HIERARCHY_MULTIPLE instead.
  */
 const TAXONOMY_HIERARCHY_MULTIPLE = 2;

Should we just add VocabularyInterface::HIERARCHY_DISABLED as concrete reference to show people what a proper transition path looks like?

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/taxonomy/taxonomy.module
@@ -19,16 +19,25 @@
 const TAXONOMY_HIERARCHY_DISABLED = 0;
...
 const TAXONOMY_HIERARCHY_SINGLE = 1;
...
 const TAXONOMY_HIERARCHY_MULTIPLE = 2;

Let's make these equal the new class constants.

wim leers’s picture

#20: Oh, I like that!

#21: that's the same as what #20 says, right?

dawehner’s picture

Yeah exactly.

wim leers’s picture

Assigned: wim leers » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.32 KB
new18.92 KB

Much better. Thanks! :)

Status: Needs review » Needs work

The last submitted patch, 24: 2807263-24.patch, failed testing.

klausi’s picture

Status: Needs work » Reviewed & tested by the community

Random test fail, queuing again.

Looks good otherwise!

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 24: 2807263-24.patch, failed testing.

alexpott’s picture

+++ b/core/modules/taxonomy/src/VocabularyInterface.php
@@ -9,6 +9,21 @@
+  /**
+   * Denotes that no term in the vocabulary has a parent.
+   */
+  const HIERARCHY_DISABLED = 0;
+
+  /**
+   * Denotes that one or more terms in the vocabulary has a single parent.
+   */
+  const HIERARCHY_SINGLE = 1;
+
+  /**
+   * Denotes that one or more terms in the vocabulary have multiple parents.
+   */
+  const HIERARCHY_MULTIPLE = 2;

+++ b/core/modules/taxonomy/taxonomy.module
@@ -19,18 +19,27 @@
-const TAXONOMY_HIERARCHY_DISABLED = 0;
+const TAXONOMY_HIERARCHY_DISABLED = VocabularyInterface::HIERARCHY_DISABLED;
...
-const TAXONOMY_HIERARCHY_SINGLE = 1;
+const TAXONOMY_HIERARCHY_SINGLE = VocabularyInterface::HIERARCHY_SINGLE;
...
-const TAXONOMY_HIERARCHY_MULTIPLE = 2;
+const TAXONOMY_HIERARCHY_MULTIPLE = VocabularyInterface::HIERARCHY_MULTIPLE;

+++ b/core/modules/taxonomy/tests/modules/vocabulary_serialization_test/vocabulary_serialization_test.routing.yml
@@ -0,0 +1,6 @@
+    _controller: 'Drupal\vocabulary_serialization_test\VocabularySerializationTestController::vocabularyResponse'

We need to do this the other way around. :( the module file is loaded before the classloader is updated.

klausi’s picture

fail: [PHP Fatal error] Line 26 of core/modules/taxonomy/taxonomy.module:
 Class 'Drupal\taxonomy\VocabularyInterface' not found

So we cannot use the class in the global scope because the class loader for the module is not there when the module is installed/uninstalled.

Back to hard code the numbers then?

klausi’s picture

@alexpott: we cannot do it the other way around because then the module would have to be loaded all the time, which it is not :)

I think it is fine to duplicate the constant values and not care too much about it.

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

So let's go back to #14 because then we don't have to change anything when we drop support for the old constants.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 8a97a26 and pushed to 8.3.x. Thanks!

diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index 1babe40..4c8cee3 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -400,7 +400,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
         $changed_terms[$term->id()] = $term;
       }
       $weight++;
-      $hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE: $hierarchy;
+      $hierarchy = $term->parents[0] != 0 ? VocabularyInterface::HIERARCHY_SINGLE : $hierarchy;
       $term = $tree[$weight];
     }
 
diff --git a/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
index 0ae0c48..b9aba8a 100644
--- a/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/VocabularySerializationTest.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\Tests\taxonomy\Functional;
 
-use Drupal\Core\Url;
 use Drupal\taxonomy\Entity\Vocabulary;
 use Drupal\Tests\BrowserTestBase;
 

Fixed on commit.

  • alexpott committed 8a97a26 on 8.3.x
    Issue #2807263 by Wim Leers: Impossible to write unit tests involving...
klausi’s picture

Status: Fixed » Reviewed & tested by the community

This should also be committed to 8.2.x, right?

klausi’s picture

Version: 8.2.x-dev » 8.3.x-dev
Status: Reviewed & tested by the community » Fixed

alexpott said this cannot happen in 8.2.x for now, maybe after 8.2.0. Marking as fixed for now, will also publish the change notice for 8.3.x.

wim leers’s picture

Version: 8.3.x-dev » 8.2.x-dev
Status: Fixed » Patch (to be ported)

This must be ported to 8.2.x. It's a hard blocker for #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method, which we need to make REST as stable/reliable as possible in 8.2.x. Alex and catch have been marking those issues that still need to be committed to 8.2.x after 8.2.0 as "patch to be ported" and assigned to 8.2.x. So doing the same here.

Please don't yet publish the CR, until we know in which version of 8.2.x this lands, so that we don't need to retroactively update it after publishing.

alexpott’s picture

+++ b/core/modules/taxonomy/src/VocabularyInterface.php
@@ -9,6 +9,21 @@
+  /**
+   * Denotes that no term in the vocabulary has a parent.
+   */
+  const HIERARCHY_DISABLED = 0;
+
+  /**
+   * Denotes that one or more terms in the vocabulary has a single parent.
+   */
+  const HIERARCHY_SINGLE = 1;
+
+  /**
+   * Denotes that one or more terms in the vocabulary have multiple parents.
+   */
+  const HIERARCHY_MULTIPLE = 2;

This is the sum disruption of the issue. The old constants are still in place - just deprecated. I think doing this in 8.2.1 is acceptable given what it blocks.

catch’s picture

Yes I also think it's reasonable to backport the new class constants.

wim leers’s picture

Yep, they're just deprecated, but they'll continue to work just fine.

Status: Patch (to be ported) » Needs work

The last submitted patch, 24: 2807263-24.patch, failed testing.

alexpott’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new18.67 KB

Doh - we committed #14... that's why the last patch should always be the committed one :( Re-uploading to make this so.

alexpott’s picture

Status: Reviewed & tested by the community » Patch (to be ported)

Making the actual status clear

alexpott’s picture

Status: Patch (to be ported) » Fixed

Committed fc4b8d3 and pushed to 8.2.x. Thanks!

  • alexpott committed fc4b8d3 on 8.2.x
    Issue #2807263 by Wim Leers: Impossible to write unit tests involving...
wim leers’s picture

Status: Fixed » Closed (fixed)

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

xjm’s picture

Issue tags: +8.2.2 release notes