Drupal, like other web applications, receives an HTTP request for a resource and returns a response. This is a multi-step process that involves decoding the request and mapping it to a predefined callback. When the callback function is executed, the requested resource is prepared and returned to the caller.

Let us take a closer look at the way a simple page is built and displayed in both Drupal 7 and 8.

Drupal 7

In a module, for example simple_page_demo, we implement a hook_menu function as follows:

// simple_page_demo.module

/**
 * Implements hook_menu().
 */
function simple_page_demo_menu() {
  $items = array();
  $items['simple-page-demo'] = array(
    'title' => 'Simple page demo',
    'description' => 'Demonstrates building a simple page',
    'page callback' => 'simple_page_demo_output',
    'access arguments' => array('access content'),
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

The key to the $items array is the path to the page (/simple-page-demo), which is an associative array. We have defined a type key as a normal menu item. Thus we will see a link to our page in the main navigation block. The title is the link title. If we do not set a page title, this will also be the page title. Access to the menu is controlled by the access arguments key, and, in this case, all roles with the "access content" permission are granted access to this menu item.

The value of the page callback key is the name of the function where the page is built and returned. The function should return a string, HTML, or any output that the caller can understand and present to the end user. It may be as simple as this:

/**
 * Build page output.
 *
 * @return string
 */
function simple_page_demo_output() {
  drupal_set_title("Simple page title");
  return "This is a simple page";
}

When an HTTP request for /simple-page-demo is received in index.php, the menu_execute_active_handler() function is called. That is where the /simple-page-demo path is matched to the correct menu item and the page callback function is executed.

Ideally, this function should return a renderable array, but an HTML string is also acceptable. If there is no content, NULL may be returned. Alternatively, an integer representing a defined menu status constant or status code (e.g. MENU_NOT_FOUND, MENU_ACCESS_DENIED, MENU_SITE_OFFLINE, etc.) may be returned too. By the way, having a menu status code as the output is not ideal, because you may return MENU_SITE_OFFLINE while strictly speaking, the site is not offline at all.

For the purpose of our demonstration, the returned simple string is sufficient. Later, the Drupal 7 templating system takes over, and the page is displayed.

Drupal 8

In Drupal 8, our custom code lives in a module too. Paths are defined in a YAML routing configuration file. Again, assuming we have a simple_page_demo module, this will be our simple_page_demo.routing.yml file:

# simple_page_demo.routing.yml
simple_page_demo.page:
  path: '/simple-page-demo'
  defaults:
    _title: 'Simple page title'
    _controller: '\Drupal\simple_page_demo\Controller\SimplePageController::page'
  requirements:
     _access: 'TRUE'

The id of this route is simple_page_demo.page and it should be unique. _title is the page title. We specify where the page content will come from in the value of the _controller key. The controller class needs to be created with the implementation of page() method. Here, we return a renderable array:

<?php

namespace Drupal\simple_page_demo\Controller;

class SimplePageDemoController {

  /**
   * Build page output.
   *
   * @return array
   */
  public function page() {
    return array(
      '#markup' => "This is a simple page",
    );
  }
}

The page title may also be dynamic, but the route requires a _title_callback key instead of _title. If our YAML example looks like this:

# simple_page_demo.routing.yml
simple_page_demo.page:
  path: '/simple-page-demo'
  defaults:
    _title_callback: '\Drupal\simple_page_demo\Controller\SimplePageDemoController::title'
    _controller: '\Drupal\simple_page_demo\Controller\SimplePageDemoController::page'
  requirements:
     _access: 'TRUE'

Then our callback in the SimplePageDemoController class may look like this:

// src\Drupal\simple_page_demo\Controller\SimplePageDemoController.php

  public function title() {
    return "Simple page title";
  }

To display a link in the main navigation block, we create a simple_page_demo.links.menu.yml configuration file:

# simple_page_demo.links.menu.yml
simple_page_demo.menu:
  title: 'Simple page demo'
  route_name: simple_page_demo.page

When the HTTP request for /simple-page-demo is received in the index.php file, the HttpKernel converts the request into a Symfony Request object and calls the handle() method on it. The controller is resolved, loaded, and called. What is returned is a Response object. Then, the Twig templating system kicks in, and the page is displayed.

Conclusion

This is a brief look at how to output a simple page in both Drupal 7 and 8. It is to illustrate what is required for transitioning to Drupal 8 development apart from understanding the Object-Oriented Programming principles on which it has been built. In addition, we have not looked at things like theming, templating or any of the more complex aspects of page rendering.

Code

Demo modules for this article are in the X-Team Drupal 7 Examples and Drupal 8 Examples Github repositories.