You are here

schema.module in Schema 8

Same filename and directory in other branches
  1. 5 schema.module
  2. 6 schema.module
  3. 7 schema.module

The Schema module provides functionality built on the Schema API.

File

schema.module
View source
<?php

/**
 * @file
 * The Schema module provides functionality built on the Schema API.
 */
use Drupal\Core\Database\Database;
use Drupal\schema\Comparison\SchemaComparator;
use Drupal\schema\Comparison\SchemaComparisonInfoBuilder;
use Drupal\schema\SchemaManager;
use Drupal\schema\SchemaProviderInterface;

/**
 * Implements hooK_help().
 */
function schema_help($path, $arg) {
  switch ($path) {
    case 'admin/structure/schema':
      return '<p>' . t('This page compares the live database as it currently exists against the combination of all schema information provided by all enabled modules.') . '<p>';
    case 'admin/structure/schema/describe':
      return '<p>' . t("This page describes the Drupal database schema. Click on a table name to see that table's description and fields. Table names within a table or field description are hyperlinks to that table's description.") . '</p>';
    case 'admin/structure/schema/inspect':
      $output = '<p>' . t("This page shows the live database schema as it currently exists on this system. Known tables are grouped by the module that defines them; unknown tables are all grouped together.") . '</p>';
      $output .= '<p>' . t("To implement hook_schema() for a module that has existing tables, copy the schema structure for those tables directly into the module's hook_schema() and return \$schema.") . '</p>';
      return $output;
    case 'admin/structure/schema/sql':
      return '<p>' . t('This page shows the CREATE TABLE statements that the Schema API generates for the selected database engine for each table defined by a module. It is for debugging purposes.') . '</p>';
    case 'admin/structure/schema/show':
      return '<p>' . t('This page displays the Drupal database schema data structure. It is for debugging purposes.') . '</p>';
  }
}

/**
 * Fetch the schema engine class name for a given database connection.
 *
 * @param string $connection
 *   A database connection key, defaults to 'default'.
 *
 * @return string
 *   The schema engine class name if available, otherwise FALSE.
 */
function schema_get_connection_engine_class($connection = 'default') {
  if ($info = Database::getConnectionInfo($connection)) {
    $driver = $info['default']['driver'];
    $class_name = 'Drupal\\schema\\SchemaDatabaseSchema_' . $driver;
    if (class_exists($class_name)) {
      return $class_name;
    }
  }
  return FALSE;
}

/**
 * Fetch a schema engine class instance for a given database connection.
 *
 * @param string $connection
 *   A database connection key, defaults to the schema_database_connection
 *   variable, which itself defaults to 'default'.
 *
 * @return Drupal\schema\DatabaseSchemaInspectionInterface
 *   A schema engine class set to the given connection.
 */
function schema_dbobject($connection = NULL) {
  if (!isset($connection)) {
    $connection = \Drupal::config('schema.settings')
      ->get('schema_database_connection');
  }
  if ($class = schema_get_connection_engine_class($connection)) {
    return new $class(Database::getConnection('default', $connection));
  }
}

/**
 * Get an array of connection options that are supported by schema inspection.
 */
function schema_get_connection_options() {
  $options =& drupal_static(__FUNCTION__);
  if (!isset($options)) {
    foreach ($GLOBALS['databases'] as $connection => $targets) {

      // Only support connections that can be inspected by schema module.
      if (!schema_get_connection_engine_class($connection)) {
        continue;
      }
      $options[$connection] = $connection;
    }
  }
  return $options;
}

//////////////////////////////////////////////////////////////////////

// Schema print functions

//////////////////////////////////////////////////////////////////////

/**
 * Builds a pretty ASCII-formatted version of a $schema array.
 *
 * This is nothing more than a specialized variation of var_dump and
 * similar functions and is used only as a convenience to generate the
 * PHP for existing database tables (to bootstrap support for modules
 * that previously used CREATE TABLE explicitly) and for debugging.
 */
