The DX Files: Abandon Anonymous Arrays of Attributes

This is part three of my series, The DX Files: Improving Drupal Developer Experience. This time, I’m suggesting changing some of Drupal’s most basic data structures and APIs by replacing anonymous arrays with well-defined data structures. I fully expect lots of disagreement.

Many of Drupal’s APIs (Form API, Schema API, etc.) use PHP arrays to represent complex structured data. For example, here is a Form API data structure:

<?php
$form
['author'] = array(
 
'#type' => 'fieldset',
 
'#access' => user_access('administer nodes'),
 
'#title' => t('Authoring information'),
 
'#collapsible' => TRUE,
 
'#collapsed' => TRUE,
 
'#weight' => 20,
);
$form['author']['name'] = array(
 
'#type' => 'textfield',
 
'#title' => t('Authored by'),
 
'#maxlength' => 60,
 
'#autocomplete_path' => 'user/autocomplete',
 
'#default_value' => $node->name ? $node->name : '',
 
'#weight' => -1,
 
'#description' => t('Leave blank for anonymous.');
);
?>

Some of the downsides to this representation include:

  • Developer IDEs cannot provide auto-completion or a similar form of assistance while the code is being written.
  • Invalid form properties (those that begin with “#”) cannot be identified at compile- or run-time.
  • It is awkward to associate default values or other automatic behaviors with array structures.
  • Functions that operate on specific kinds of form elements, such as textfield_validate(), are not assured they are being passed an “array of the right type.”

An alternative representation uses typed data structures, specifically a PHP class but without any methods (basically, what C calls a struct). For example:

<?php
$form
= new Form();

$form->elements['author'] = $author = new FieldsetElement();
$author->access = user_access('administer nodes');
$author->title = t('Authoring information'),
$author->collapsible = TRUE;
$author->collapsed = TRUE;
$author->weight = 20;

$author->elements['name'] = $name = new TextfieldElement();
$name->title = t('Authored by');
$name->maxlength = 60;
$name->autocomplete_path = 'user/autocomplete';
$name->default_value = $node->name ? $node->name : '';
$name->weight = -1;
$name->description = t('Leave blank for anonymous.');
?>

The second version of the code is almost identical to the first except for the change in representation; it is no harder to write and the conversion can even mostly be handled by search-and-replace. However, the object representation addresses (or can address) all the problems with the array representation and provide a variety of other benefits.

As an example, class representation solves the problem of functions not being sure about the kind of data they are passed. In the file that defines text field elements, we might have code like:

<?php
class TextfieldElement extends FormElement {
 
// Maximum length; NULL means no limit;
 
public $maxlength = NULL;

 
// AJAX path for auto-completition; NULL means no auto-complete.
 
public $autocomplete_path = NULL;
}

function
textfield_validate(TextfieldElement $element) {
 
// $element is guaranteed to be a TextfieldElement
}
?>

Now, textfield_validate() is guaranteed to be passed a TextfieldElement.

Note: Yes, we could also change functions like textfield_validate() into methods of class TextfieldElement. Gotta start somewhere, though. Baby steps!