# The Drupal blocks system

## from drop.org to Drupal 8

by Frédéric G. Marand aka fgm / OSInet

## Once upon a time...

> “First we start making a slashdot.org alike site: a community weblog that offers all you need to know”

docs/mission.php, circa 2000-03

## Drupal – LoC vs time

Drupal – LoC vs time

## PHP code in Drupal

PHP code in Drupal

## CSS code in Drupal

CSS code in Drupal

## JavaScript code in Drupal

JavaScript code in Drupal

# drop.org

## drop.org – what's a block, really ?

> Every single “box” on the website can be themed: not only the colors but the entire look-and-feel and position.

docs/features.php, circa 2000-03

### drop.org – what's a block, really ?

> Boxes:

> Box for latest headlines.
> Box for older headlines.
> Box for related links.
> Box with login form.
> User-defined box.

docs/features.php, circa 2000-03

### drop.org – what's a block, really ?

> Theme support:

> Not only can you change the colors, you can also change the position of most boxes and such.

docs/features.php, circa 2000-03

## drop.org – page cycle

No front controller, controller = PHP file

- `index.php, faq.php, article.php, ...`
- uses GET query string, notably op for action variation
- `register_globals = on` required
- dedicated back-office : `admin.php`

### drop.org – page cycle

Controller

- loads shared functions, configuration
- `$theme->header()`
- if action variants exists, switch `($op)`
  - adds content base on `$op`,
  - e.g., `$theme->article()`, others build HTML inline
  - `article()`, `box()` , `comment()`, `abstract()`,
- `footer()`, `header()`, `preview()`
- `$theme->footer()`

## drop.org – block placement

- No block placement UI
- Theme methods decide which blocks they will display in their predefined `header` et `footer` elements.
- `themes/Dries/theme.class` `function footer() { /* ..snip.. */
  global $PHP_SELF;
  if (strstr($PHP_SELF, "index.php")) {
  global $user;
  displayAccount($this);`
- Predefined function invoke `$theme->box($subject, $content)`

### drop.org – block placement

- `functions.inc` `function displayAccount($theme) {
  global $user, $cookie;
  if ($user) {
  ### Display userblock if any:
  displayUserblock();
  }
  else {
  $content = "<CENTER><FORM METHOD=\"post\" ACTION=\"account.php\">\n <P>Username:<BR><INPUT TYPE=\"text\" NAME=\"uname\" MAXLENGTH=\"50\"SIZE=\"12\"></P>\n <P>Password:<BR> <INPUT TYPE=\"password\" NAME=\"pass\"MAXLENGTH=\"25\" SIZE=\"12\"></P>\n <INPUT TYPE=\"submit\" NAME=\"op\" VALUE=\"Login\">\n</FORM>\n<P><A HREF=\"account.php\"> Register</A> as new user.<BR><A HREF=\"account.php\">Forgot</A> your password?</P> </CENTER>";
  $theme->box("Login", $content);
  }
  }`

## drop.org : box theme

- `themes/Natrak/theme.class` Themes are PHP classes `######
  # Syntax.......: box($title, $body);
  # Description..: a function to draw a box/block.
  function box($subject, $content) {
  include "config.inc";
  print "<TABLE BORDER=\"0\" CELLPADDING=\"3\" CELLSPACING=\"3\" WIDTH=\"100%\">";
  print " <TR><TD ALIGN=\"center\" BGCOLOR=\"$this->bgcolor1\" WIDTH=\"100%\"><FONT COLOR=\"$this->fgcolor1\"><B>$subject</B></FONT></TD></TR>";
  print " <TR><TD BGCOLOR=\"$this->bgcolor2\">$content</TD></TR>";
  print "</TABLE><BR>";
  }`

### drop.org : box theme

- `themes/Natrak/theme.php` Just not only... `function themebox($subject, $content) {
  global $bgcolor1, $bgcolor2, $bgcolor3;
  include "config.inc";
  print "<TABLE BORDER=\"0\" CELLPADDING=\"3\" CELLSPACING=\"3\" WIDTH=\"100%\">";
  print " <TR><TD ALIGN=\"center\" BGCOLOR=\"$bgcolor1\"><FONT COLOR=\"$bgcolor2\"><B>$subject</B></FONT></TD></TR>";
  print " <TR><TD BGCOLOR=\"$bgcolor2\">$content</TD></TR>";
  print "</TABLE><BR>";
  }`
- `webboard.php` uses the function format, other controllers invoke the method format.

## drop.org – article page

