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

code-1998-2014 code-1998-2014
code-1998-2014 code-1998-2014

Drupal – LoC vs time

PHP code in Drupal

php-1998-2014
php-1998-2014

PHP code in Drupal

CSS code in Drupal

css-1998-2014
css-1998-2014

CSS code in Drupal

JavaScript code in Drupal

css-1998-2014
css-1998-2014

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

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

Theme example

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 override

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 1 Block override

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 1 Block override

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 $repositoryfunction_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 Block configuration 1

Drupal 3 – blocks configuration

Drupal 3 Block configuration 2

Drupal 3 – blocks configuration

Drupal 3 Block configuration 3

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 Goofy theme

Drupal 3 – new themes

Jeroen

Drupal 3 Jeroen theme

Drupal 3 – new themes

Stoneage

Drupal 3 Stoneage theme

Drupal 3 – new themes

Yaroon

Drupal 3 Yaroon theme

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 Block administration

Drupal 4.0 – blocks admin/placement

Drupal 4 Block preview

Drupal 4.0 – node form FO Basic

Drupal 4 node form FO

Drupal 4.0 – node form BO

Drupal 4 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 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 Block admin page

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 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/blockadmin/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 Chameleon theme

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 Marvin theme

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 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 Custom block page

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 Block preview

Drupal 5 – per-role visibility

Drupal 5 Block 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 Block cache

Drupal 6.0 – block cache

Drupal 6 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, footerblock 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_contentdrupal_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 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 administration page

Drupal 8 – block add

Drupal 8 Block administration page

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>

Drupal

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

Drupal

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!