fromJune 2014
Feature:

Upgrading Your Modules

What You Need To Know
1

Drupal's philosophy regarding backward compatibility is "the Drop is always moving". In order to create a framework that is as performant, scalable, and extensible as possible, each major release of Drupal can and will make changes, often radical changes, to its developer APIs in order to provide optimal solutions for Drupal users and developers.

To this end, Drupal 8, far more-so than any previous release, has undergone extensive refactoring under the hood. It sports an object-oriented architecture powered by Symfony components. In addition, it utilizes modern PHP (5.4 or later) best-practices, a new Plugin API that provides consistency for pluggable pieces such as blocks and image styles, a revamped and complete Entity and Field API, a new Configuration API to provide fully deployable settings, and numerous other great improvements.

The flip-side is that while a data migration path is always provided between major versions of Drupal for a site's content and users (and in Drupal 8's case, from both Drupal 6 and Drupal 7), migrating the code of contributed and custom modules is left for developers to do.

This article will therefore provide some starting points for folks trying to port their modules from Drupal 7 to Drupal 8. (If you still have Drupal 6 modules kicking around, the "Coder Upgrade" sub-module of Coder will get you a fair chunk of the way towards converting them to Drupal 7.)

Note that as of this writing, Drupal 8 is still in active development. While the hope is that by the time this article is published, Drupal 8 will be at least in beta, and the APIs relatively stable (apart from API changes necessary to fix critical issues), information here could still change prior to D8’s final release.

If you don't have a module of your own to port, but still want to cut your teeth on Drupal 8 code, an excellent place to start is porting one of the example projects from Examples for Developers module. Once you get some experience under your belt, drupalupgrade.info tracks the top 50 contributed modules in Drupal 7 and their current porting status, and is another great place to jump in and contribute.

Pre-Requisites

Change records for Drupal core is the definitive resource on API changes between Drupal 7 and Drupal 8, as well as where you most likely will be spending the better part of your life while porting your module to Drupal 8.

Before digging in, it's helpful to have the following tools installed and links handy:

  • Drupal 8 installed on your development environment (required):

    Note that Drupal 8 requires PHP 5.4 or later, so plan accordingly. You should download and port against the most recent alpha/beta of Drupal 8 available, since the tip of the 8.x branch changes quite frequently.

  • List of Drupal 8 API Change Records (required): Each of Drupal 8's API changes is associated with a "change record" which details what change was made, why (including links to the issues that made the change), and some before/after code between Drupal 7 and Drupal 8. This will become your go-to resource as you find errors in your module and look up how to fix them. :)
  • Drupal 8 API documentation (required): These pages provide architectural overview/example documentation for the major API changes in Drupal 8, and the API reference allows you to look up the documentation for specific classes and functions.
  • The "master" branch of Drush (recommended): If you're not familiar with Drush – a command-line interface for Drupal – it's about to become your very best friend. Commands such as drush si (to reinstall your Drupal 8 site from scratch if something goes awry) and drush cache-rebuild (which clears both database and code caches so new code changes show up) will prove very useful as you go along.
  • Drupal Module Upgrader (optional): This is a command-line script that's still incomplete and under development; it leverages the PHP_CodeSniffer library. It will scan your module, detect several of the changes needed between Drupal 7 and 8, and, where possible, will automatically make the necessary code changes to the module files directly. Your first time out, you may want to port your code the “hard” way to help the new changes in Drupal 8 “stick” with you more, but reading the generated report can provide a useful starting point, with pointers off to specific Drupal 8 change notices that affect your module's code.

The Module Porting Process

Read through all the steps first to have them in your mind. We will then walk through the first problem you will encounter – getting your module to appear on the modules page.

  1. Place the 7.x version of your module into the /modules folder of your 8.x site. Yes, you heard that right, /modules, not /sites/all/modules (although that will still work). Drupal 8 has now moved all of the core modules, themes, includes, etc. under a top-level /core directory, so now /modules is the place to be.
  2. Find a problem, such as a fatal error or missing output. With over 700 API changes between Drupal 7 and 8, this shouldn't be very hard. ;)
  3. Search https://drupal.org/list-changes for keywords related to the problem, then follow the instructions there to fix it. Note that change records are “wiki” pages and can be edited by any Drupal.org user, so if you spot mistakes or things that could use clarification, please help clean 'em up!
  4. If porting a contrib module, upload your code to Drupal.org as you go. Even if it's not fully baked yet, some starter 8.x code can still be really useful to folks and provide something to build from.
  5. GOTO step 2, until there are no more problems. Out of problems? Time for some ice cream! You've earned it!

Example: Getting Your Module to Show Up

While porting your module to Drupal 8, your first challenge is getting it to actually appear on the modules page.

When you try and complete step 1 above, the first thing you'll notice is that your module doesn't appear at all on the Modules page. Great, we're already at step 2: we found a problem!

Quiz time: What actually makes a module appear on that page, and where does Drupal get information to display such a module's name and description?

If you said ".info files," you are correct!

So now we're at step 3.

Search the API change records at https://drupal.org/list-changes for ".info file." One of the results should be http://wdog.it/4/1/yml, which explains that .info files in Drupal 7 have now been moved to .info.yml files in Drupal 8, and are written with the widely used YAML standard (http://wdog.it/4/1/yaml), rather than the Drupalism of “not-quite-INI-but-similar” .info syntax.

