Skip to content

Commit 9b45c6b

Browse files
author
Nathaniel Catchpole
committed
Issue #2701829 by alexpott, andypost, Soul88, Graber, Eduardo Morales, dawehner, pingwin4eg, catch, Berdir, jibran, httang12: Extension objects should not implement \Serializable
1 parent 0c69b4b commit 9b45c6b

File tree

8 files changed

+214
-33
lines changed

8 files changed

+214
-33
lines changed

lib/Drupal/Core/Extension/Extension.php

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
/**
66
* Defines an extension (file) object.
7+
*
8+
* This class does not implement the Serializable interface since problems
9+
* occurred when using the serialize method.
10+
*
11+
* @see https://bugs.php.net/bug.php?id=66052
712
*/
8-
class Extension implements \Serializable {
13+
class Extension {
914

1015
/**
1116
* The type of the extension (e.g., 'module').
@@ -156,47 +161,27 @@ public function __call($method, array $args) {
156161
}
157162

158163
/**
159-
* Implements Serializable::serialize().
164+
* Magic method implementation to serialize the extension object.
160165
*
161-
* Serializes the Extension object in the most optimized way.
166+
* @return array
167+
* The names of all variables that should be serialized.
162168
*/
163-
public function serialize() {
169+
public function __sleep() {
170+
// @todo \Drupal\Core\Extension\ThemeExtensionList is adding custom
171+
// properties to the Extension object.
172+
$properties = get_object_vars($this);
164173
// Don't serialize the app root, since this could change if the install is
165-
// moved.
166-
$data = [
167-
'type' => $this->type,
168-
'pathname' => $this->pathname,
169-
'filename' => $this->filename,
170-
];
171-
172-
// @todo ThemeHandler::listInfo(), ThemeHandler::rebuildThemeData(), and
173-
// system_list() are adding custom properties to the Extension object.
174-
$info = new \ReflectionObject($this);
175-
foreach ($info->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
176-
$data[$property->getName()] = $property->getValue($this);
177-
}
178-
179-
return serialize($data);
174+
// moved. Don't serialize splFileInfo because it can not be.
175+
unset($properties['splFileInfo'], $properties['root']);
176+
return array_keys($properties);
180177
}
181178

182179
/**
183-
* {@inheritdoc}
180+
* Magic method implementation to unserialize the extension object.
184181
*/
185-
public function unserialize($data) {
186-
$data = unserialize($data);
182+
public function __wakeup() {
187183
// Get the app root from the container.
188184
$this->root = DRUPAL_ROOT;
189-
$this->type = $data['type'];
190-
$this->pathname = $data['pathname'];
191-
$this->filename = $data['filename'];
192-
193-
// @todo ThemeHandler::listInfo(), ThemeHandler::rebuildThemeData(), and
194-
// system_list() are adding custom properties to the Extension object.
195-
foreach ($data as $property => $value) {
196-
if (!isset($this->$property)) {
197-
$this->$property = $value;
198-
}
199-
}
200185
}
201186

202187
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: 'Theme test subseven'
2+
type: theme
3+
description: 'Test theme which uses seven as the base theme.'
4+
version: VERSION
5+
core: 8.x
6+
base theme: seven
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
langcode: en
2+
status: true
3+
dependencies:
4+
module:
5+
- user
6+
id: user_batch_action_test_action
7+
label: 'Process user in batch'
8+
type: user
9+
plugin: user_batch_action_test_action
10+
configuration: { }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
action.configuration.user_batch_action_test_action:
2+
type: action_configuration_default
3+
label: 'Process user in batch'
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace Drupal\user_batch_action_test\Plugin\Action;
4+
5+
use Drupal\Core\Action\ActionBase;
6+
use Drupal\Core\Entity\ContentEntityInterface;
7+
use Drupal\Core\Session\AccountInterface;
8+
9+
/**
10+
* Provides action that sets batch precessing.
11+
*
12+
* @Action(
13+
* id = "user_batch_action_test_action",
14+
* label = @Translation("Process user in batch"),
15+
* type = "user",
16+
* )
17+
*/
18+
class BatchUserAction extends ActionBase {
19+
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public function executeMultiple(array $entities) {
24+
$operations = [];
25+
26+
foreach ($entities as $entity) {
27+
$operations[] = [
28+
[get_class($this), 'processBatch'],
29+
[
30+
[
31+
'entity_type' => $entity->getEntityTypeId(),
32+
'entity_id' => $entity->id(),
33+
],
34+
],
35+
];
36+
}
37+
38+
if ($operations) {
39+
$batch = [
40+
'operations' => $operations,
41+
'finished' => [get_class($this), 'finishBatch'],
42+
];
43+
batch_set($batch);
44+
}
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function execute(ContentEntityInterface $entity = NULL) {
51+
$this->executeMultiple([$entity]);
52+
}
53+
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
58+
return TRUE;
59+
}
60+
61+
/**
62+
* Processes the batch item.
63+
*
64+
* @param array $data
65+
* Keyed array of data to process.
66+
* @param array $context
67+
* The batch context.
68+
*/
69+
public static function processBatch($data, &$context) {
70+
if (!isset($context['results']['processed'])) {
71+
$context['results']['processed'] = 0;
72+
$context['results']['theme'] = \Drupal::service('theme.manager')->getActiveTheme(\Drupal::routeMatch())->getName();
73+
}
74+
$context['results']['processed']++;
75+
}
76+
77+
/**
78+
* Finish batch.
79+
*
80+
* @param bool $success
81+
* Indicates whether the batch process was successful.
82+
* @param array $results
83+
* Results information passed from the processing callback.
84+
*/
85+
public static function finishBatch($success, $results) {
86+
\Drupal::messenger()->addMessage(
87+
\Drupal::translation()->formatPlural($results['processed'], 'One item has been processed.', '@count items have been processed.')
88+
);
89+
\Drupal::messenger()->addMessage($results['theme'] . ' theme used');
90+
}
91+
92+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: 'User batch action test'
2+
type: module
3+
description: 'Support module for user batch action testing.'
4+
package: Testing
5+
version: VERSION
6+
core: 8.x
7+
dependencies:
8+
- views
9+
- user
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace Drupal\Tests\views\Functional;
4+
5+
use Drupal\Tests\BrowserTestBase;
6+
7+
/**
8+
* Tests the views bulk form with batch action.
9+
*
10+
* @group action
11+
* @see \Drupal\action\Plugin\views\field\BulkForm
12+
*/
13+
class UserBatchActionTest extends BrowserTestBase {
14+
15+
/**
16+
* Modules to install.
17+
*
18+
* @var array
19+
*/
20+
public static $modules = ['user', 'user_batch_action_test', 'views'];
21+
22+
/**
23+
* Tests user admin batch.
24+
*/
25+
public function testUserAction() {
26+
$themes = ['classy', 'seven', 'bartik', 'test_subseven'];
27+
$this->container->get('theme_installer')->install($themes);
28+
29+
$this->drupalLogin($this->rootUser);
30+
31+
foreach ($themes as $theme) {
32+
$this->config('system.theme')->set('default', $theme)->save();
33+
$this->drupalGet('admin/people');
34+
$edit = [
35+
'user_bulk_form[0]' => TRUE,
36+
'action' => 'user_batch_action_test_action',
37+
];
38+
$this->drupalPostForm(NULL, $edit, t('Apply'));
39+
$this->assertSession()->pageTextContains('One item has been processed.');
40+
$this->assertSession()->pageTextContains($theme . ' theme used');
41+
}
42+
}
43+
44+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Drupal\Tests\Core\Extension;
4+
5+
use Drupal\Tests\UnitTestCase;
6+
use Drupal\Core\Extension\Extension;
7+
8+
/**
9+
* Tests Extension serialization.
10+
*
11+
* @coversDefaultClass \Drupal\Core\Extension\Extension
12+
* @group Extension
13+
*/
14+
class ExtensionSerializationTest extends UnitTestCase {
15+
16+
/**
17+
* Tests dynamically assigned public properties kept when serialized.
18+
*
19+
* @covers ::__sleep
20+
* @covers ::__wakeup
21+
* @runInSeparateProcess
22+
*/
23+
public function testPublicProperties() {
24+
define('DRUPAL_ROOT', '/dummy/app/root');
25+
$extension = new Extension('/dummy/app/root', 'module', 'core/modules/system/system.info.yml', 'system.module');
26+
// Assign a public property dynamically.
27+
$extension->test = 'foo';
28+
$extension = unserialize(serialize($extension));
29+
$this->assertSame('foo', $extension->test);
30+
}
31+
32+
}

0 commit comments

Comments
 (0)