## drop.org – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 9.821 seconds
Complete requests: | 10000
Total transferred: | 93350000 bytes
HTML transferred: | 90880000 bytes
Requests per second: | 1018.24 [#/sec] (mean)
Time per request: | 9.821 [ms] (mean)
Time per request: | 0.982 [ms] (mean, across all concurrent requests)
Transfer rate: | 9282.49 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 2
Processing: | 2 | 10 | 4.0 | 9 | 62
Waiting: | 1 | 9 | 3.8 | 9 | 61
Total: | 2 | 10 | 4.0 | 9 | 62

`

## drop.org – default theme

# Drupal 1

15/01/2001

## Drupal 1.0

- `module`, `hook` and `weight` are born
  - Explicit hook naming `$module = array("help" => "box_help",
    "block" => "box_block",
    "admin" => "box_admin");`
  - Hook registry, called `repository`
  - 7 hooks : `admin block cron export help page user`

### Drupal 1.0

- Birth of the `block` module
  - Separation : block vs custom block vs box
  - Birth of regions, `header` and `footer` remain separate.
  - User selection of optional blocks
- “Classical” Drupal file layout:
  - `includes/`, `modules/`, `scripts/`, `themes/`
  - New : `.htaccess` and `robots.txt`

## Drupal 1 – block_help()

> - Blocks are the boxes visible in the side bars on the left and the right-hand side of the website.
> - They are either exported by the engine or by any of the available modules. […]
> - The placement of blocks is delegated to the administrator but for most blocks, i.e. those called “custom blocks”, the sole force behind enabling and disabling them is the user itself.
> - An administrator can lay out and arrange the available blocks to fit in two regions: “left” and “right”. Regions simply contain blocks. In addition, an administrator can assign each block (within a region) a weight to sort them vertically. [...]
> - As mentioned above, blocks can be arranged to fit in two regions: left and right. For theme builders, each region is identified by a corresponding constant: “left” and “right”.

admin.php?mod=block&op=help

### Drupal 1 – block_help()

> - [...] Simply put, boxes are small bits of text, HTML or PHP code which will get plugged into the site just like any other block. Boxes are typically used to add custom blocks to the site.
> - Each box consists of a subject and an associated block of text, HTML or PHP code which can be as long as you want it to be and that will “render” the content of the box.

admin.php?mod=block&op=help

... but `Theme::box()` still exists !

## Drupal 1 – page cycle

- No front controller, but `module.php?mod=..`
- Controller
  - loads shared functions from `common.inc`
    - configuration: `getenv("HTTP_HOST") .".conf";`
    - theme: `$theme = load_theme();`
  - defines its action functions
  - if variants exist, `switch ($op)`
    - invoke action functions based on `$op`
  - action function
    - prepares its data
    - requests view build from the theme:
      - `$theme->header();`
      - multiple calls to `$theme->box()` on prepared data (account.php) – or less clean formatting... (search.php)
      - `$theme->footer();`

## Drupal 1 – block placement

- The block admin UI offers a choice of 3 layouts:
  - Left + Content
  - Left + Content + Right
  - Content + Right
- Administrator decides which custom blocks to display in the `left` et `right` regions
- `Theme::header()` and `Theme::footer()` decide separately which blocks they will display in `header` and `footer`, which are not regions
- Layouts are theme-independent
- The blocks table does not keep per-theme info

## Drupal 1 – block overview

## Drupal 1: block hook

- A single block hook: `hook_block()` returns a hash of:
  - Subject: displayed title
  - Content: raw content, HTML, or PHP for `eval()`
  - Info: administrative title
  - Link: URL, only displayed on the b.o.
- Used both for administration and display

### Drupal 1: block hook

- Example : box.module
  `function box_block() {
  $result = db_query("SELECT * FROM boxes");
  $i = 0;
  while ($block = db_fetch_object($result)) {
  $blocks[$i]["subject"] = check_output($block->subject);
  $blocks[$i]["content"] = ($block->type == 2)
  ? eval($block->content)
  : check_output($block->content, ($block->type == 1) ? 0 : 1);
  $blocks[$i]["info"] = check_output($block->info);
  $blocks[$i]["link"] = check_output($block->link);
  $i++;
  }
  return $blocks;
  }`

## Drupal 1: box theme

- `themes/marvin/marvin.theme`
  Themes are still PHP classes, but not located in <theme>.theme
  `function box($subject, $content) { ?>
  <TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0" BGCOLOR="<? echo $this->brcolor1; ?>" WIDTH="100%">
  <TR><TD><?
  print "<TABLE BORDER=\"0\" CELLPADDING=\"3\" CELLSPACING=\"1\" WIDTH=\"100%\">";
  print " <TR><TD ALIGN=\"center\" BGCOLOR=\"$this->bgcolor1\" NOWRAP><FONT COLOR=\"$this->fgcolor1\"><B>$subject</B></FONT></TD></TR>";
  print " <TR><TD BGCOLOR=\"$this->bgcolor2\">$content</TD></TR>";
  print "</TABLE>";
  ?>
  </TD></TR>
  </TABLE><BR><?
  }`
- No longer any non-class theme functions

## Drupal 1 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 6.388 seconds
Complete requests: | 10000
Total transferred: | 49950000 bytes
HTML transferred: | 45530000 bytes
Requests per second: | 1565.36 [#/sec] (mean)
Time per request: | 6.388 [ms] (mean)
Time per request: | 0.639 [ms] (mean, across all concurrent requests)
Transfer rate: | 7635.70 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 4
Processing: | 2 | 6 | 2.8 | 6 | 33
Waiting: | 2 | 6 | 2.8 | 6 | 33
Total: | 2 | 6 | 2.8 | 6 | 33

`

## Drupal 1 – UnConeD theme

UnConeD

# Drupal 2

15/03/2001 (2 months later)

## Drupal 2.0

- Filtering of box content climbs from the “model” to the controller
  - Filtering of block is still on the “model”
- Block `subject` is now translatable

### Drupal 2.0

- No significant changes on block/box
  - File Layout: birth of `misc/`
  - PHP “short tags” are dropped
  - First dash of blue: pre-Druplicon “drop” in UnConeD turns blue
  - `Theme::abstract()` → `Theme::story()`
  - `class Story extends Node {...}`

  Newborns | Locale
  First update script, only SQL
  CHANGELOG, CREDITS

  Changes | Comment, section, user ratings, permissions
  First optional module: submission

## Drupal 2 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 8.921 seconds
Complete requests: | 10000
Total transferred: | 63570000 bytes
HTML transferred: | 59190000 bytes
Requests per second: | 1120.93 [#/sec] (mean)
Time per request: | 8.921 [ms] (mean)
Time per request: | 0.892 [ms] (mean, across all concurrent requests)
Transfer rate: | 6958.77 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 4
Processing: | 2 | 9 | 3.5 | 8 | 32
Waiting: | 2 | 9 | 3.5 | 8 | 32
Total: | 2 | 9 | 3.5 | 8 | 32

`

## Drupal 2 – UnConeD theme

UnConeD

# Drupal 3

26/11/2001 (8 months later)

## Drupal 3.0.2

- Blocks created by modules...
  - can now be disabled, optional, or required
  - ...just like boxes
  - and appear in the regions preview
- No significant changes for block/box

  Newborns | Node, file, (page) cache, `form_*()`
  Front: fluid themes: Goofy, Jeroen, Yaroon, raw: Stone Age

  Changes | Hooks : `global $repository` → `function_exists()`
  Druplicon looking straight ahead replaces the drop
  Section → meta and introduces hierarchy
  Faq → book and introduces hierarchy
  <font> HTML element: 74 → 14

## Drupal 3 – blocks configuration

## Drupal 3.0.2 – box theme

- Default theme: `class Theme extends BaseTheme { /* ..snip.. */
  function box($subject, $content, $region = "main") { ?>
  <TABLE>
  <TR>
  <TD>
  <DIV ALIGN="center">
  <BIG><? echo $subject; ?></BIG>
  </DIV>
  <HR>
  </TD>
  </TR>
  <TR>
  <TD>
  <?php echo $content; ?>
  </TD>
  </TR>
  </TABLE>
  <BR><?php
  } // close box function
  }
  `

### Drupal 3.0.2 – box theme

- And the base theme class... `class BaseTheme {/
  function links(...) {...}
  function image($name) {}
  function comment_controls(...) {}
  }
  `

## Drupal 3 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 13.971 seconds
Complete requests: | 10000
Total transferred: | 47793825 bytes
HTML transferred: | 43413825 bytes
Requests per second: | 715.75 [#/sec] (mean)
Time per request: | 13.971 [ms] (mean)
Time per request: | 1.397 [ms] (mean, across all concurrent requests)
Transfer rate: | 3340.65 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 5
Processing: | 4 | 14 | 4.6 | 13 | 44
Waiting: | 4 | 14 | 4.6 | 13 | 44
Total: | 4 | 14 | 4.6 | 13 | 44

`

## Drupal 3 – new themes

Goofy

### Drupal 3 – new themes

Jeroen

### Drupal 3 – new themes

Stoneage

### Drupal 3 – new themes

Yaroon

# Drupal 4

08/12/2002 (12 months later)

## Drupal 4.0

- block/box admin merge, box module removed
- Placement defined by block, stores `region`/`weight`
  - Administrator-configured
  - Automatic fallback from nonexistent region (left/right) to the other one
  - Visibility defined by block, path column (regex), vs themes/controllers
- Content is defined by box, with a choice of ASCII / HTML / PHP format
- `BaseTheme::box()` provide a default box theme implementation

### Drupal 4.0

- And elsewhere...

  Newborns | Default theme == Basic
  Node form switched to two columns, FO / BO forms differ
  XML-RPC, Useful Inc. version

  Changes | Meta/section/category → taxonomy/vocabulary/term
  Input format per node, in the extra table for each content type
  Front page is customized with PHP `eval`
  More modules converted to generic module.php controller, but not all
  DB API based on PEAR:DB, supports PostgreSQL, SQL dump includes update
  Withdrawn themes: Dries, Jeroen; Stone Age → example

## Drupal 4.0 – blocks admin/placement

### Drupal 4.0 – node form FO Basic

### Drupal 4.0 – node form BO

## Drupal 4 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 32.101 seconds
Complete requests: | 10000
Total transferred: | 91560000 bytes
HTML transferred: | 87140000 bytes
Requests per second: | 311.52 [#/sec] (mean)
Time per request: | 32.101 [ms] (mean)
Time per request: | 3.210 [ms] (mean, across all concurrent requests)
Transfer rate: | 2785.42 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 3
Processing: | 9 | 32 | 11.6 | 31 | 225
Waiting: | 9 | 32 | 11.5 | 30 | 222
Total: | 9 | 32 | 11.6 | 31 | 225

`

## Drupal 4.1

25/06/2003 (6 months later) *

- Conditional blocks build: only if used
- Customizable blocks selection sorted by `module`/`delta`, not by name
- Block `name` (alias `$block->info`) withdrawn, meaning...
  - `blocks` table no longer has a PK, or even an index
- And elsewhere...

  Newborns | Profile

  Changes | Markup refactoring in themes with more reusable elements: `theme_mark`, `theme_head`, `theme_item_list`, `theme_error`...

* CHANGELOG claims 15/02/2003, but commits say 25/06/2003

## Drupal 4.1 – user block sorting

## Drupal 4.1 – conditional build

- Block building in `theme_blocks` only appears if that have to be displayed: required, default and anonymous user, or user-selected, but only without a path or if patch matches `functiontheme_blocks($region,&$theme){
  global $user, $PHP_SELF;
  $result = db_query("SELECT * FROM blocks WHERE (status = '1' OR custom = '1') ". ($region != "all" ? "AND region = '%s' " : "") ."ORDER BY weight, module", $region == "left" ? 0 : 1);
  while ($result && ($block = db_fetch_object($result))) {
  if (
  ( ($block->status && (!$user->uid || !$block->custom)) || ($block->custom && $user->block[$block->module][$block->delta])) && (!$block->path || preg_match("|$block->path|", $PHP_SELF))
  ){
  $block_data = module_invoke($block->module, "block", "view", $block->delta);
  if ($block_data["content"]) {
  $theme->box($block_data["subject"], $block_data["content"], $region);
  }
  }
  }
  }`

## Drupal 4.1 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 15.174 seconds
Complete requests: | 10000
Total transferred: | 19590000 bytes
HTML transferred: | 15170000 bytes
Requests per second: | 659.03 [#/sec] (mean)
Time per request: | 15.174 [ms] (mean)
Time per request: | 1.517 [ms] (mean, across all concurrent requests)
Transfer rate: | 1260.79 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 5
Processing: | 5 | 15 | 4.9 | 14 | 70
Waiting: | 5 | 15 | 4.9 | 14 | 70
Total: | 5 | 15 | 4.9 | 14 | 70

`

## Drupal 4.2

04/10/2003 (4.5 months later)

- hook_block($op) `$op = list|view`
- Block `delta`: int → varchar(32)
- Block preview : “Themes with (right|left)-sidebar only...”
- `t('messages from block.module code')`
- `BaseTheme::box()` receives filtered content, no longer filters it itself
- `BaseTheme::block($subject, $content, $region = 'main')`
  - Simple wrapper for `BaseTheme::box()`
  - Separate in Xtemplate, invoked by `theme_blocks()` instead of `theme('box')`

### Drupal 4.2

- And elsewhere...

  Newborns | Support for Microsoft SQL Server
  `index.php` front controller for “normal” paths
  Xtemplate, engine and base theme
  `theme()` replaces `theme_invoke()`
  Only `system.module` uses `$theme->foo()`, others use `theme('foo')`

  Changes | Clean URLs
  WYSIWYG support (!)
  BO now integrated in the overall UI
  Themes: Goofy withdrawn, blue becomes more prevalent, first CSS fields
  `register_globals = On` no longer needed

## Drupal 4.2 – block admin

## Drupal 4.2 – BaseTheme::box()

- Drupal 4.1 `function box($subject, $content, $region = "main") {
  $output = "<b>"
  . check_output($subject)
  ."</b><br />"
  check_output($content)
  ."<p />";
  print $output;
  }`
- Drupal 4.2 `function box($subject, $content, $region = "main") {
  $output = "

  $subject
  $content

  ";
  print $output;
  }
  function block($subject, $content, $region = "main") {
  global $theme;
  $theme->box($subject, $content, $region);
  }`

## Drupal 4.2 – XTemplate

## Drupal 4.2 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 22.128 seconds
Complete requests: | 10000
Total transferred: | 41080000 bytes
HTML transferred: | 36510000 bytes
Requests per second: | 451.92 [#/sec] (mean)
Time per request: | 22.128 [ms] (mean)
Time per request: | 2.213 [ms] (mean, across all concurrent requests)
Transfer rate: | 1812.97 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 6
Processing: | 6 | 2 | 7.6 | 21 | 154
Waiting: | 6 | 2 | 7.6 | 21 | 154
Total: | 6 | 2 | 7.6 | 21 | 154

`

## Drupal 4.3.x

01/11/2003 (4 weeks later)

- UX: `admin/block` → `admin/system/block`
- And elsewhere...

  | Minor releases 4.3.0, 4.3.1, 4.3.2, 4.3.x unreleased HEAD until 02/2004

  Newborns | Alias
  DB prefixing using {}

  Changes | User object simplified
  Some controller actions start using `return` instead of `print`

### Drupal 4.3.x – Nothing

That's all for 4.3!

## Drupal 4.3 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 15.036 seconds
Complete requests: | 10000
Total transferred: | 39170000 bytes
HTML transferred: | 34600000 bytes
Requests per second: | 6665.0659 [#/sec] (mean)
Time per request: | 15.036 [ms] (mean)
Time per request: | 1.504 [ms] (mean, across all concurrent requests)
Transfer rate: | 2543.97 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 3
Processing: | 5 | 15 | 4.5 | 14 | 44
Waiting: | 5 | 15 | 4.5 | 14 | 44
Total: | 5 | 15 | 4.5 | 14 | 44

`

## Drupal 4.4.x

01/04/2004 (2 months later)

- `theme_blocks()` delegates...
  - listing blocks to `block_list()` instead of performing the request itself
  - rendering to `theme_block('view')` instead of invoking `hook_block('view')`
- `theme('box')` no longer implemented except in `theme.inc` and Xtemplate
- The lack of a PK on blocks is documented as “(bad)!”

### Drupal 4.4.x

- And elsewhere...

  Performance | Generic throttling with `throttle.module`: deactivation of modules or invididual functions
  Cached pages load less modules
  Locale only caches data below a size limit

  Newborns | Pushbutton

  Changes | Uniform use of `theme()`, theme_* functions and use of `return`
  Marvin → Chameleon + subtheme

  Removals | Theme classes
  Microsoft SQL Server support
  Goofy

## Drupal 4.4 – theme_blocks()

- Drupal 4.3
  - cf Drupal 4.1: `db_query()` + complex test
- Drupal 4.4
  - delegates to `block_list()`, `theme_block()`
  `function theme_blocks($region) {
  $output = '';
  if ($list = module_invoke('block', 'list', $region)){
  foreach ($list as $key => $block) {
  // $key == <i>module</i>_<i>delta</i>
  $output .= theme('block', $block);
  }
  }
  return $output;
  }`

### Drupal 4.4 – theme_block()

`function theme_block($block) {
$output = "<div class=\"block block-$block->module\" id=\"block-$block->module-$block->delta\">\n";
$output .= " <h2 class=\"title\">$block->subject</h2>\n";
$output .= " <div class=\"content\">$block- >content</div>\n";
$output .= "</div>\n";
return $output;
}`

## Drupal 4.4 – Chameleon theme

Chameleon

## Drupal 4.4 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 20.207 seconds
Complete requests: | 10000
Total transferred: | 34080000 bytes
HTML transferred: | 29510000 bytes
Requests per second: | 494.87 [#/sec] (mean)
Time per request: | 20.207 [ms] (mean)
Time per request: | 2.021 [ms] (mean, across all concurrent requests)
Transfer rate: | 1646.99 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 13
Processing: | 7 | 20 | 11.5 | 19 | 445
Waiting: | 7 | 20 | 11.4 | 19 | 434
Total: | 7 | 20 | 11.5 | 19 | 446

`

## Drupal 4.5

18/10/2004 (6 months later)

- New blocks provided by comment, taxonomy
- `theme_(block|blocks|box)`: no changes
- And elsewhere...

  | New menu system with caching, `hook_menu($may_cache)`
  | Multiple roles per user

  Newborns | Bluemarine

  Changes | SQL: multiple connections, PostgreSQL no longer PEAR:DB
  Themes: refactoring engines, templates and styles, settings and screenshots; XTemplate and Pure themes withdrawn; XTemplate engine remains
  i18n: 100 % UI

## Drupal 4.5 – Marvin

Marvin

## Drupal 4.5 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 28.487 seconds
Complete requests: | 10000
Total transferred: | 60070000 bytes
HTML transferred: | 54620000 bytes
Requests per second: | 351.03 [#/sec] (mean)
Time per request: | 28.487 [ms] (mean)
Time per request: | 2.849 [ms] (mean, across all concurrent requests)
Transfer rate: | 2059.24 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 3
Processing: | 8 | 28 | 7.5 | 27 | 195
Waiting: | 8 | 28 | 7.5 | 27 | 195
Total: | 8 | 28 | 7.5 | 28 | 195

`

## Drupal 4.6

15/04/2005 (6 months later)

- New block settings:
  - `visibility`
    - Previously: everywhere or if matches regex
    - 4.6: on.. or except_on.. (but no PHP yet)
    - Paths criteria decode aliases before being applied
  - `types`
    - On all node types or only on these...
- New `hook_block() $op` values: `configure`, `save`
- `theme_(block|blocks|box)` unchanged

### Drupal 4.6

- And elsewhere...

  Supports PHP 5, UTF-8 everywhere
  Home: 92ms without the page cache, 19ms with it...

  Newborns | Contact, ImageAPI

  Changes | First UX refactorings

  Removals | Admin controller `admin.php`
  PEAR:DB abstraction>

## Drupal 4.6 – Block configuration

## Drupal 4.6 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 18.530 seconds
Complete requests: | 10000
Total transferred: | 920361989 bytes
HTML transferred: | 916920711 bytes
Requests per second: | 539.67 [#/sec] (mean)
Time per request: | 18.530 [ms] (mean)
Time per request: | 1.853 [ms] (mean, across all concurrent requests)
Transfer rate: | 48505.27 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 11
Processing: | 5 | 18 | 8.3 | 17 | 199
Waiting: | 4 | 13 | 6.4 | 12 | 160
Total: | 5 | 18 | 8.3 | 17 | 199

`

## Drupal 4.7

01/05/2006 (11 months later)

- Placement: blocks are now configured as placed in theme custom regions
- In core, regions are named, not numbered
- PHP visibility for blocks in addition to paths/types
- Input formats on box
- `theme_(block|blocks|box)` unchanged

### Drupal 4.7

- And elsewhere...

  Newborns | PHPTemplate, replacing Xtemplate
  Maintenance mode
  JS: AJAX, collapse effect, textarea resize...
  Form API: arrays of arrays of arrays...

  Changes | “Loose caching” for pages, variables cache, data caching in modules (archive)
  Taxonomy: free tagging, global contact form
  Bluemarine and Pushbutton converted to PHPTemplate
  Content moderation withdrawn
  Upgrade path can be extended by modules
  XML-RPC new version
  SQL: PostgreSQL stored procedures removed
  Queue module withdrawn (moderation, not queueing)

## Drupal 4.7 – block templates

- Bluemarine 4.6 XTemplate `<!-- BEGIN: block -->
  <div class="block block-{module}" id="block-{module}-{delta}">
  <h2 class="title">{title}</h2>
  <div class="content">{content}</div>
  </div>
  <!-- END: block -->`
- Bluemarine 4.7 PHPTemplate `<div class="<?php print "block block-$block->module" ?>" id="<?php
  print "block-$block->module-$block->delta"; ?>">
  <h2><?php print $block->subject ?></h2>
  <div class="content"><?php print $block->content ?></div>
  </div>`

## Drupal 4.7 – custom blocks

## Drupal 4.7 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 10.249 seconds
Complete requests: | 10000
Total transferred: | 53820228 bytes
HTML transferred: | 48630000 bytes
Requests per second: | 975.74 [#/sec] (mean)
Time per request: | 10.249 [ms] (mean)
Time per request: | 1.025 [ms] (mean, across all concurrent requests)
Transfer rate: | 5128.37 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 3
Processing: | 3 | 10 | 3.6 | 10 | 82
Waiting: | 3 | 10 | 3.6 | 10 | 82
Total: | 3 | 10 | 3.6 | 10 | 84

`

# Drupal 5

15/01/2007 (8 months later)

## Drupal 5.0

- Block visibility by role
- `hook_block('view')` may not return a title, it uses the one `from block_list()`, passing it to `check_plain()`
- `theme_(block|blocks|box)` unchanged
- Blocks preview merged into the blocks admin form

### Drupal 5.0

- And elsewhere...

  Newborns | Localized installer replaces dump loading
  Install profiles
  `.info` files for modules, dependencies
  jQuery 1.0.4
  Themes: Garland, Minelli, color.module, admin theme concept, module CSS

  Changes | Performance: sessions (in Memcached), node_access, fast404, aggressive page cache
  52 ms no cache, 7 ms normal cache, 5 ms aggressive cache
  File layout: 1 directory per core module, `sites/all` introduced
  Cache plugins, multiple cache bins
  Status page, mail altering, uninstallable modules, bulk operations on users/nodes
  PHPTemplate override

## Drupal 5 – block admin + preview

## Drupal 5 – per-role visibility

## Drupal 5.0 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 5.523 seconds
Complete requests: | 10000
Total transferred: | 47180102 bytes
HTML transferred: | 41720000 bytes
Requests per second: | 1810.69 [#/sec] (mean)
Time per request: | 5.523 [ms] (mean)
Time per request: | 0.552 [ms] (mean, across all concurrent requests)
Transfer rate: | 8342.63 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.0 | 0 | 1
Processing: | 2 | 5 | 3.6 | 5 | 110
Waiting: | 2 | 5 | 3.6 | 5 | 110
Total: | 2 | 5 | 3.6 | 5 | 111

`

# Drupal 6

13/02/2008 (13 months later)

## Drupal 6.0

- Block cache
- `theme_block()` → `modules/system/block.tpl.php`
- `theme_(box|blocks)` unchanged, `box.tpl.php` still exists
- Per-page block visibility refactored to `drupal_match_path()`
- {blocks} surrogate PK on `bid`, unique index on `module/delta/theme`

### Drupal 6.0

- And elsewhere...

  Newborns | `hook_watchdog`, syslog

  Changes | 47 ms no cache, 46 ms block cache, 6.5 ms normal cache, 5.9 ms aggressive page cache
  Performance: single pass `hook_menu()` (without `$may_cache`); SQL: schema API, needs less privileges on DB (temporary, lock tables); files split, .admin.inc, JS aggregator, reverse proxy support
  Quality: E_ALL, UX, core = 6.x, batch API, HTML Corrector
  Deployment: scripts/drupal.sh (think Drush)
  Security: php.module, weak password check, OpenID, update
  Themes: .info, templates no longer need a matching function, preprocess hooks, jQuery 1.2.3
  i18n: RTL, language detection, content translation, alias, JS UI

## Drupal 6.0 – block cache

## Drupal 6 – cache + block throttling

`if ($block->enabled && $block->page_match) {
// Check the current throttle status and see if block should be displayed
// based on server load.
if (!($block->throttle && (module_invoke('throttle', 'status') > 0))) {
// Try fetching the block from cache. Block caching is not compatible with
// node_access modules. We also preserve the submission of forms in blocks,
// by fetching from cache only if the request method is 'GET'.
if (!count(module_implements('node_grants'))
&& $_SERVER['REQUEST_METHOD'] == 'GET'
&& ($cid = _block_get_cache_id($block))
&& ($cache = cache_get($cid, 'cache_block'))) {
$array = $cache->data;
}
else {
$array = module_invoke($block->module, 'block', 'view', $block->delta);
if (isset($cid)) {
cache_set($cid, $array, 'cache_block', CACHE_TEMPORARY);
}
}
} /* ... snip ... */
}`

Q1: Why?Q2: What's in a cid?

## Drupal 6.0 – cache rules

`* The block cache is cleared in
* cache_clear_all(), and uses the same
* clearing policy than page cache
* (node, comment, user, taxonomy added
* or updated...). Blocks requiring more
* fine-grained clearing might consider
* disabling the built-in block cache
* (BLOCK_NO_CACHE) and roll their own.
*
* user 1 is excluded from block
* caching. */
/* This setting should be used:
* - for simple blocks (notably those
* that do not perform any db query),
* where querying the db cache would be
* more expensive than directly
* generating the content.
* - for blocks that change too
* frequently.
*/
define('BLOCK_NO_CACHE', -1);
` `/* This is the default setting, used when the block does not specify anything. */
define('BLOCK_CACHE_PER_ROLE', 0x0001);
/* The block can change depending on the user viewing the page. Can be resource-consuming for sites with many users */
define('BLOCK_CACHE_PER_USER', 0x0002);
/* The block can change depending on the page being viewed. */
define('BLOCK_CACHE_PER_PAGE', 0x0004);
/* The block is the same for every user on every page where it is visible. */
define('BLOCK_CACHE_GLOBAL', 0x0008);
`

Q: Which advice is lacking?

## Drupal 6.0 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 4.690 seconds
Complete requests: | 10000
Total transferred: | 3230000 bytes
HTML transferred: | 0 bytes
Requests per second: | 2132.20 [#/sec] (mean)
Time per request: | 4.690 [ms] (mean)
Time per request: | 0.469 [ms] (mean, across all concurrent requests)
Transfer rate: | 672.56 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.0 | 0 | 1
Processing: | 1 | 5 | 2.3 | 4 | 50
Waiting: | 1 | 5 | 2.3 | 4 | 50
Total: | 1 | 5 | 2.3 | 4 | 51

`

# Drupal 7

05/01/2011 (35 months later)

## Drupal 7.0

- `hook_block($op) → hook_block_(info|configure|save|view)`
- `hook_block_(info|list|view[_MODULE_DELTA])_alter`
- `block['content']` render array => cache cid without theme/language
- `block_custom.body` translatable
- `block_custom.format` int → varchar(255)
- `block.api.php`, `block.test`, `block.js` (vertical tabs, d'n'd)
- `block_theme()` declares `block`, not just `block_admin_display_form`
- `BLOCK_CACHE_*` → `DRUPAL_CACHE_*`
- `{blocks}` → `{block}`, `{boxes}` → {block_custom}

### Drupal 7.0

- Permissions:
  - `- use PHP for block visibility`
  - `+ administer blocks`
- UI:
  - `block` integration with `contextual`, vertical tabs, local actions
  - Generic regions: block placement in `content`, `help`...
  - Main content → block dans `content`, site mission → block dans `highlighted`, `footer` → `block` dans `footer`
  - Regions preview again separated from block admin, without the blocks themselves

### Drupal 7.0

- Theme:
  - `theme_box()` RIP – box concept removed
  - Block template suggestions
  - Process
- `(block).throttle` RIP
- `block_list()` replaces join + `db_rewrite_sql()` by `_block_load_blocks()` + `block_block_list_alter()`
- Visibility:
  - Invalidates inconsistent visibility, like no page allows (positif/negative)
  - PHP: `module_exists('php')`

### Drupal 7.0

- `hook_modules_uninstalled()` enables block to clean blocks created by other modules
- Alternative: `drupal_set_content` → `drupal_add_region_content`
- And elsewhere...

  | Entity API, Field API, EFQ, DBTNG, render arrays
  | More caches and one-off plugins
  | i18n: field translation
  | Theme: Bartik, Corolla, Seven; return of the raw theme: Stark

## Drupal 7 – block preview

## Drupal 7.0 – ab -n10000 -c10 (home)

`

Concurrency Level: | 10
Time taken for tests: | 6.223 seconds
Complete requests: | 10000
Total transferred: | 63250000 bytes
HTML transferred: | 58260000 bytes
Requests per second: | 1606.99 [#/sec] (mean)
Time per request: | 6.223 [ms] (mean)
Time per request: | 0.622 [ms] (mean, across all concurrent requests)
Transfer rate: | 9926.00 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.1 | 0 | 4
Processing: | 2 | 6 | 2.3 | 6 | 29
Waiting: | 2 | 6 | 2.3 | 6 | 29
Total: | 2 | 6 | 2.3 | 6 | 29

`

# Drupal 8

02/2014? (37 months later)

## Drupal 8.0

- Assetic
- Composer
- Doctrine: annotations
- EasyRDF
- Guzzle
- PHPUnit
- PSR/3

- Symfony 2
- Symfony CMF: routing (chain router)
- Twig
- Zend Framework 2: escaper, feed, stdlib
- Plus some Drupal to wrap it up... ;-)

## Drupal 8 – just the basics...

- `hook_block_info()` → Doctrine annotation on the block class
- `hook_block_view()` → `BlockPluginInterface::build()`
- `hook_block_configure()` → `BlockPluginInterface::blockForm()`
- `hook_block_save()` → `BlockPluginInterface::blockSubmit()`
- New methods:
  - `BlockPluginInterface::access()`: block visibility
  - `BlockPluginInterface::settings()`: default configuration
    (à la Views `option_definition`)
  - `BlockPluginInterface::validate()`: validation before submit
- Derivative blocks

## Drupal 8 – block "online users"

`<?php
/**
* @file
* Contains \Drupal\user\Plugin\Block
* \UserOnlineBlock.
*/
namespace Drupal\user\Plugin\Block;
use Drupal\block\BlockBase;
use Drupal\Component\Annotation\Plugin;
use Drupal\Core\Annotation\Translation;
/**
* Provides a "Who's online" block.
*
* @Plugin(
* id = "user_online_block",
* admin_label = @Translation("Who's
* online"),
* module = "user"
* )
*/
` `class UserOnlineBlock extends BlockBase {
//** Overrides \Drupal\block\BlockBase::settings().
public function settings() { /* ... */ }
//** Overrides \Drupal\block\BlockBase::access().
public function access() { /* ... */ }
//** Overrides \Drupal\block\BlockBase::blockForm().
public function blockForm($form, &$form_state)
{ /* ... */ }
//** Overrides \Drupal\block\BlockBase::blockSubmit().
public function blockSubmit($form, &$form_state)
{ /* ... */ }
/**
* {@inheritdoc}
*/
public function build() { }
}`

## Drupal 8.0 – Block statistics

Drupal 6 | PHP | JavaScript |
| 3 files | 1 file |
| 329 comment lines | 20 comment lines |
| 651 code lines | 65 code lines |

Drupal 7 | PHP | JavaScript |
| 8 files | 1 file |
| 1148 comment lines | 31 comment lines |
| 1746 code lines | 105 code lines |

Drupal 8 | PHP | JavaScript | YAML
| 85 files | 3 files | 17 files
| 3443 comment lines | 66 comment lines | 2 comment lines
| 5089 code lines | 170 code lines | 428 data lines

Drupal 3 = 5622 PHP LoC

## Drupal 8 – UI

- New administration UI:
  - No kilometer-high list of unused blocks
  - Add block page for new blocks
    - Optionally filtered by module
    - With search form
- Simplified configuration form

## Drupal 8 – block admin

## Drupal 8 – block add

### Drupal 8.0 – ab -n10000 -c10 (home)

Default classloader

`

Concurrency Level: | 10
Time taken for tests: | 46.494 seconds
Complete requests: | 10000
Total transferred: | 85030000 bytes
HTML transferred: | 80260000 bytes
Requests per second: | 215.08 [#/sec] (mean)
Time per request: | 46.494 [ms] (mean)
Time per request: | 4.649 [ms] (mean, across all concurrent requests)
Transfer rate: | 1785.98 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.0 | 0 | 3
Processing: | 14 | 46 | 14.9 | 45 | 276
Waiting: | 14 | 46 | 14.9 | 45 | 276
Total: | 14 | 46 | 14.9 | 45 | 276

`

## Drupal 8.0 – ab -n10000 -c10 (home)

APC classloader

`

Concurrency Level: | 10
Time taken for tests: | 40.986 seconds
Complete requests: | 10000
Total transferred: | 85030000 bytes
HTML transferred: | 80260000 bytes
Requests per second: | 273.78 [#/sec] (mean)
Time per request: | 36.526 [ms] (mean)
Time per request: | 3.653 [ms] (mean, across all concurrent requests)
Transfer rate: | 2273.40 [Kbytes/sec] received

Connection Times (ms)
| min | mean | [+/-sd] | median | max
Connect: | 0 | 0 | 0.0 | 0 | 1
Processing: | 11 | 36 | 10.6 | 35 | 99
Waiting: | 10 | 36 | 10.6 | 35 | 99
Total: | 10 | 36 | 10.6 | 35 | 99

`

## ms/<anonymous home>

PHP 5.3.10, MySQL 5.5, APC 3.1.9, aggressive Cache, D8 APC Classloader

PHP 5.3.10, MySQL 5.5, APC 3.1.9, aggressive Cache, D8 APC Classloader

### Thank you!

Any questions?

### Come and say hello!

Frédéric G. Marand (fgm) | Outi Munter (Outi) | Brigitte Taïeb (brigtai)
Let's take a look at your code! | I design and theme for you. | I can talk business.

www.osinet.fr

WHAT DID YOU THINK?

Locate this session at the DrupalCon Prague website:

http://prague2013.drupal.org/session/blocks-drop.org-drupal-8-and-beyond

Click the "Take the survey" link

THANK YOU!