function schema_phpprint($schema) {
  $out = '';
  foreach ($schema as $name => $table) {
    $out .= schema_phpprint_table($name, $table);
  }
  return $out;
}
function schema_phpprint_table($name, $table) {
  $cols = array();
  if (isset($table['fields'])) {
    foreach ($table['fields'] as $colname => $col) {
      $cols[] = "'{$colname}' => " . schema_phpprint_column($col, TRUE);
    }
  }
  $unique = $index = array();
  if (isset($table['unique keys'])) {
    foreach ($table['unique keys'] as $keyname => $key) {
      $unique[] = "'{$keyname}' => " . schema_phpprint_key($key);
    }
  }
  if (isset($table['indexes'])) {
    foreach ($table['indexes'] as $keyname => $key) {
      $index[] = "'{$keyname}' => " . schema_phpprint_key($key);
    }
  }
  if ($table['description']) {
    $description = $table['description'];
  }
  else {
    $description = t('TODO: please describe this table!');
  }
  $out = '';
  $out .= "\$schema['" . $name . "'] = array(\n";
  $out .= "  'description' => '{$description}',\n";
  $out .= "  'fields' => array(\n    ";
  $out .= implode(",\n    ", $cols);
  $out .= ",\n  ),\n";
  if (isset($table['primary key'])) {
    $out .= "  'primary key' => array('" . implode("', '", $table['primary key']) . "'),\n";
  }
  if (count($unique) > 0) {
    $out .= "  'unique keys' => array(\n    ";
    $out .= implode(",\n    ", $unique);
    $out .= "\n  ),\n";
  }
  if (count($index) > 0) {
    $out .= "  'indexes' => array(\n    ";
    $out .= implode(",\n    ", $index);
    $out .= ",\n  ),\n";
  }
  $out .= ");\n";
  return $out;
}
function schema_phpprint_column($col, $multiline = FALSE) {
  $attrs = array();
  $description = isset($col['description']) ? $col['description'] : '';
  unset($col['description']);
  $attrs[] = "'description' => '{$description}'";
  if ($col['type'] == 'varchar' || $col['size'] == 'normal') {
    unset($col['size']);
  }
  foreach (array(
    'type',
    'unsigned',
    'size',
    'length',
    'not null',
    'default',
  ) as $attr) {
    if (isset($col[$attr])) {
      if (is_string($col[$attr])) {
        $attrs[] = "'{$attr}' => '{$col[$attr]}'";
      }
      elseif (is_bool($col[$attr])) {
        $attrs[] = "'{$attr}' => " . ($col[$attr] ? 'TRUE' : 'FALSE');
      }
      else {
        $attrs[] = "'{$attr}' => {$col[$attr]}";
      }
      unset($col[$attr]);
    }
  }
  foreach (array_keys($col) as $attr) {
    if (is_string($col[$attr])) {
      $attrs[] = "'{$attr}' => '{$col[$attr]}'";
    }
    else {
      $attrs[] = "'{$attr}' => {$col[$attr]}";
    }
  }
  if ($multiline) {
    return "array(\n      " . implode(",\n      ", $attrs) . ",\n    )";
  }
  return "array(" . implode(', ', $attrs) . ")";
}
function schema_phpprint_key($keys) {
  $ret = array();
  foreach ($keys as $key) {
    if (is_array($key)) {
      $ret[] = "array('{$key[0]}', {$key[1]})";
    }
    else {
      $ret[] = "'{$key}'";
    }
  }
  return "array(" . implode(", ", $ret) . ")";
}

//////////////////////////////////////////////////////////////////////

// Schema comparison functions

//////////////////////////////////////////////////////////////////////

/**
 * Unprefix a table name.
 *
 * This is pretty much the converse of DatabaseConnection::prefixTables().
 *
 * @param string $name
 *   The prefixed table name.
 * @param DatabaseConnection $connection
 *   An optional database connection object.
 *
 * @return string
 *   The unprefixed table name.
 */
function schema_unprefix_table($name, $connection = NULL) {
  $prefixes =& drupal_static(__FUNCTION__, array());
  if (!isset($connection)) {
    $connection = Database::getConnection();
  }
  $key = $connection
    ->getKey();
  if (!isset($prefixes[$key])) {
    $prefixes[$key] = array();
    $info = $connection
      ->getConnectionOptions();
    if (isset($info['prefix'])) {
      if (is_array($info['prefix'])) {
        $info['prefix'] = $info['prefix'] + array(
          'default' => '',
        );
      }
      else {
        $info['prefix'] = array(
          'default' => $info['prefix'],
        );
      }
      foreach ($info['prefix'] as $table => $prefix) {
        if ($table != 'default') {
          $prefixes[$key][$prefix . $table] = $table;
        }
        elseif ($prefix !== '') {
          $prefixes[$key][$prefix] = '';
        }
      }
    }
  }
  return !empty($prefixes[$key]) ? strtr($name, $prefixes[$key]) : $name;
}

