Skip to content

Commit fa9764f

Browse files
authored
refactor(drupal): Drop Drupal 8 support, introduce Drupal 10 compat (#1267)
1 parent 9e4f4dd commit fa9764f

File tree

24 files changed

+95
-87
lines changed

24 files changed

+95
-87
lines changed

.github/workflows/testing.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ jobs:
1616
php-versions: ['7.3', '7.4', '8.0', '8.1']
1717
drupal-core: ['9.3.x']
1818
include:
19-
# Extra run to also test on latest Drupal 8 and PHP 7.2.
20-
- php-versions: '7.2'
21-
drupal-core: '8.9.x'
19+
# Extra run to also test on latest Drupal 10 and PHP 8.1.
20+
- php-versions: '8.1'
21+
drupal-core: '10.0.x'
2222
steps:
2323
- name: Checkout Drupal core
2424
uses: actions/checkout@v2
@@ -73,17 +73,18 @@ jobs:
7373
run: |
7474
composer install --no-progress --prefer-dist --optimize-autoloader
7575
composer --no-interaction run-script drupal-phpunit-upgrade
76+
composer config --no-plugins allow-plugins.phpstan/extension-installer true
7677
7778
- name: Install GraphQL dependencies
7879
run: composer --no-interaction --no-progress require \
7980
webonyx/graphql-php:^14.8 \
8081
drupal/typed_data:^1.0 \
81-
drupal/redirect:^1.6 \
8282
phpstan/phpstan:^1.4.6 \
8383
mglaman/phpstan-drupal:^1.1.2 \
8484
phpstan/phpstan-deprecation-rules:^1.0.0 \
8585
jangregor/phpstan-prophecy:^1.0.0 \
86-
phpstan/phpstan-phpunit:^1.0.0
86+
phpstan/phpstan-phpunit:^1.0.0 \
87+
phpstan/extension-installer:^1.0
8788

8889
# We install Coder separately because updating did not work in the local
8990
# Drupal vendor dir.

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"homepage": "http://drupal.org/project/graphql",
66
"license": "GPL-2.0+",
77
"require": {
8-
"php": ">=7.2",
8+
"php": ">=7.3",
99
"webonyx/graphql-php": "^14.8.0"
1010
},
1111
"minimum-stability": "dev"

examples/graphql_composable/graphql_composable.info.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ name: GraphQL composable example
22
type: module
33
description: 'Examples for composable GraphQL schema.'
44
package: GraphQL
5-
core: 8.x
65
dependencies:
76
- graphql:graphql
87
- node:node
9-
core_version_requirement: ^8 || ^9
8+
core_version_requirement: ^9.3 || ^10

examples/graphql_composable/src/Plugin/GraphQL/DataProducer/CreateArticle.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Drupal\graphql_composable\GraphQL\Response\ArticleResponse;
99
use Drupal\node\Entity\Node;
1010
use Symfony\Component\DependencyInjection\ContainerInterface;
11+
use Drupal\Core\StringTranslation\StringTranslationTrait;
1112

1213
/**
1314
* Creates a new article entity.
@@ -28,6 +29,8 @@
2829
*/
2930
class CreateArticle extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
3031

32+
use StringTranslationTrait;
33+
3134
/**
3235
* The current user.
3336
*

examples/graphql_example/graphql_examples.info.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ name: GraphQL examples
22
type: module
33
description: 'Examples for the GraphQL module.'
44
package: GraphQL
5-
core: 8.x
65
dependencies:
76
- graphql:graphql
87
- node:node
9-
core_version_requirement: ^8 || ^9
8+
core_version_requirement: ^9.3 || ^10

graphql.info.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ type: module
33
description: 'Base module for integrating GraphQL with Drupal.'
44
package: GraphQL
55
configure: graphql.config_page
6-
core: 8.x
7-
core_version_requirement: ^8 || ^9
6+
core_version_requirement: ^9.3 || ^10
87
dependencies:
98
- typed_data:typed_data

graphql.services.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ services:
159159
- '@lock'
160160
- '@config.factory'
161161
- '@renderer'
162+
- '@event_dispatcher'
162163

163164
plugin.manager.graphql.persisted_query:
164165
class: Drupal\graphql\Plugin\PersistedQueryPluginManager

phpstan.neon

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
includes:
2-
- ../../vendor/phpstan/phpstan-deprecation-rules/rules.neon
3-
- ../../vendor/mglaman/phpstan-drupal/extension.neon
4-
- ../../vendor/jangregor/phpstan-prophecy/extension.neon
5-
- ../../vendor/phpstan/phpstan-phpunit/extension.neon
6-
71
parameters:
82
# PHPStan cannot find files in this test directory automatically.
93
scanDirectories:
@@ -21,6 +15,10 @@ parameters:
2115
# Not sure we can specify generic types properly with Drupal coding standards
2216
# yet, disable for now.
2317
checkGenericClassInNonGenericObjectType: false
18+
# Exclude the RouteLoad producer because the redirect module is not D10
19+
# compatible so we are not downloading it.
20+
excludePaths:
21+
- src/Plugin/GraphQL/DataProducer/Routing/RouteLoad.php
2422
ignoreErrors:
2523
# @todo Ignore phpstan-drupal extension's rules for now, activate later.
2624
- '#\Drupal calls should be avoided in classes, use dependency injection instead#'
@@ -31,28 +29,7 @@ parameters:
3129
- "#^Access to an undefined property Drupal\\\\#"
3230
# PHPUnit deprecation warnings in Drupal 9 that we don't care about.
3331
- "#^Call to deprecated method setMethods\\(\\) of class PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder:#"
34-
# Other deprecations in Drupal 9 that we can't change because we want to
35-
# support Drupal 8.
36-
- "#deprecated class Symfony\\\\Component\\\\EventDispatcher\\\\Event#"
37-
- "#deprecated class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\GetResponseEvent#"
3832
- "#^Method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) invoked with 2 parameters, 1 required\\.$#"
39-
- "#deprecated interface Symfony\\\\Component\\\\HttpFoundation\\\\File\\\\MimeType\\\\MimeTypeGuesserInterface#"
40-
- "#^Parameter .+ of class Symfony\\\\Component\\\\HttpFoundation\\\\File\\\\UploadedFile constructor expects .+ given\\.$#"
41-
- "#^Parameter \\#1 \\$event of method Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\:\\:dispatch\\(\\) expects object, string given\\.$#"
42-
- """
43-
#^Call to deprecated method toInt\\(\\) of class Drupal\\\\Component\\\\Utility\\\\Bytes\\:
44-
in drupal\\:9\\.1\\.0 and is removed from drupal\\:10\\.0\\.0\\. Use \\\\Drupal\\\\Component\\\\Utility\\\\Bytes\\:\\:toNumber\\(\\) instead$#
45-
"""
46-
- """
47-
#^Call to deprecated function file_munge_filename\\(\\)#
48-
"""
49-
- """
50-
#^Call to deprecated function drupal_get_path\\(\\)#
51-
"""
52-
- """
53-
#^Call to deprecated function file_create_url\\(\\)#
54-
"""
55-
- "#^Call to an undefined method Drupal\\\\Tests\\\\graphql\\\\Kernel\\\\DataProducer\\\\EntityTest\\:\\:assertMatchesRegularExpression\\(\\)\\.$#"
5633
# Drupal allows object property access to custom fields, so we cannot fix
5734
# that.
5835
- "#^Property Drupal\\\\.+ \\(Drupal\\\\Core\\\\Field\\\\FieldItemListInterface\\) does not accept .+\\.$#"

src/Event/OperationEvent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use Drupal\graphql\GraphQL\Execution\ResolveContext;
66
use GraphQL\Executor\ExecutionResult;
7-
use Symfony\Component\EventDispatcher\Event;
7+
use Drupal\Component\EventDispatcher\Event;
88

99
/**
1010
* Represents an event that is triggered before and after a GraphQL operation.

src/EventSubscriber/SubrequestSubscriber.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Drupal\language\LanguageNegotiatorInterface;
99
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1010
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
11-
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
11+
use Symfony\Component\HttpKernel\Event\RequestEvent;
1212
use Symfony\Component\HttpKernel\KernelEvents;
1313

1414
/**
@@ -36,10 +36,10 @@ public function __construct(LanguageManagerInterface $languageManager, Translato
3636
/**
3737
* Handle kernel request events.
3838
*
39-
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
39+
* @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
4040
* The kernel event object.
4141
*/
42-
public function onKernelRequest(GetResponseEvent $event): void {
42+
public function onKernelRequest(RequestEvent $event): void {
4343
$request = $event->getRequest();
4444
if (!$request->attributes->has('_graphql_subrequest')) {
4545
return;

src/GraphQL/Execution/Executor.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,11 +277,11 @@ protected function doExecuteUncached() {
277277
);
278278

279279
$event = new OperationEvent($this->context);
280-
$this->dispatcher->dispatch(OperationEvent::GRAPHQL_OPERATION_BEFORE, $event);
280+
$this->dispatcher->dispatch($event, OperationEvent::GRAPHQL_OPERATION_BEFORE);
281281

282282
return $executor->doExecute()->then(function ($result) {
283283
$event = new OperationEvent($this->context, $result);
284-
$this->dispatcher->dispatch(OperationEvent::GRAPHQL_OPERATION_AFTER, $event);
284+
$this->dispatcher->dispatch($event, OperationEvent::GRAPHQL_OPERATION_AFTER);
285285

286286
$this->logUnsafeErrors($this->context->getOperation(), $result);
287287

@@ -443,7 +443,13 @@ protected function cacheWrite($prefix, CacheableExecutionResult $result) {
443443
* @see \Drupal\Core\Cache\CacheBackendInterface::set()
444444
*/
445445
protected function maxAgeToExpire($maxAge) {
446-
$time = $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME');
446+
/* @todo Can be removed when D9 support is dropped. In D9
447+
* \Drupal\Core\Http\RequestStack is used here for forward compatibility,
448+
* but phpstan thinks it's \Symfony\Component\HttpFoundation\RequestStack
449+
* which doesn't have getMainRequest(), but in Drupal10 (Symfony 6) it has.
450+
*/
451+
/* @phpstan-ignore-next-line */
452+
$time = $this->requestStack->getMainRequest()->server->get('REQUEST_TIME');
447453
return ($maxAge === Cache::PERMANENT) ? Cache::PERMANENT : (int) $time + $maxAge;
448454
}
449455

src/GraphQL/Utility/FileUpload.php

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
use Drupal\Core\Utility\Token;
2020
use Drupal\file\FileInterface;
2121
use Drupal\graphql\GraphQL\Response\FileUploadResponse;
22-
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface;
2322
use Symfony\Component\HttpFoundation\File\UploadedFile;
23+
use Drupal\Core\File\Event\FileUploadSanitizeNameEvent;
24+
use Symfony\Component\Mime\MimeTypeGuesserInterface;
25+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2426

2527
/**
2628
* Service to manage file uploads within GraphQL mutations.
@@ -48,7 +50,7 @@ class FileUpload {
4850
/**
4951
* The mime type guesser service.
5052
*
51-
* @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
53+
* @var \Symfony\Component\Mime\MimeTypeGuesserInterface
5254
*/
5355
protected $mimeTypeGuesser;
5456

@@ -94,6 +96,13 @@ class FileUpload {
9496
*/
9597
protected $renderer;
9698

99+
/**
100+
* The event dispatcher service.
101+
*
102+
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
103+
*/
104+
protected $eventDispatcher;
105+
97106
/**
98107
* Constructor.
99108
*/
@@ -106,7 +115,8 @@ public function __construct(
106115
Token $token,
107116
LockBackendInterface $lock,
108117
ConfigFactoryInterface $config_factory,
109-
RendererInterface $renderer
118+
RendererInterface $renderer,
119+
EventDispatcherInterface $eventDispatcher
110120
) {
111121
/** @var \Drupal\file\FileStorageInterface $file_storage */
112122
$file_storage = $entityTypeManager->getStorage('file');
@@ -119,6 +129,7 @@ public function __construct(
119129
$this->lock = $lock;
120130
$this->systemFileConfig = $config_factory->get('system.file');
121131
$this->renderer = $renderer;
132+
$this->eventDispatcher = $eventDispatcher;
122133
}
123134

124135
/**
@@ -127,14 +138,14 @@ public function __construct(
127138
* @param array $settings
128139
* The file field settings.
129140
*
130-
* @return int
141+
* @return float
131142
* Max upload size.
132143
*/
133-
protected function getMaxUploadSize(array $settings) {
144+
protected function getMaxUploadSize(array $settings): float {
134145
// Cap the upload size according to the PHP limit.
135-
$max_filesize = Bytes::toInt(Environment::getUploadMaxSize());
146+
$max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
136147
if (!empty($settings['max_filesize'])) {
137-
$max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
148+
$max_filesize = min($max_filesize, Bytes::toNumber($settings['max_filesize']));
138149
}
139150
return $max_filesize;
140151
}
@@ -240,7 +251,7 @@ public function saveFileUpload(UploadedFile $uploaded_file, array $settings): Fi
240251
$file = $this->fileStorage->create([]);
241252
$file->setOwnerId($this->currentUser->id());
242253
$file->setFilename($prepared_filename);
243-
$file->setMimeType($this->mimeTypeGuesser->guess($prepared_filename));
254+
$file->setMimeType($this->mimeTypeGuesser->guessMimeType($prepared_filename));
244255
$file->setFileUri($temp_file_path);
245256
// Set the size. This is done in File::preSave() but we validate the file
246257
// before it is saved.
@@ -378,13 +389,15 @@ protected function prepareFilename(string $filename, array &$validators): string
378389
// valid extensions, munge the filename to protect against possible
379390
// malicious extension hiding within an unknown file type. For example,
380391
// "filename.html.foo".
381-
$filename = file_munge_filename($filename, $validators['file_validate_extensions'][0]);
392+
$event = new FileUploadSanitizeNameEvent($filename, $validators['file_validate_extensions'][0]);
393+
$this->eventDispatcher->dispatch($event);
394+
$filename = $event->getFilename();
382395
}
383396

384397
// Rename potentially executable files, to help prevent exploits (i.e.
385398
// will rename filename.php.foo and filename.php to filename._php._foo.txt
386399
// and filename._php.txt, respectively).
387-
if (preg_match(FILE_INSECURE_EXTENSION_REGEX, $filename)) {
400+
if (preg_match(FileSystemInterface::INSECURE_EXTENSION_REGEX, $filename)) {
388401
// If the file will be rejected anyway due to a disallowed extension, it
389402
// should not be renamed; rather, we'll let file_validate_extensions()
390403
// reject it below.
@@ -401,7 +414,10 @@ protected function prepareFilename(string $filename, array &$validators): string
401414
// URI.
402415
$filename .= '.txt';
403416
}
404-
$filename = file_munge_filename($filename, $validators['file_validate_extensions'][0] ?? '');
417+
418+
$event = new FileUploadSanitizeNameEvent($filename, $validators['file_validate_extensions'][0] ?? '');
419+
$this->eventDispatcher->dispatch($event);
420+
$filename = $event->getFilename();
405421

406422
// The .txt extension may not be in the allowed list of extensions. We
407423
// have to add it here or else the file upload will fail.
@@ -461,9 +477,9 @@ protected function getUploadValidators(array $settings): array {
461477
];
462478

463479
// Cap the upload size according to the PHP limit.
464-
$max_filesize = Bytes::toInt(Environment::getUploadMaxSize());
480+
$max_filesize = Bytes::toNumber(Environment::getUploadMaxSize());
465481
if (!empty($settings['max_filesize'])) {
466-
$max_filesize = min($max_filesize, Bytes::toInt($settings['max_filesize']));
482+
$max_filesize = min($max_filesize, Bytes::toNumber($settings['max_filesize']));
467483
}
468484

469485
// There is always a file size limit due to the PHP server limit.

src/Plugin/GraphQL/DataProducer/DataProducerPluginBase.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@
33
namespace Drupal\graphql\Plugin\GraphQL\DataProducer;
44

55
use Drupal\Component\Plugin\Exception\ContextException;
6-
use Drupal\Core\Plugin\ContextAwarePluginBase;
6+
use Drupal\Component\Plugin\ContextAwarePluginBase;
77
use Drupal\graphql\GraphQL\Execution\FieldContext;
88
use Drupal\graphql\Plugin\DataProducerPluginInterface;
9+
use Drupal\Core\Plugin\ContextAwarePluginTrait;
910

1011
/**
1112
* Base class for data producers that resolve fields for queries or mutations.
1213
*/
1314
abstract class DataProducerPluginBase extends ContextAwarePluginBase implements DataProducerPluginInterface {
1415
use DataProducerPluginCachingTrait;
16+
use ContextAwarePluginTrait;
1517

1618
/**
1719
* {@inheritdoc}

src/Plugin/GraphQL/DataProducer/DataProducerProxy.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ protected function cacheWrite($prefix, $value, FieldContext $field): void {
363363
* @see \Drupal\Core\Cache\CacheBackendInterface::set()
364364
*/
365365
protected function maxAgeToExpire($maxAge) {
366-
$time = $this->requestStack->getMasterRequest()->server->get('REQUEST_TIME');
366+
/* @phpstan-ignore-next-line */
367+
$time = $this->requestStack->getMainRequest()->server->get('REQUEST_TIME');
367368
return ($maxAge === Cache::PERMANENT) ? Cache::PERMANENT : (int) $time + $maxAge;
368369
}
369370

0 commit comments

Comments
 (0)