diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..5ec2adb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: ci +on: [push, pull_request] +jobs: + tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php_version: ["7.4", "8.0", "8.1"] + drupal_version: ["9", "10"] + exclude: + - php_version: "7.4" + drupal_version: "10" + - php_version: "8.0" + drupal_version: "10" + env: + PHP_VERSION: ${{ matrix.php_version }} + DRUPAL_VERSION: ${{ matrix.drupal_version }} + DOCKER_USER_ID: "1001" + steps: + - name: clone + uses: actions/checkout@v3 + - name: docker-compose up -d + run: docker-compose up -d + - name: composer self-update + run: docker-compose exec -T php composer self-update + - name: composer require + run: docker-compose exec -u ${DOCKER_USER_ID} -T php composer require --no-interaction --dev --no-update drupal/core:^${DRUPAL_VERSION} + - name: composer install + run: docker-compose exec -T php composer install + - name: composer test + run: docker-compose exec -T php composer test diff --git a/.gitignore b/.gitignore index 50dd546..102fcde 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,8 @@ composer.lock reports vendor drush +/drupal .phpunit.result.cache +/.editorconfig +/.gitattributes diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 69e2c7c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: php - -php: - - 7.4 - - 8.0 - - 8.1 - -install: - - composer install - -script: - - composer test diff --git a/CHANGELOG.md b/CHANGELOG.md index 52956b6..1e57de1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [2.2.1] + * [#250](https://github.com/jhedstrom/DrupalDriver/pull/250) Drupal 10 compatibility. ## [2.2.0] ### Fixed * [#232](https://github.com/jhedstrom/DrupalDriver/pull/232) Fixes typo in ImageHandler. diff --git a/composer.json b/composer.json index 6c7af2a..8bfda1a 100644 --- a/composer.json +++ b/composer.json @@ -15,16 +15,26 @@ "php": ">=7.4", "symfony/process": "~2.5|~3.0|~4.4|^6", "symfony/dependency-injection": "~2.6|~3.0|~4.4|^6", - "drupal/core-utility": "^8.4 || ^9 || ^10.0.0-alpha1" + "drupal/core-utility": "^8.4 || ^9 || ^10@beta" }, "require-dev": { + "composer/installers": "^2.1", "drupal/coder": "~8.3.0", "phpspec/phpspec": "~2.0 || ~4.0 || ~6.1 || dev-main", "phpunit/phpunit": "~6.0 || ~7.0 || ^9", - "mockery/mockery": "^0.9.4", + "mockery/mockery": "^1.5", + "drupal/core-composer-scaffold": "^8.4 || ^9 || ^10@beta", + "drupal/core-recommended": "^8.4 || ^9 || ^10@beta", + "drupal/mailsystem": "^4.4", "drush-ops/behat-drush-endpoint": "*", "php-parallel-lint/php-parallel-lint": "^1.0", - "dms/phpunit-arraysubset-asserts": "^0.4.0" + "dms/phpunit-arraysubset-asserts": "^0.4.0", + "mglaman/drupal-check": "^1", + "palantirnet/drupal-rector": "^0.13", + "symfony/phpunit-bridge": "^6.1" + }, + "conflict": { + "drupal/core": ">=8.0 <9.3" }, "scripts": { "test": [ @@ -32,8 +42,11 @@ "parallel-lint src spec tests", "phpunit", "phpspec run -f pretty --no-interaction", - "phpcs --standard=./phpcs-ruleset.xml ." - + "phpcs --standard=./phpcs-ruleset.xml .", + "./vendor/bin/drupal-check --drupal-root=drupal ./src/Drupal/Driver/Cores/Drupal8.php ./src/Drupal/Driver/Fields/Drupal8", + "cp ./vendor/palantirnet/drupal-rector/rector.php drupal/.", + "cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Cores/Drupal8.php --dry-run", + "cd drupal && ../vendor/bin/rector process ../src/Drupal/Driver/Fields/Drupal8 --dry-run" ] }, "autoload": { @@ -42,9 +55,34 @@ "Drupal\\Tests\\Driver" : "tests/" } }, + "repositories": { + "drupal": { + "type": "composer", + "url": "https://packages.drupal.org/8" + } + }, + "prefer-stable": true, + "minimum-stability": "beta", + "extra": { + "installer-paths": { + "drupal/core": [ + "type:drupal-core" + ], + "drupal/modules/{$name}": [ + "type:drupal-module" + ] + }, + "drupal-scaffold": { + "locations": { + "web-root": "drupal/" + } + } + }, "config": { "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true + "dealerdirect/phpcodesniffer-composer-installer": true, + "drupal/core-composer-scaffold": true, + "composer/installers": true } } } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..38dd332 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "2" +services: + + php: + image: wodby/drupal-php:${PHP_VERSION} + environment: + PHP_FPM_USER: wodby + PHP_FPM_GROUP: wodby + PHP_FPM_CLEAR_ENV: "yes" + PHP_OPCACHE_PRELOAD_USER: wodby + PHP_XDEBUG_MODE: "off" + PHP_XDEBUG_REMOTE_CONNECT_BACK: 1 + PHP_XDEBUG_REMOTE_HOST: "10.254.254.254" + PHP_XDEBUG_IDEKEY: "PHPSTORM" + PHP_IDE_CONFIG: "serverName=drupaldriver" + volumes: + - ./:/var/www/html diff --git a/phpcs-ruleset.xml b/phpcs-ruleset.xml index 97abe68..f87e72f 100644 --- a/phpcs-ruleset.xml +++ b/phpcs-ruleset.xml @@ -24,5 +24,6 @@ */vendor/* */CHANGELOG.md */README.md + ./drupal/* diff --git a/src/Drupal/Driver/Cores/Drupal7.php b/src/Drupal/Driver/Cores/Drupal7.php index 7c8d133..4736104 100644 --- a/src/Drupal/Driver/Cores/Drupal7.php +++ b/src/Drupal/Driver/Cores/Drupal7.php @@ -346,7 +346,7 @@ public function languageCreate(\stdClass $language) { // If the language code is not valid then throw an InvalidArgumentException. if (!isset($predefined_languages[$language->langcode])) { - throw new InvalidArgumentException("There is no predefined language with langcode '{$language->langcode}'."); + throw new \InvalidArgumentException("There is no predefined language with langcode '{$language->langcode}'."); } // Enable a language only if it has not been enabled already. diff --git a/src/Drupal/Driver/Cores/Drupal8.php b/src/Drupal/Driver/Cores/Drupal8.php index 4379c16..a681767 100644 --- a/src/Drupal/Driver/Cores/Drupal8.php +++ b/src/Drupal/Driver/Cores/Drupal8.php @@ -4,6 +4,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Routing\RouteObjectInterface; use Drupal\Driver\Exception\BootstrapException; use Drupal\field\Entity\FieldStorageConfig; use Drupal\language\Entity\ConfigurableLanguage; @@ -15,7 +16,6 @@ use Drupal\taxonomy\TermInterface; use Drupal\user\Entity\Role; use Drupal\user\Entity\User; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; @@ -53,9 +53,12 @@ public function bootstrap() { $request = Request::createFromGlobals(); $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod'); $kernel->boot(); - // A route is required for route matching. - $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('')); - $request->attributes->set(RouteObjectInterface::ROUTE_NAME, ''); + // A route is required for route matching. In order to support Drupal 10 + // along with 8/9, we use the hardcoded values of RouteObjectInterface + // constants ROUTE_NAME and ROUTE_OBJECT. + // @see https://www.drupal.org/node/3151009 + $request->attributes->set('_route_object', new Route('')); + $request->attributes->set('_route', ''); $kernel->preHandle($request); // Initialise an anonymous session. required for the bootstrap. @@ -75,6 +78,7 @@ public function clearCache() { */ public function nodeCreate($node) { // Throw an exception if the node type is missing or does not exist. + /** @var \Drupal\node\Entity\Node $node */ if (!isset($node->type) || !$node->type) { throw new \Exception("Cannot create content because it is missing the required property 'type'."); } @@ -83,11 +87,12 @@ public function nodeCreate($node) { $bundle_info = \Drupal::service('entity_type.bundle.info'); $bundles = $bundle_info->getBundleInfo('node'); if (!in_array($node->type, array_keys($bundles))) { - throw new \Exception("Cannot create content because provided content type '$node->type' does not exist."); + throw new \Exception(sprintf('Cannot create content because provided content type %s does not exist.', $node->type)); } // If 'author' is set, remap it to 'uid'. if (isset($node->author)) { $user = user_load_by_name($node->author); + /** @var \Drupal\user\Entity\User $user */ if ($user) { $node->uid = $user->id(); } @@ -329,8 +334,13 @@ public function termCreate(\stdClass $term) { $term->vid = $term->vocabulary_machine_name; if (isset($term->parent)) { - $parent = \taxonomy_term_load_multiple_by_name($term->parent, $term->vocabulary_machine_name); + $query = \Drupal::entityQuery('taxonomy_term') + ->accessCheck(FALSE) + ->condition('id', $term->parent) + ->condition('vid', $term->vocabulary_machine_name); + $parent = $query->execute(); if (!empty($parent)) { + /** @var \Drupal\taxonomy\Entity\Term $parent */ $parent = reset($parent); $term->parent = $parent->id(); } @@ -380,12 +390,12 @@ public function getExtensionPathList() { * * @param string $entity_type * The entity type for which to return the field types. - * @param object $entity + * @param \StdClass $entity * Entity object. * @param array $base_fields * Base fields to be expanded in addition to user defined fields. */ - public function expandEntityBaseFields($entity_type, \stdClass $entity, array $base_fields) { + public function expandEntityBaseFields($entity_type, \StdClass $entity, array $base_fields) { $this->expandEntityFields($entity_type, $entity, $base_fields); } @@ -436,7 +446,7 @@ public function languageCreate(\stdClass $language) { if (!ConfigurableLanguage::load($langcode)) { $created_language = ConfigurableLanguage::createFromLangcode($language->langcode); if (!$created_language) { - throw new InvalidArgumentException("There is no predefined language with langcode '{$langcode}'."); + throw new \InvalidArgumentException("There is no predefined language with langcode '{$langcode}'."); } $created_language->save(); return $language; diff --git a/src/Drupal/Driver/DrushDriver.php b/src/Drupal/Driver/DrushDriver.php index 2086e8a..bc50179 100644 --- a/src/Drupal/Driver/DrushDriver.php +++ b/src/Drupal/Driver/DrushDriver.php @@ -414,7 +414,7 @@ public function drush($command, array $arguments = [], array $options = []) { // Add any global arguments. $global = $this->getArguments(); - $process = new Process("{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}"); + $process = Process::fromShellCommandline("{$this->binary} {$alias} {$string_options} {$global} {$command} {$arguments}"); $process->setTimeout(3600); $process->run(); diff --git a/src/Drupal/Driver/Fields/Drupal8/AbstractHandler.php b/src/Drupal/Driver/Fields/Drupal8/AbstractHandler.php index cda97d5..62bb0f3 100644 --- a/src/Drupal/Driver/Fields/Drupal8/AbstractHandler.php +++ b/src/Drupal/Driver/Fields/Drupal8/AbstractHandler.php @@ -25,7 +25,7 @@ abstract class AbstractHandler implements FieldHandlerInterface { /** * Constructs an AbstractHandler object. * - * @param object $entity + * @param \StdClass $entity * The simulated entity object containing field information. * @param string $entity_type * The entity type. @@ -35,7 +35,7 @@ abstract class AbstractHandler implements FieldHandlerInterface { * @throws \Exception * Thrown when the given field name does not exist on the entity. */ - public function __construct(\stdClass $entity, $entity_type, $field_name) { + public function __construct(\StdClass $entity, $entity_type, $field_name) { if (empty($entity_type)) { throw new \Exception("You must specify an entity type in order to parse entity fields."); } diff --git a/src/Drupal/Driver/Fields/Drupal8/ImageHandler.php b/src/Drupal/Driver/Fields/Drupal8/ImageHandler.php index d042bfb..b314fcc 100644 --- a/src/Drupal/Driver/Fields/Drupal8/ImageHandler.php +++ b/src/Drupal/Driver/Fields/Drupal8/ImageHandler.php @@ -17,9 +17,7 @@ public function expand($values) { } /** @var \Drupal\file\FileInterface $file */ - $file = file_save_data( - $data, - 'public://' . uniqid() . '.jpg'); + $file = \Drupal::service('file.repository')->writeData($data, 'public://' . uniqid() . '.jpg'); if (FALSE === $file) { throw new \Exception("Error saving file");