/**
 * Converts a column's Schema type into an engine-specific data type.
 */
function schema_engine_type($col, $table, $field, $engine = NULL) {
  $map = schema_dbobject()
    ->getFieldTypeMap();
  $size = isset($col['size']) ? $col['size'] : 'normal';
  $type = $col['type'] . ':' . $size;
  if (isset($map[$type])) {
    return $map[$type];
  }
  else {
    trigger_error(t('@table.@field: no @engine type for schema type @type.', array(
      '@engine' => $engine,
      '@type' => $type,
      '@table' => $table,
      '@field' => $field,
    )), E_USER_WARNING);
    return $col['type'];
  }
}

/**
 * Convert an engine-specific data type into a Schema type.
 */
function schema_schema_type($type, $table, $field, $engine = NULL) {
  $map = schema_dbobject()
    ->schema_type_map();
  $type = strtolower($type);
  if (isset($map[$type])) {
    return explode(':', $map[$type]);
  }
  else {
    if (!\Drupal::config('schema.settings')
      ->get('schema_suppress_type_warnings')) {
      trigger_error(t('@table.@field: no schema type for @engine type @type.', array(
        '@engine' => $engine,
        '@type' => $type,
        '@table' => $table,
        '@field' => $field,
      )), E_USER_WARNING);
    }
    return array(
      $type,
      'normal',
    );
  }
}

/**
 * Compares two complete schemas.
 * @param $ref array is considered the reference copy
 * @param $inspect array is compared against it.  If $inspect is NULL, a
 *         schema for the active database is generated and used.
 *
 * @deprecated
 */
function schema_compare_schemas($ref, $inspect = NULL) {
  if (!isset($inspect)) {
    $inspect = schema_dbobject()
      ->inspect();
  }
  $comparator = new SchemaComparator($ref, schema_dbobject());
  $result = $comparator
    ->execute();
  $result = new SchemaComparisonInfoBuilder($result);
  return $result
    ->getInfoArray();
}

/**
 * Compares a reference specification (such as one returned by a
 * module's hook_schema) to an inspected specification from the
 * database.
 * @param $inspect if not provided, the database is inspected.
 *
 * @deprecated
 */
function schema_compare_table($ref, $inspect = NULL) {
  return;

  // @todo Refactor SchemaComparator and call from this function.
  $_db_type = db_driver();
  if (!isset($inspect)) {

    // TODO: Handle prefixing the D7 way
    //    $ref_name = db_prefix_tables('{' . $ref['name'] . '}');
    $ref_name = $ref['name'];
    $inspect = schema_dbobject()
      ->inspect(NULL, $ref_name);
    $inspect = $inspect[$ref['name']];
  }
  if (!isset($inspect)) {
    return array(
      'status' => 'missing',
    );
  }

  // Removed comparison code; most of it now resides in
  // SchemaComparator::compareTable().
}

/**
 * Computes and returns the complete schema for all the drupal things.
 */
function schema_get_schema($rebuild = FALSE) {

  /** @var SchemaManager $manager */
  $manager = Drupal::service('plugin.manager.schema');
  $plugins = $manager
    ->createInstances();
  $complete_schema = array();

  /** @var SchemaProviderInterface $plugin */
  foreach ($plugins as $plugin) {
    $complete_schema += $plugin
      ->get($rebuild);
  }
  return $complete_schema;
}

Functions

Namesort descending Description
schema_compare_schemas Compares two complete schemas.
schema_compare_table Compares a reference specification (such as one returned by a module's hook_schema) to an inspected specification from the database.
schema_dbobject Fetch a schema engine class instance for a given database connection.
schema_engine_type Converts a column's Schema type into an engine-specific data type.
schema_get_connection_engine_class Fetch the schema engine class name for a given database connection.
schema_get_connection_options Get an array of connection options that are supported by schema inspection.
schema_get_schema Computes and returns the complete schema for all the drupal things.
schema_help Implements hooK_help().
schema_phpprint Builds a pretty ASCII-formatted version of a $schema array.
schema_phpprint_column
schema_phpprint_key
schema_phpprint_table
schema_schema_type Convert an engine-specific data type into a Schema type.
schema_unprefix_table Unprefix a table name.