The change record is typical of most, and includes links to the issue(s) from which the change originated so you can read up on the background, an explanation of why the change was made, and before/after code snippets you can analyze and copy/paste from.

Let's take a look at an example conversion:

Drupal 7: example.info

name = Example
description = An example module.
core = 7.x
files[] = example.test
dependencies[] = user

Drupal 8: example.info.yml

name: Example
description: An example module.
core: 8.x
dependencies:
  - user
# Note: New property required as of Drupal 8!
type: module

By making changes similar to these in your own module's .info file, you should then see a checkbox on the Modules page.

Progress!

Now go ahead and turn the module on and go back to step 2 and find more problems. ;)

You'll find a lot of YAML in Drupal 8, and this is indicative of a general trend in Drupal 8 of “Proudly Found Elsewhere,” where we've made a concerted effort to look outside of the Drupal ecosystem to find the best solutions to problems and incorporate them into the software (contrasted to "Not Invented Here," which aims to avoid external solutions).

In Drupal 8, you'll find many examples of Proudly Found Elsewhere, from the PSR-0/4 class autoloader (as opposed to a special classes[] key in the .info file and a registry table), the PHPUnit testing framework (as opposed to Drupal's heavily customized fork of the SimpleTest library), to the Guzzle HTTP client (as opposed to drupal_http_request(), the function in Drupal with the highest cyclomatic complexity), and more.

This is all indicative of a general movement and philosophy change in core development toward Drupal participating more actively in the wider PHP community, and is welcomed by many, though it has not been without controversy.

Debugging Techniques

As you port your module, there's a good chance you're going to run into spots where your site blows up, and you need a way to recover. Here are some debugging tips for you as you go along porting your module, some of them new to Drupal 8.

  • Comment out hook_install() and hook_uninstall(). If your module previously did fancy things on installation, you may want to comment out this code until you get further along, so you can at least get the module turned on.
  • Use the debug() function and make ample use of your PHP error log.debug() is useful for outputting the result of a variable in Drupal's messages area. That is, as long as the site is responding. If it's not, and you're only getting a “white screen of death,” you should check your web server's PHP error log instead.
  • Run the rebuild.php script (or drush rebuild), new to Drupal 8. For improved performance, there is now a compiled cache of PHP in the files directory under /sites/default/files/php. If this gets stale, it can cause scary-looking errors. The script – and equivalent Drush command – will force a rebuild of the underlying system, including the caches, the compiled PHP files, etc. in order to re-jigger things. Think of it as drush cc all on steroids. Note that in order to run the rebuild.php script, you must edit your settings.php file to set $settings['rebuild_access'] = TRUE;.
  • Uninstall/reinstall your module, or worst-case, reinstall the entire site. Since there are no 8.x => 8.x content upgrades supported yet (this will happen in a later beta/release candidate), it's recommended that you keep your test site fairly sparse while doing initial development. Note that you now need to clear the sites/default/files/php directory on site reinstalls as well, if you don't use drush si.

Key API Changes

The Drupal 8 APIs portion of Drupal.org's documentation contains useful architectural overview information on all of Drupal 8's major new APIs.

While there's obviously not enough space in this article to cover all of the API changes in Drupal 8 (and some of the heavy-hitters are still under active development), here are some pointers to some of the big ones that will affect most modules.

  • Background and pre-requisites: This resource provides an overview of the new vocabulary/concepts in Drupal 8, including namespaces, Plugins, and Annotations.
  • .info files are now .info.yml files: YAML is also used to define menu routes, libraries, configuration schemas, services, and more, so it's worth spending some time brushing up on the syntax.
  • PSR-0 compatible class loader in core: This change record describes how to name your class files so that Drupal can pick them up. Examples of where you use classes in Drupal 8 include page callbacks, blocks, entities, fields, automated tests, and more, so this is a key new concept.
  • hook_menu() replaced with new routing system: This is oa key new architectural change utilizing the Symfony 2 framework that de-couples menu routes (the URL → page callback mapping, now defined in modulename.routing.yml) from menu links (navigation items, now defined in hook_menu_link_defaults()). For more detailed information on the new routing system, see the documentation.
  • New configuration system: In Drupal 8, variable_get(), variable_set() and other associated functions have been retired in favor of the new file-based Configuration and State APIs, and now work for far more than simple variables, but also larger concepts such as content types and views. Use the Configuration API> for “deployable” changes (settings which you want to take effect across multiple environments) and use the State API more info at for settings specific to a given environment, such as the last time Drupal cron ran.
  • "Info" hooks now generally use the new Plugin system. If your module previously defined a hook_something_info(), chances are good that it now uses the new plugin system, which involves declaring a new class with an "annotation" (specially formatted PHP comments) above it, which provides metadata. Examples in core are blocks, image styles, text formats, field widgets and formatters, and more. Plugin API in Drupal 8 has more detailed information on the new Plugin system.
  • New theme system: Drupal 8’s markup engine has moved from PHPTemplate to Twig, which offers a clear separation between presentation and logic, for ease of use for themers, as well as better security. If your module provides any theme-able output, it now needs to provide a Twig template. Read more about .
  • New Entity Field API: A new object-oriented API for entities and fields for much better readability. Read more about Entity API in Drupal 8.

Happy porting! :)

Image: ©iStockphoto.com/RichVintage

Comments

Apparently, drupalupgrade.info is in need of some love, too,