From ad8d8568b683e6935bec64abe88f79bf31706dd7 Mon Sep 17 00:00:00 2001 From: Silvio Date: Wed, 18 Aug 2010 15:22:43 -0300 Subject: Adding audit and finder classes, cleanup and organization --- apps/audit.php | 26 ++ apps/csv.php | 2 +- apps/field.php | 21 +- apps/index.php | 2 +- apps/repetition.php | 21 +- apps/subfield.php | 24 +- classes/BiblioIsisDb.php | 418 ------------------------------- classes/Cinisis.php | 196 +++++++++++++++ classes/CinisisDb.php | 196 --------------- classes/IsisAudit.php | 38 +++ classes/IsisConnector.php | 6 +- classes/IsisDb.php | 63 ----- classes/IsisFinder.php | 103 ++++++++ classes/MaleteDb.php | 161 ------------ classes/PhpIsisDb.php | 158 ------------ classes/SchemaDb.php | 96 ------- classes/backends/BiblioIsisDb.php | 418 +++++++++++++++++++++++++++++++ classes/backends/IsisDb.php | 63 +++++ classes/backends/MaleteDb.php | 161 ++++++++++++ classes/backends/PhpIsisDb.php | 158 ++++++++++++ classes/backends/SchemaDb.php | 96 +++++++ classes/helpers/CinisisDisplayHelper.php | 2 +- index.php | 5 +- samples/read.php | 2 +- samples/test.php | 2 +- 25 files changed, 1290 insertions(+), 1148 deletions(-) create mode 100644 apps/audit.php delete mode 100644 classes/BiblioIsisDb.php create mode 100644 classes/Cinisis.php delete mode 100644 classes/CinisisDb.php create mode 100644 classes/IsisAudit.php delete mode 100644 classes/IsisDb.php create mode 100644 classes/IsisFinder.php delete mode 100644 classes/MaleteDb.php delete mode 100644 classes/PhpIsisDb.php delete mode 100644 classes/SchemaDb.php create mode 100644 classes/backends/BiblioIsisDb.php create mode 100644 classes/backends/IsisDb.php create mode 100644 classes/backends/MaleteDb.php create mode 100644 classes/backends/PhpIsisDb.php create mode 100644 classes/backends/SchemaDb.php diff --git a/apps/audit.php b/apps/audit.php new file mode 100644 index 0000000..28237b0 --- /dev/null +++ b/apps/audit.php @@ -0,0 +1,26 @@ +run(); + + // Format output. + echo "
\n";
+  print_r($result);
+  echo '
'; +} + +$display->footer(); diff --git a/apps/csv.php b/apps/csv.php index e50249b..ae99ad0 100644 --- a/apps/csv.php +++ b/apps/csv.php @@ -56,7 +56,7 @@ function merge_fields($data, $field) { require_once '../index.php'; // Get a db instance. -$isis = new CinisisDb(); +$isis = new Cinisis(); // Test connection. if ($isis->db) { diff --git a/apps/field.php b/apps/field.php index 13320e6..f34bffa 100644 --- a/apps/field.php +++ b/apps/field.php @@ -14,28 +14,19 @@ $field = CinisisHttpHelper::get_numeric_arg('field'); $display = new CinisisDisplayHelper('Field finder'); $form = $display->form_input_text('entry', $entry); $form .= $display->form_input_text('field', $field); -$display->form($form, 'field.php'); +$display->form($form, basename(__FILE__)); // Get a db instance. -$isis = new CinisisDb(); +$isis = new IsisFinder(); // Setup database and entry number. -if ($isis->db) { - // Get the number of entries. - $field_name = $isis->db->format['fields'][$field]['name']; - $entries = $isis->db->entries(); - $entry--; - +if ($isis) { // Query database. - do { - $result = $isis->db->read(++$entry); - if ($entry == $entries) { - break; - } - } while (!isset($result[$field_name])); + $field_name = $isis->getFieldName($field); + $result = $isis->nextField($entry, $field_name); // Navigation bar. - $display->navbar($entry, $entries, $repetition, '&field='. $field); + $display->navbar($entry, $isis->entries, $repetition, '&field='. $field); // Format output. echo "
\n";
diff --git a/apps/index.php b/apps/index.php
index 54d7d0e..cd5e5bb 100644
--- a/apps/index.php
+++ b/apps/index.php
@@ -14,7 +14,7 @@ $display = new CinisisDisplayHelper('Isis Navigator');
 $display->form($display->form_input_text('entry', $entry));
 
 // Get a db instance.
-$isis = new CinisisDb();
+$isis = new Cinisis();
 
 // Setup database and entry number.
 if ($isis->db) {
diff --git a/apps/repetition.php b/apps/repetition.php
index 7c7f245..6b7f9de 100644
--- a/apps/repetition.php
+++ b/apps/repetition.php
@@ -14,28 +14,19 @@ $field = CinisisHttpHelper::get_numeric_arg('field');
 $display = new CinisisDisplayHelper('Repetition finder');
 $form    = $display->form_input_text('entry', $entry);
 $form   .= $display->form_input_text('field', $field);
-$display->form($form, 'repetition.php');
+$display->form($form, basename(__FILE__));
 
 // Get a db instance.
-$isis = new CinisisDb();
+$isis = new IsisFinder();
 
 // Setup database and entry number.
-if ($isis->db) {
-  // Get the number of entries.
-  $field_name = $isis->db->format['fields'][$field]['name'];
-  $entries    = $isis->db->entries();
-  $entry--;
-
+if ($isis) {
   // Query database.
-  do {
-    $result = $isis->db->read(++$entry);
-    if ($entry == $entries) {
-      break;
-    }
-  } while (!isset($result[$field_name]) || count($result[$field_name]) < 2);
+  $field_name = $isis->getFieldName($field);
+  $result     = $isis->nextRepetition($entry, $field_name);
 
   // Navigation bar.
-  $display->navbar($entry, $entries, $repetition, '&field='. $field);
+  $display->navbar($entry, $isis->entries, $repetition, '&field='. $field);
 
   // Format output.
   echo "
\n";
diff --git a/apps/subfield.php b/apps/subfield.php
index 93de5a9..e3d5d75 100644
--- a/apps/subfield.php
+++ b/apps/subfield.php
@@ -16,32 +16,22 @@ $display = new CinisisDisplayHelper('Subfield finder');
 $form    = $display->form_input_text('entry', $entry);
 $form   .= $display->form_input_text('field', $field);
 $form   .= $display->form_input_text('subfield', $subfield);
-$display->form($form, 'subfield.php');
+$display->form($form, basename(__FILE__));
 
 // Get a db instance.
-$isis = new CinisisDb();
+$isis = new IsisFinder();
 
 // Setup database and entry number.
-if ($isis->db) {
-  // Get the number of entries.
-  $field_name    = $isis->db->format['fields'][$field]['name'];
-  $subfield_name = $isis->db->format['fields'][$field]['subfields'][$subfield];
-  $entries       = $isis->db->entries();
-  $entry--;
-
+if ($isis) {
   // Query database.
-  do {
-    $result = $isis->db->read(++$entry);
-    if ($entry == $entries) {
-      break;
-    }
-  } while (!isset($result[$field_name][0][$subfield_name]));
+  $field_name    = $isis->getFieldName($field);
+  $subfield_name = $isis->getSubfieldName($field, $subfield);
+  $result        = $isis->nextSubfield($entry, $field_name, $subfield_name);
 
   // Navigation bar.
-  $display->navbar($entry, $entries, $repetition, '&field='. $field . '&subfield='. $subfield);
+  $display->navbar($entry, $isis->entries, $repetition, '&field='. $field . '&subfield='. $subfield);
 
   // Format output.
-  $link = $display->entry_link($entry);
   echo "
\n";
   echo "Selected field: $field: $field_name.\n";
   echo "Selected subfield: $subfield: $subfield_name.\n";
diff --git a/classes/BiblioIsisDb.php b/classes/BiblioIsisDb.php
deleted file mode 100644
index 9db0145..0000000
--- a/classes/BiblioIsisDb.php
+++ /dev/null
@@ -1,418 +0,0 @@
-format = $schema;
-
-    // Setup $fdt.
-    foreach ($schema['fields'] as $field => $info) {
-      $this->fdt[$field] = $info['name'];
-    }
-
-    // Create a perl instance.
-    $this->perl = new Perl();
-  }
-
-  /**
-   * Class logger.
-   *
-   * @param $message
-   *   Log message.
-   */
-  function logger($message) {
-    $this->log[] = $message;
-  }
-
-  /**
-   * Send requests to the perl backend.
-   *
-   * @param $method
-   *   Backend method name to invoke.
-   *
-   * @param $args
-   *   Backend method arguments.
-   *
-   * @return
-   *   Backend return value.
-   */
-  function backend($method = 'count', $args = NULL) {
-    // Setup the database.
-    $name = $this->format['db']['name'];
-    $db   = CinisisDb::file("$name/$name", 'db');
-
-    // Setup arguments.
-    if ($args != NULL) {
-      $args = '('. $args .')';
-    }
-
-    try {
-      // Call backend.
-      return $this->perl->eval('
-        use Biblio::Isis;
-
-        my $isis = new Biblio::Isis(
-           isisdb => "'. $db .'",
-        );
-
-        return $isis->'. $method . $args .';');
-    }
-    catch (PerlException $exception) {
-      echo __CLASS__ .': Perl error: ' . $exception->getMessage();
-      return FALSE;
-    }    
-  }
-
-  /**
-   * Read an entry.
-   *
-   * @param $id
-   *   Record Id.
-   *
-   * @param $method
-   *   Database read method.
-   *
-   * @see IsisDb::read()
-   */  
-  public function read($id, $method = 'fetch') {
-    // Database query.
-    $results = $this->backend($method, $id);
-
-    if ($results) {
-      // Tag results.
-      $data = $this->tag($results, $method);
-
-      // Charset conversion.
-      if (is_array($data)) {
-        array_walk_recursive($data, array(__CLASS__, 'charset'));
-      }
-
-      // Return the result.
-      return $data;
-    }
-  }
-
-  /**
-   * Return number of entries in the database.
-   *
-   * @see IsisDb::entries()
-   */  
-  public function entries() {
-    return $this->backend('count');
-  }
-
-  /**
-   * Return an example schema.
-   *
-   * @see IsisDb::example()
-   */  
-  public function example() {
-    return SchemaDb::example();
-  }
-
-  /**
-   * Check configuration.
-   *
-   * @see IsisDb::check()
-   */  
-  static function check($schema, $section = NULL) {
-    // Check API availability.
-    if (!class_exists('Perl')) {
-      throw new Exception('Could not find Perl class. Please check your php-perl installation.');
-      return FALSE;
-    }
-
-    // Check schema configuration.
-    return SchemaDb::check($schema, $section);
-  }
-
-  /**
-   * Tag results of a db query.
-   *
-   * This function converts the keys of query result from field numbers
-   * to names.
-   *
-   * @param $results
-   *   Database query results.
-   *
-   * @param $method
-   *   Database read method.
-   *
-   * @return
-   *   Tagged database result.
-   */
-  function tag($results, $method = 'fetch') {
-    foreach ($results as $key => $value) {
-      // Key '000' used to hold MFN.
-      if ($key != '000') {
-        if (!isset($this->format['fields'][$key])) {
-          continue;
-        }
-
-        // Format, repetition and subfield handling.
-        $name        = $this->format['fields'][$key]['name'];
-        $data[$name] = $this->repetition($key, $value);
-        $data[$name] = $this->subfields($data[$name], $key, $method);
-      }
-    }
-
-    return $data;
-  }
-
-  /**
-   * Checks whether a field has subfields.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @return
-   *   True if has subfields, false otherwise.
-   */
-  function has_subfields($key) {
-    if (isset($this->format['fields'][$key]['subfields'])) {
-      return TRUE;
-    }
-
-    return FALSE;
-  }
-
-  /**
-   * Switch keys on subfields.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @param $value
-   *   Dataset.
-   */
-  function subfields_switch($key, &$value) {
-    if (!is_array($value)) {
-      return;
-    }
-
-    foreach ($value as $subkey => $subvalue) {
-      if (isset($this->format['fields'][$key]['subfields'][$subkey])) {
-        $subname = $this->format['fields'][$key]['subfields'][$subkey];
-      } else {
-        $subname = $subkey;
-      }
-
-      $value[$subname] = $subvalue;
-
-      if ($subkey != $subname) {
-        unset($value[$subkey]);
-      }
-    }
-  }
-
-  /**
-   * Makes subfield substitution in a dataset.
-   *
-   * @param $name
-   *   Dataset.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @param $method
-   *   Database read method.
-   *
-   * @return
-   *   Data with processed subfields.
-   */
-  function subfields($name, $key, $method) {
-    if ($this->has_subfields($key) && is_array($name)) {
-      $method = 'subfields_from_'. $method;
-      return $this->{$method}($name, $key);
-    }
-    else {
-      foreach ($name as $value) {
-        $data[] = array(CinisisDb::main_field_name($this->format, $key) => $value);
-      }
-    }
-
-    return $data;
-  }
-
-  /**
-   * Subfield handling for data read by 'to_hash' method. This method
-   * is not fully supported and therefore not recommended.
-   *
-   * It does not deal very well when data has "main" fields and
-   * subfields (like "data1^adata2^bdata3") and doesn't deal with
-   * advanced configuration such as 'join_subfields'.
-   *
-   * @param $name
-   *   Dataset.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @return
-   *   Data with processed subfields.
-   */
-  function subfields_from_to_hash($name, $key) {
-    if ($this->is_repetitive($key, $name)) {
-      foreach ($name as $entry => $value) {
-        $this->subfields_switch($key, $value);
-        $name[$entry] = $value;
-      }
-    }
-    else {
-      $this->subfields_switch($key, $name);
-    }
-
-    return $name;
-  }
-
-  /**
-   * Subfield handling for data read by 'from_fetch' method.
-   *
-   * @param $name
-   *   Dataset.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @return
-   *   Data with processed subfields.
-   */
-  function subfields_from_fetch($name, $key) {
-    // Check if entry has repetitions.
-    $this->is_repetitive($key, $name);
-
-    // Iterate over all values.
-    foreach ($name as $entry => $value) {
-      if (substr($value, 0, 1) != '^') {
-        $field     = preg_replace('/\^.*/', '', $value);
-        $subfields = substr($value, strlen($field) + 1);
-        $subfields = (!empty($subfields)) ? $subfields = explode('^', $subfields) : array();
-
-        if (isset($field)) {
-          $data[$entry]['field'] = $field;
-        }
-      }
-      else {
-        $subfields = explode('^', substr($value, 1));
-      }
-
-      // Subfield tagging.
-      foreach ($subfields as $subfield => $subvalue) {
-        $subkey = substr($subvalue, 0, 1);
-        if (isset($this->format['fields'][$key]['subfields'][$subkey])) {
-          $subkey = $this->format['fields'][$key]['subfields'][$subkey];
-        }
-        $data[$entry]['subfields'][$subkey] = substr($subvalue, 1);
-      }
-
-      // Join subfields and main field if needed.
-      if (CinisisDb::join_subfields($this->format)) {
-        if (isset($data[$entry]['subfields'])) {
-          $data[$entry] = $data[$entry]['subfields'];
-        }
-
-        if (isset($field)) {
-          unset($data[$entry]['field']);
-          $data[$entry][CinisisDb::main_field_name($this->format, $key)] = $field;
-        }
-      }
-    }
-
-    return $data;
-  }
-
-  /**
-   * Deals with repetition.
-   *
-   * As Biblio::Isis always return field values as arrays, we
-   * have to check the database schema to see if we want to
-   * convert then to a single value.
-   *
-   * @param $field
-   *   Database field.
-   *
-   * @param $value
-   *   Data (with or without repetition).
-   *
-   * @return
-   *   True if repetitive, false otherwise.
-   */
-  function is_repetitive($field, $value) {
-    if (isset($this->format['fields'][$field]['repeat']) &&
-      $this->format['fields'][$field]['repeat'] == FALSE) {
-        if (is_array($value) && count($value) > 1) {
-          $this->logger("Field $field is configured as non repetitive but data shows a repetition for value.");
-        }
-        return FALSE;
-      }
-
-    return TRUE;
-  }
-
-  /**
-   * Deals with repetition.
-   *
-   * As Biblio::Isis always return field values as arrays, we
-   * have to check the database schema to see if we want to
-   * convert then to a single value. The current implementation
-   * is just a placeholder as no conversion is done.
-   *
-   * @param $key
-   *   Database key.
-   *
-   * @param $value
-   *   Query field result.
-   *
-   * @return
-   *   The value according to the repetition config.
-   */
-  function repetition($key, $value) {
-    return $value;
-  }
-
-  /**
-   * Charset conversion.
-   *
-   * Converts a string from the database charset to UTF-8.
-   *
-   * @param $data
-   *   String to be converted.
-   *
-   * @return
-   *   String converted to UTF-8.
-   */
-  function charset(&$data) {
-    $data = iconv($this->format['db']['charset'], 'UTF-8', $data);
-  }
-}
diff --git a/classes/Cinisis.php b/classes/Cinisis.php
new file mode 100644
index 0000000..8771b21
--- /dev/null
+++ b/classes/Cinisis.php
@@ -0,0 +1,196 @@
+open($config);
+  }
+
+  /**
+   * Open an ISIS database.
+   *
+   * @param $config
+   *   Optional parameter to set alternative config file or array
+   *   with configuration.
+   */
+  public function open($config)
+  {
+    try {
+      // Check main configuration.
+      $config = $this->parse($this->file($config));
+
+      // Set database implementation.
+      $this->implementation = $config['implementation'] .'Db';
+
+      // Check database schema.
+      $schema = $this->parse($this->file($config['database'] .'.yaml', 'schemas'), $this->implementation);
+    } catch (Exception $e) {
+      echo __CLASS__ .' caught exception: ', $e->getMessage(), "\n";
+      return FALSE;
+    }
+
+    // Setup database connection.
+    $this->db = new $this->implementation($schema);
+  }
+
+  /**
+   * Config file load.
+   *
+   * @param $file
+   *   Config file.
+   *
+   * @return
+   *   Array with configuration or FALSE if error.
+   */
+  public function load($file) {
+    if (!file_exists($file)) {
+      throw new Exception('Config '. $file .' not found.');
+      return FALSE;
+    }
+
+    // Load configuration.
+    return Spyc::YAMLLoad($file);
+  }
+
+  /**
+   * Parse configuration.
+   *
+   * @param $config
+   *   Config file or array with configuration.
+   *
+   * @param $class
+   *   Configuration class name.
+   *
+   * @return
+   *   Array with configuration or FALSE on error.
+   */
+  public function parse($config, $class = __CLASS__) {
+    // Load configuration if needed.
+    if (!is_array($config)) {
+      $config = $this->load($config);
+    }
+
+    // Check configuration.
+    return call_user_func(array($class, 'check'), $config);
+  }
+
+  /**
+   * Check configuration.
+   *
+   * @param $config
+   *   Config file or array with configuration.
+   *
+   * @return
+   *   Array with configuration or FALSE on error.
+   */  
+  static function check($config) {
+    // Set default database backend if needed.
+    if (!isset($config['implementation'])) {
+      $config['implementation'] = 'PhpIsis';
+    }
+
+    // Check database configuration.
+    if (!isset($config['database'])) {
+      throw new Exception('No database set on configuration.');
+      return FALSE;
+    }
+
+    return $config;
+  }
+
+  /**
+   * Get library base folder.
+   *
+   * @return
+   *   Return base folder.
+   */
+  static function base() {
+    return dirname(__FILE__) .'/../';
+  }
+
+  /**
+   * Get a file path.
+   *
+   * @param $config
+   *   Config file name (either relative to the library or absolute)
+   *   or array with configuration.
+   *
+   * @param $section
+   *   Config file section (ignored for absolute files).
+   *
+   * @return
+   *   Return the assembled file path.
+   */
+  static function file($config = NULL, $section = 'config') {
+    // Check config format (array, NULL or relative config path).
+    if (is_array($config)) {
+      return $config;
+    }
+    elseif ($config == NULL) {
+      $config = "$section/config.yaml";
+    }
+    elseif (substr($config, 0, 1) != '/') {
+      $config = "$section/$config";
+    }
+
+    return call_user_func(array(__CLASS__, 'base')) .'/'. $config;
+  }
+
+  /**
+   * Whether to join field and subfields in a single array.
+   *
+   * @param $format
+   *   Database format.
+   *
+   * @return
+   *   Boolean.
+   */
+  static function join_subfields($format) {
+    if ($format['db']['join_subfields']) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Determine the main field name depending on db configuration.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @param $format
+   *   Database format.
+   *
+   * @return
+   *   Main field name, 'field' by default.
+   */
+  static function main_field_name($format, $key) {
+    if (self::join_subfields($format)) {
+      return $format['fields'][$key]['name'];
+    }
+
+    return 'field';
+  }
+}
diff --git a/classes/CinisisDb.php b/classes/CinisisDb.php
deleted file mode 100644
index a016528..0000000
--- a/classes/CinisisDb.php
+++ /dev/null
@@ -1,196 +0,0 @@
-open($config);
-  }
-
-  /**
-   * Open an ISIS database.
-   *
-   * @param $config
-   *   Optional parameter to set alternative config file or array
-   *   with configuration.
-   */
-  public function open($config)
-  {
-    try {
-      // Check main configuration.
-      $config = $this->parse($this->file($config));
-
-      // Set database implementation.
-      $this->implementation = $config['implementation'] .'Db';
-
-      // Check database schema.
-      $schema = $this->parse($this->file($config['database'] .'.yaml', 'schemas'), $this->implementation);
-    } catch (Exception $e) {
-      echo __CLASS__ .' caught exception: ', $e->getMessage(), "\n";
-      return FALSE;
-    }
-
-    // Setup database connection.
-    $this->db = new $this->implementation($schema);
-  }
-
-  /**
-   * Config file load.
-   *
-   * @param $file
-   *   Config file.
-   *
-   * @return
-   *   Array with configuration or FALSE if error.
-   */
-  public function load($file) {
-    if (!file_exists($file)) {
-      throw new Exception('Config '. $file .' not found.');
-      return FALSE;
-    }
-
-    // Load configuration.
-    return Spyc::YAMLLoad($file);
-  }
-
-  /**
-   * Parse configuration.
-   *
-   * @param $config
-   *   Config file or array with configuration.
-   *
-   * @param $class
-   *   Configuration class name.
-   *
-   * @return
-   *   Array with configuration or FALSE on error.
-   */
-  public function parse($config, $class = __CLASS__) {
-    // Load configuration if needed.
-    if (!is_array($config)) {
-      $config = $this->load($config);
-    }
-
-    // Check configuration.
-    return call_user_func(array($class, 'check'), $config);
-  }
-
-  /**
-   * Check configuration.
-   *
-   * @param $config
-   *   Config file or array with configuration.
-   *
-   * @return
-   *   Array with configuration or FALSE on error.
-   */  
-  static function check($config) {
-    // Set default database backend if needed.
-    if (!isset($config['implementation'])) {
-      $config['implementation'] = 'PhpIsis';
-    }
-
-    // Check database configuration.
-    if (!isset($config['database'])) {
-      throw new Exception('No database set on configuration.');
-      return FALSE;
-    }
-
-    return $config;
-  }
-
-  /**
-   * Get library base folder.
-   *
-   * @return
-   *   Return base folder.
-   */
-  static function base() {
-    return dirname(__FILE__) .'/../';
-  }
-
-  /**
-   * Get a file path.
-   *
-   * @param $config
-   *   Config file name (either relative to the library or absolute)
-   *   or array with configuration.
-   *
-   * @param $section
-   *   Config file section (ignored for absolute files).
-   *
-   * @return
-   *   Return the assembled file path.
-   */
-  static function file($config = NULL, $section = 'config') {
-    // Check config format (array, NULL or relative config path).
-    if (is_array($config)) {
-      return $config;
-    }
-    elseif ($config == NULL) {
-      $config = "$section/config.yaml";
-    }
-    elseif (substr($config, 0, 1) != '/') {
-      $config = "$section/$config";
-    }
-
-    return call_user_func(array(__CLASS__, 'base')) .'/'. $config;
-  }
-
-  /**
-   * Whether to join field and subfields in a single array.
-   *
-   * @param $format
-   *   Database format.
-   *
-   * @return
-   *   Boolean.
-   */
-  static function join_subfields($format) {
-    if ($format['db']['join_subfields']) {
-      return TRUE;
-    }
-
-    return FALSE;
-  }
-
-  /**
-   * Determine the main field name depending on db configuration.
-   *
-   * @param $key
-   *   Field key.
-   *
-   * @param $format
-   *   Database format.
-   *
-   * @return
-   *   Main field name, 'field' by default.
-   */
-  static function main_field_name($format, $key) {
-    if (self::join_subfields($format)) {
-      return $format['fields'][$key]['name'];
-    }
-
-    return 'field';
-  }
-}
diff --git a/classes/IsisAudit.php b/classes/IsisAudit.php
new file mode 100644
index 0000000..79cbe26
--- /dev/null
+++ b/classes/IsisAudit.php
@@ -0,0 +1,38 @@
+format['fields'] as $field) {
+      $field_name = $this->getFieldName($field);
+      $repetition = $this->nextRepetition(null, $field_name);
+
+      // Check for repetitions.
+      if ($field['repeat'] && !$repetition)
+      {
+        echo "Field $field_name is configured for repetitions but no repetitions found.\n";
+      }
+      elseif (!$field['repeat'] && $repetition) {
+        echo "Field $field_name is not configured for repetitions but a repetition was found.\n";
+      }
+
+      // Check for subfields.
+      foreach ($field['subfields'] as $subfield) {
+        $subfield_name = $this->getSubfieldName($field, $subfield);
+        $next_subfield = $isis->nextSubfield(null, $field_name, $subfield_name);
+
+        if (!$next_subfield) {
+          echo "No occurrences found for field $field_name and subfield $subfield_name\n";
+        }
+      }
+    }
+  }
+}
diff --git a/classes/IsisConnector.php b/classes/IsisConnector.php
index c8d8234..e0c87c2 100644
--- a/classes/IsisConnector.php
+++ b/classes/IsisConnector.php
@@ -19,7 +19,7 @@ class IsisConnector {
    *   Config file or array.
    */
   public function open($config) {
-    $this->isis = new CinisisDb($config);
+    $this->isis = new Cinisis($config);
 
     if ($this->isis->db) {
       $this->entries = $this->isis->db->entries();
@@ -59,7 +59,7 @@ class IsisConnector {
    */
   public function getMainItemName($field) {
     $key = $this->getFieldKey($field);
-    return CinisisDb::main_field_name($this->format, $key);    
+    return Cinisis::main_field_name($this->format, $key);    
   }
 
   /**
@@ -69,7 +69,7 @@ class IsisConnector {
    *   Boolean.
    */
   public function joinSubfields() {
-    if (CinisisDb::join_subfields($this->format)) {
+    if (Cinisis::join_subfields($this->format)) {
       return TRUE;
     }
 
diff --git a/classes/IsisDb.php b/classes/IsisDb.php
deleted file mode 100644
index 4a2218a..0000000
--- a/classes/IsisDb.php
+++ /dev/null
@@ -1,63 +0,0 @@
-read(++$entry);
+      if ($entry == $entries) {
+        break;
+      }
+    } while (!isset($result[$field]) || count($result[$field]) < 2);
+
+    if (!isset($result[$field]) || count($result[$field]) < 2) {
+      return FALSE;
+    }
+
+    return $result;
+  }
+
+  /**
+   * Find the next occurrence of a field.
+   *
+   * @param $entry
+   *   Start entry number to begin the search.
+   *
+   * @param $field
+   *   Field name.
+   *
+   * @return
+   *   Next occurrence.
+   */
+  public function nextField($entry = 1, $field) {
+    $entry--;
+
+    // Query database.
+    do {
+      $result = $this->read(++$entry);
+      if ($entry == $entries) {
+        break;
+      }
+    } while (!isset($result[$field]));
+
+    if (!isset($result[$field])) {
+      return FALSE;
+    }
+
+    return $result;
+  }
+
+  /**
+   * Find the next occurrence of a subfield.
+   *
+   * @param $entry
+   *   Start entry number to begin the search.
+   *
+   * @param $field
+   *   Field name.
+   *
+   * @param $subfield
+   *   Subfield name.
+   *
+   * @return
+   *   Next occurrence.
+   *
+   * @todo
+   *   The subfield might be in any now and not just
+   *   in the first one.
+   */
+  public function nextSubfield($entry = 1, $field, $subfield) {
+    $entry--;
+
+    // Query database.
+    do {
+      $result = $this->read(++$entry);
+      if ($entry == $entries) {
+        break;
+      }
+    } while (!isset($result[$field][0][$subfield]));
+
+    if (!isset($result[$field][0][$subfield])) {
+      return FALSE;
+    }
+
+    return $result;
+  }
+}
diff --git a/classes/MaleteDb.php b/classes/MaleteDb.php
deleted file mode 100644
index 114995b..0000000
--- a/classes/MaleteDb.php
+++ /dev/null
@@ -1,161 +0,0 @@
-format = $schema;
-
-    // Setup $fdt used by malete.
-    foreach ($schema['fields'] as $field => $info) {
-      $this->fdt[$field] = $info['name'];
-    }
-
-    // Open a database connection.
-    $this->db = new Isis_Db($this->fdt, $schema['db']['name'], new Isis_Server());
-    if (!$this->db->srv->sock) {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Read an entry.
-   *
-   * @see IsisDb::read()
-   *
-   * @todo
-   *   Subfield handling.
-   */  
-  public function read($id) {
-    if (!is_numeric($id)) {
-      return FALSE;
-    }
-    if ($results !== FALSE) {
-      $results = $this->db->read($id);
-      return $this->tag($results);
-    }
-    else {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Return number of entries in the database.
-   *
-   * The Malete API doen't implement such feature so we
-   * have to emulate it by iterating over all entries
-   * until MaleteDb::read() returns FALSE.
-   *
-   * @see IsisDb::entries()
-   */  
-  public function entries() {
-    // The first entry in a malete database has id 1 and
-    // not 0, therefore $id's initial value should be 1.
-    $id = 1;
-    while($this->db->read($id)) {
-      $id++; 
-    }
-    return $id - 1;
-  }
-
-  /**
-   * Return an example schema.
-   *
-   * @see IsisDb::example()
-   */  
-  public function example() {
-    return SchemaDb::example();
-  }
-
-  /**
-   * Check configuration.
-   *
-   * @see IsisDb::check()
-   */  
-  public function check($schema, $section = NULL) {
-    // Check API availability.
-    if (!class_exists('Isis_Db')) {
-      throw new Exception('Could not find Isis_Db class. Please check your malete installation.');
-      return FALSE;
-    }
-
-    // Check schema configuration.
-    return SchemaDb::check($schema, $section);
-  }
-
-  /**
-   * Tag results of a db query.
-   *
-   * This function converts the keys of query result from field numbers
-   * to names and and also puts repetition fields into place as Malete
-   * deals with field repetition by using a 'tag' property in the resulting
-   * query object. 
-   *
-   * @param $results
-   *   Database query results.
-   *
-   * @return
-   *   Tagged database result.
-   */
-  function tag($results) {
-    foreach ($results->val as $key => $value) {
-      $field = $results->tag[$key];
-      $name  = $this->format['fields'][$field]['name'];
-
-      // Handles field repetition.
-      if ($this->format['fields'][$field]['repeat']) {
-        $data[$name][] = $value;
-      }
-      else {
-        $data[$name] = $value;
-      }
-    }
-    return $data;
-  }
-
-  /**
-   * Class logger.
-   *
-   * @param $message
-   *   Log message.
-   */
-  function logger($message) {
-    $this->log[] = $message;
-  }
-}
diff --git a/classes/PhpIsisDb.php b/classes/PhpIsisDb.php
deleted file mode 100644
index 41319cc..0000000
--- a/classes/PhpIsisDb.php
+++ /dev/null
@@ -1,158 +0,0 @@
-format = $schema;
-
-    // Setup $fdt.
-    foreach ($schema['fields'] as $field => $info) {
-      $this->fdt[$field] = $info['name'];
-    }
-
-    // Open the database.
-    $name     = $schema['db']['name'];
-    $this->db = isis_open(CinisisDb::file("$name/$name", 'db'));
-  }
-
-  /**
-   * Read an entry.
-   *
-   * The PHP-Isis API doen't implement such feature so we
-   * have to emulate it by geting all entries and using
-   * isis_data_seek() to get the desired record.
-   *
-   * @see IsisDb::read()
-   *
-   * @todo
-   *   Subfield handling.
-   */    
-  public function read($id) {
-    $results = isis_search('$', $this->db);
-    if (!isis_data_seek($results, $id - 1)) {
-      return FALSE;
-    }
-
-    // Tag results.
-    $data = $this->tag(isis_fetch_array($results));
-
-    // Charset conversion.
-    array_walk_recursive($data, array(__CLASS__, 'charset'));
-
-    // Return the result.
-    return $data;
-  }
-
-  /**
-   * Return number of entries in the database.
-   *
-   * @see IsisDb::entries()
-   */    
-  public function entries() {
-    return isis_last_mfn($this->db);
-  }
-
-  /**
-   * Return an example schema.
-   *
-   * @see IsisDb::example()
-   */    
-  public function example() {
-    return SchemaDb::example();
-  }
-
-  /**
-   * Check configuration.
-   *
-   * @see IsisDb::check()
-   */  
-  static function check($schema, $section = NULL) {
-    // Check API availability.
-    if (!function_exists('isis_open')) {
-      throw new Exception('Could not find function isis_open. Please check your php-isis installation.');
-      return FALSE;
-    }
-
-    // Check schema configuration.
-    return SchemaDb::check($schema, $section);
-  }
-
-  /**
-   * Tag results of a db query.
-   *
-   * This function converts the keys of query result from field
-   * numbers to names.
-   *   
-   * @param $results
-   *   Database query results.
-   *
-   * @return
-   *   Tagged database result.
-   */
-  function tag($results) {
-    foreach ($results as $key => $value) {
-      if ($key != 'mfn') {
-        $name        = $this->format['fields'][$key]['name'];
-        $data[$name] = $value;
-      }
-    }
-
-    return $data;
-  }
-
-  /**
-   * Charset conversion.
-   *
-   * Converts a string from the database charset to UTF-8.
-   *
-   * @param $data
-   *   String to be converted.
-   *
-   * @return
-   *   String converted to UTF-8.
-   */
-  function charset(&$data) {
-    $data = iconv($this->format['db']['charset'], 'UTF-8', $data);
-  }  
-
-  /**
-   * Class logger.
-   *
-   * @param $message
-   *   Log message.
-   */
-  function logger($message) {
-    $this->log[] = $message;
-  }
-}
diff --git a/classes/SchemaDb.php b/classes/SchemaDb.php
deleted file mode 100644
index db9ca30..0000000
--- a/classes/SchemaDb.php
+++ /dev/null
@@ -1,96 +0,0 @@
- array(
-        'name'         => 'dbname',
-      ),
-      'fields'         => array(
-      ),
-    );
-
-    return $schema;
-  }
-
-  /**
-   * Return the optional database config.
-   *
-   * @return
-   *   Array with optional config.
-   */  
-  public function optional() {
-    $schema   = array(
-      'db'             => array(
-        'charset'      => 'charset',
-      ),
-    );
-
-    return $schema;
-  }
-
-  /**
-   * Return an example database schema.
-   *
-   * @see IsisDb::example()
-   */    
-  public function example() {
-    $required = SchemaDb::required();
-    $optional = SchemaDb::optional();
-    $schema   = array(
-      'db'             => array(
-        'charset'      => 'charset',
-      ),
-      'fields'         => array(
-        1              => array(
-          'name'       => 'field_name',
-          'size'       => 1000,
-          'format'     => 'numeric',
-          'repeat'     => TRUE,
-          'subfields'  => array(
-            'a'        => 'test',
-            'b'        => 'test2',
-          ),
-        ),
-      ),
-    );
-
-    return array_merge_recursive($required, $optional, $schema);
-  }
-
-  /**
-   * Recursively check for required fields in a database schema.
-   *
-   * @see IsisDb::check()
-   */
-  static function check($schema, $section = NULL) {
-    if ($section === NULL) {
-      $section = SchemaDb::required();
-    }
-
-    foreach ($section as $key => $value) {
-      if (!isset($schema[$key])) {
-        throw new Exception('Undefined required parameter '. $key .' on database configuration.');
-        return FALSE;
-      }
-
-      if (is_array($value)) {
-        if (SchemaDb::check($schema[$key], $section[$key]) == FALSE) {
-          return FALSE;
-        }
-      }
-    }
-
-    return $schema;
-  }
-}
diff --git a/classes/backends/BiblioIsisDb.php b/classes/backends/BiblioIsisDb.php
new file mode 100644
index 0000000..bb7f9f5
--- /dev/null
+++ b/classes/backends/BiblioIsisDb.php
@@ -0,0 +1,418 @@
+format = $schema;
+
+    // Setup $fdt.
+    foreach ($schema['fields'] as $field => $info) {
+      $this->fdt[$field] = $info['name'];
+    }
+
+    // Create a perl instance.
+    $this->perl = new Perl();
+  }
+
+  /**
+   * Class logger.
+   *
+   * @param $message
+   *   Log message.
+   */
+  function logger($message) {
+    $this->log[] = $message;
+  }
+
+  /**
+   * Send requests to the perl backend.
+   *
+   * @param $method
+   *   Backend method name to invoke.
+   *
+   * @param $args
+   *   Backend method arguments.
+   *
+   * @return
+   *   Backend return value.
+   */
+  function backend($method = 'count', $args = NULL) {
+    // Setup the database.
+    $name = $this->format['db']['name'];
+    $db   = Cinisis::file("$name/$name", 'db');
+
+    // Setup arguments.
+    if ($args != NULL) {
+      $args = '('. $args .')';
+    }
+
+    try {
+      // Call backend.
+      return $this->perl->eval('
+        use Biblio::Isis;
+
+        my $isis = new Biblio::Isis(
+           isisdb => "'. $db .'",
+        );
+
+        return $isis->'. $method . $args .';');
+    }
+    catch (PerlException $exception) {
+      echo __CLASS__ .': Perl error: ' . $exception->getMessage();
+      return FALSE;
+    }    
+  }
+
+  /**
+   * Read an entry.
+   *
+   * @param $id
+   *   Record Id.
+   *
+   * @param $method
+   *   Database read method.
+   *
+   * @see IsisDb::read()
+   */  
+  public function read($id, $method = 'fetch') {
+    // Database query.
+    $results = $this->backend($method, $id);
+
+    if ($results) {
+      // Tag results.
+      $data = $this->tag($results, $method);
+
+      // Charset conversion.
+      if (is_array($data)) {
+        array_walk_recursive($data, array(__CLASS__, 'charset'));
+      }
+
+      // Return the result.
+      return $data;
+    }
+  }
+
+  /**
+   * Return number of entries in the database.
+   *
+   * @see IsisDb::entries()
+   */  
+  public function entries() {
+    return $this->backend('count');
+  }
+
+  /**
+   * Return an example schema.
+   *
+   * @see IsisDb::example()
+   */  
+  public function example() {
+    return SchemaDb::example();
+  }
+
+  /**
+   * Check configuration.
+   *
+   * @see IsisDb::check()
+   */  
+  static function check($schema, $section = NULL) {
+    // Check API availability.
+    if (!class_exists('Perl')) {
+      throw new Exception('Could not find Perl class. Please check your php-perl installation.');
+      return FALSE;
+    }
+
+    // Check schema configuration.
+    return SchemaDb::check($schema, $section);
+  }
+
+  /**
+   * Tag results of a db query.
+   *
+   * This function converts the keys of query result from field numbers
+   * to names.
+   *
+   * @param $results
+   *   Database query results.
+   *
+   * @param $method
+   *   Database read method.
+   *
+   * @return
+   *   Tagged database result.
+   */
+  function tag($results, $method = 'fetch') {
+    foreach ($results as $key => $value) {
+      // Key '000' used to hold MFN.
+      if ($key != '000') {
+        if (!isset($this->format['fields'][$key])) {
+          continue;
+        }
+
+        // Format, repetition and subfield handling.
+        $name        = $this->format['fields'][$key]['name'];
+        $data[$name] = $this->repetition($key, $value);
+        $data[$name] = $this->subfields($data[$name], $key, $method);
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Checks whether a field has subfields.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @return
+   *   True if has subfields, false otherwise.
+   */
+  function has_subfields($key) {
+    if (isset($this->format['fields'][$key]['subfields'])) {
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Switch keys on subfields.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @param $value
+   *   Dataset.
+   */
+  function subfields_switch($key, &$value) {
+    if (!is_array($value)) {
+      return;
+    }
+
+    foreach ($value as $subkey => $subvalue) {
+      if (isset($this->format['fields'][$key]['subfields'][$subkey])) {
+        $subname = $this->format['fields'][$key]['subfields'][$subkey];
+      } else {
+        $subname = $subkey;
+      }
+
+      $value[$subname] = $subvalue;
+
+      if ($subkey != $subname) {
+        unset($value[$subkey]);
+      }
+    }
+  }
+
+  /**
+   * Makes subfield substitution in a dataset.
+   *
+   * @param $name
+   *   Dataset.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @param $method
+   *   Database read method.
+   *
+   * @return
+   *   Data with processed subfields.
+   */
+  function subfields($name, $key, $method) {
+    if ($this->has_subfields($key) && is_array($name)) {
+      $method = 'subfields_from_'. $method;
+      return $this->{$method}($name, $key);
+    }
+    else {
+      foreach ($name as $value) {
+        $data[] = array(Cinisis::main_field_name($this->format, $key) => $value);
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Subfield handling for data read by 'to_hash' method. This method
+   * is not fully supported and therefore not recommended.
+   *
+   * It does not deal very well when data has "main" fields and
+   * subfields (like "data1^adata2^bdata3") and doesn't deal with
+   * advanced configuration such as 'join_subfields'.
+   *
+   * @param $name
+   *   Dataset.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @return
+   *   Data with processed subfields.
+   */
+  function subfields_from_to_hash($name, $key) {
+    if ($this->is_repetitive($key, $name)) {
+      foreach ($name as $entry => $value) {
+        $this->subfields_switch($key, $value);
+        $name[$entry] = $value;
+      }
+    }
+    else {
+      $this->subfields_switch($key, $name);
+    }
+
+    return $name;
+  }
+
+  /**
+   * Subfield handling for data read by 'from_fetch' method.
+   *
+   * @param $name
+   *   Dataset.
+   *
+   * @param $key
+   *   Field key.
+   *
+   * @return
+   *   Data with processed subfields.
+   */
+  function subfields_from_fetch($name, $key) {
+    // Check if entry has repetitions.
+    $this->is_repetitive($key, $name);
+
+    // Iterate over all values.
+    foreach ($name as $entry => $value) {
+      if (substr($value, 0, 1) != '^') {
+        $field     = preg_replace('/\^.*/', '', $value);
+        $subfields = substr($value, strlen($field) + 1);
+        $subfields = (!empty($subfields)) ? $subfields = explode('^', $subfields) : array();
+
+        if (isset($field)) {
+          $data[$entry]['field'] = $field;
+        }
+      }
+      else {
+        $subfields = explode('^', substr($value, 1));
+      }
+
+      // Subfield tagging.
+      foreach ($subfields as $subfield => $subvalue) {
+        $subkey = substr($subvalue, 0, 1);
+        if (isset($this->format['fields'][$key]['subfields'][$subkey])) {
+          $subkey = $this->format['fields'][$key]['subfields'][$subkey];
+        }
+        $data[$entry]['subfields'][$subkey] = substr($subvalue, 1);
+      }
+
+      // Join subfields and main field if needed.
+      if (Cinisis::join_subfields($this->format)) {
+        if (isset($data[$entry]['subfields'])) {
+          $data[$entry] = $data[$entry]['subfields'];
+        }
+
+        if (isset($field)) {
+          unset($data[$entry]['field']);
+          $data[$entry][Cinisis::main_field_name($this->format, $key)] = $field;
+        }
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Deals with repetition.
+   *
+   * As Biblio::Isis always return field values as arrays, we
+   * have to check the database schema to see if we want to
+   * convert then to a single value.
+   *
+   * @param $field
+   *   Database field.
+   *
+   * @param $value
+   *   Data (with or without repetition).
+   *
+   * @return
+   *   True if repetitive, false otherwise.
+   */
+  function is_repetitive($field, $value) {
+    if (isset($this->format['fields'][$field]['repeat']) &&
+      $this->format['fields'][$field]['repeat'] == FALSE) {
+        if (is_array($value) && count($value) > 1) {
+          $this->logger("Field $field is configured as non repetitive but data shows a repetition for value.");
+        }
+        return FALSE;
+      }
+
+    return TRUE;
+  }
+
+  /**
+   * Deals with repetition.
+   *
+   * As Biblio::Isis always return field values as arrays, we
+   * have to check the database schema to see if we want to
+   * convert then to a single value. The current implementation
+   * is just a placeholder as no conversion is done.
+   *
+   * @param $key
+   *   Database key.
+   *
+   * @param $value
+   *   Query field result.
+   *
+   * @return
+   *   The value according to the repetition config.
+   */
+  function repetition($key, $value) {
+    return $value;
+  }
+
+  /**
+   * Charset conversion.
+   *
+   * Converts a string from the database charset to UTF-8.
+   *
+   * @param $data
+   *   String to be converted.
+   *
+   * @return
+   *   String converted to UTF-8.
+   */
+  function charset(&$data) {
+    $data = iconv($this->format['db']['charset'], 'UTF-8', $data);
+  }
+}
diff --git a/classes/backends/IsisDb.php b/classes/backends/IsisDb.php
new file mode 100644
index 0000000..4a2218a
--- /dev/null
+++ b/classes/backends/IsisDb.php
@@ -0,0 +1,63 @@
+format = $schema;
+
+    // Setup $fdt used by malete.
+    foreach ($schema['fields'] as $field => $info) {
+      $this->fdt[$field] = $info['name'];
+    }
+
+    // Open a database connection.
+    $this->db = new Isis_Db($this->fdt, $schema['db']['name'], new Isis_Server());
+    if (!$this->db->srv->sock) {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Read an entry.
+   *
+   * @see IsisDb::read()
+   *
+   * @todo
+   *   Subfield handling.
+   */  
+  public function read($id) {
+    if (!is_numeric($id)) {
+      return FALSE;
+    }
+    if ($results !== FALSE) {
+      $results = $this->db->read($id);
+      return $this->tag($results);
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Return number of entries in the database.
+   *
+   * The Malete API doen't implement such feature so we
+   * have to emulate it by iterating over all entries
+   * until MaleteDb::read() returns FALSE.
+   *
+   * @see IsisDb::entries()
+   */  
+  public function entries() {
+    // The first entry in a malete database has id 1 and
+    // not 0, therefore $id's initial value should be 1.
+    $id = 1;
+    while($this->db->read($id)) {
+      $id++; 
+    }
+    return $id - 1;
+  }
+
+  /**
+   * Return an example schema.
+   *
+   * @see IsisDb::example()
+   */  
+  public function example() {
+    return SchemaDb::example();
+  }
+
+  /**
+   * Check configuration.
+   *
+   * @see IsisDb::check()
+   */  
+  public function check($schema, $section = NULL) {
+    // Check API availability.
+    if (!class_exists('Isis_Db')) {
+      throw new Exception('Could not find Isis_Db class. Please check your malete installation.');
+      return FALSE;
+    }
+
+    // Check schema configuration.
+    return SchemaDb::check($schema, $section);
+  }
+
+  /**
+   * Tag results of a db query.
+   *
+   * This function converts the keys of query result from field numbers
+   * to names and and also puts repetition fields into place as Malete
+   * deals with field repetition by using a 'tag' property in the resulting
+   * query object. 
+   *
+   * @param $results
+   *   Database query results.
+   *
+   * @return
+   *   Tagged database result.
+   */
+  function tag($results) {
+    foreach ($results->val as $key => $value) {
+      $field = $results->tag[$key];
+      $name  = $this->format['fields'][$field]['name'];
+
+      // Handles field repetition.
+      if ($this->format['fields'][$field]['repeat']) {
+        $data[$name][] = $value;
+      }
+      else {
+        $data[$name] = $value;
+      }
+    }
+    return $data;
+  }
+
+  /**
+   * Class logger.
+   *
+   * @param $message
+   *   Log message.
+   */
+  function logger($message) {
+    $this->log[] = $message;
+  }
+}
diff --git a/classes/backends/PhpIsisDb.php b/classes/backends/PhpIsisDb.php
new file mode 100644
index 0000000..e69898e
--- /dev/null
+++ b/classes/backends/PhpIsisDb.php
@@ -0,0 +1,158 @@
+format = $schema;
+
+    // Setup $fdt.
+    foreach ($schema['fields'] as $field => $info) {
+      $this->fdt[$field] = $info['name'];
+    }
+
+    // Open the database.
+    $name     = $schema['db']['name'];
+    $this->db = isis_open(Cinisis::file("$name/$name", 'db'));
+  }
+
+  /**
+   * Read an entry.
+   *
+   * The PHP-Isis API doen't implement such feature so we
+   * have to emulate it by geting all entries and using
+   * isis_data_seek() to get the desired record.
+   *
+   * @see IsisDb::read()
+   *
+   * @todo
+   *   Subfield handling.
+   */    
+  public function read($id) {
+    $results = isis_search('$', $this->db);
+    if (!isis_data_seek($results, $id - 1)) {
+      return FALSE;
+    }
+
+    // Tag results.
+    $data = $this->tag(isis_fetch_array($results));
+
+    // Charset conversion.
+    array_walk_recursive($data, array(__CLASS__, 'charset'));
+
+    // Return the result.
+    return $data;
+  }
+
+  /**
+   * Return number of entries in the database.
+   *
+   * @see IsisDb::entries()
+   */    
+  public function entries() {
+    return isis_last_mfn($this->db);
+  }
+
+  /**
+   * Return an example schema.
+   *
+   * @see IsisDb::example()
+   */    
+  public function example() {
+    return SchemaDb::example();
+  }
+
+  /**
+   * Check configuration.
+   *
+   * @see IsisDb::check()
+   */  
+  static function check($schema, $section = NULL) {
+    // Check API availability.
+    if (!function_exists('isis_open')) {
+      throw new Exception('Could not find function isis_open. Please check your php-isis installation.');
+      return FALSE;
+    }
+
+    // Check schema configuration.
+    return SchemaDb::check($schema, $section);
+  }
+
+  /**
+   * Tag results of a db query.
+   *
+   * This function converts the keys of query result from field
+   * numbers to names.
+   *   
+   * @param $results
+   *   Database query results.
+   *
+   * @return
+   *   Tagged database result.
+   */
+  function tag($results) {
+    foreach ($results as $key => $value) {
+      if ($key != 'mfn') {
+        $name        = $this->format['fields'][$key]['name'];
+        $data[$name] = $value;
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Charset conversion.
+   *
+   * Converts a string from the database charset to UTF-8.
+   *
+   * @param $data
+   *   String to be converted.
+   *
+   * @return
+   *   String converted to UTF-8.
+   */
+  function charset(&$data) {
+    $data = iconv($this->format['db']['charset'], 'UTF-8', $data);
+  }  
+
+  /**
+   * Class logger.
+   *
+   * @param $message
+   *   Log message.
+   */
+  function logger($message) {
+    $this->log[] = $message;
+  }
+}
diff --git a/classes/backends/SchemaDb.php b/classes/backends/SchemaDb.php
new file mode 100644
index 0000000..db9ca30
--- /dev/null
+++ b/classes/backends/SchemaDb.php
@@ -0,0 +1,96 @@
+ array(
+        'name'         => 'dbname',
+      ),
+      'fields'         => array(
+      ),
+    );
+
+    return $schema;
+  }
+
+  /**
+   * Return the optional database config.
+   *
+   * @return
+   *   Array with optional config.
+   */  
+  public function optional() {
+    $schema   = array(
+      'db'             => array(
+        'charset'      => 'charset',
+      ),
+    );
+
+    return $schema;
+  }
+
+  /**
+   * Return an example database schema.
+   *
+   * @see IsisDb::example()
+   */    
+  public function example() {
+    $required = SchemaDb::required();
+    $optional = SchemaDb::optional();
+    $schema   = array(
+      'db'             => array(
+        'charset'      => 'charset',
+      ),
+      'fields'         => array(
+        1              => array(
+          'name'       => 'field_name',
+          'size'       => 1000,
+          'format'     => 'numeric',
+          'repeat'     => TRUE,
+          'subfields'  => array(
+            'a'        => 'test',
+            'b'        => 'test2',
+          ),
+        ),
+      ),
+    );
+
+    return array_merge_recursive($required, $optional, $schema);
+  }
+
+  /**
+   * Recursively check for required fields in a database schema.
+   *
+   * @see IsisDb::check()
+   */
+  static function check($schema, $section = NULL) {
+    if ($section === NULL) {
+      $section = SchemaDb::required();
+    }
+
+    foreach ($section as $key => $value) {
+      if (!isset($schema[$key])) {
+        throw new Exception('Undefined required parameter '. $key .' on database configuration.');
+        return FALSE;
+      }
+
+      if (is_array($value)) {
+        if (SchemaDb::check($schema[$key], $section[$key]) == FALSE) {
+          return FALSE;
+        }
+      }
+    }
+
+    return $schema;
+  }
+}
diff --git a/classes/helpers/CinisisDisplayHelper.php b/classes/helpers/CinisisDisplayHelper.php
index 325595d..48db6ea 100644
--- a/classes/helpers/CinisisDisplayHelper.php
+++ b/classes/helpers/CinisisDisplayHelper.php
@@ -188,7 +188,7 @@ class CinisisDisplayHelper {
   /**
    * Draws a line break element.
    */
-  static function h3($text) {
+  static function br($text) {
     echo "
"; } diff --git a/index.php b/index.php index 7aebd45..aff50a5 100644 --- a/index.php +++ b/index.php @@ -11,7 +11,10 @@ require_once 'contrib/spyc/spyc.php'; // Autoloader. function cinisis_autoload($class) { - if (strstr($class, 'Iterator')) { + if (strstr($class, 'Db')) { + require_once 'classes/backends/'. $class .'.php'; + } + elseif (strstr($class, 'Iterator')) { require_once 'classes/iterators/'. $class .'.php'; } elseif (strstr($class, 'Helper')) { diff --git a/samples/read.php b/samples/read.php index 5a5be04..f0d2ba6 100644 --- a/samples/read.php +++ b/samples/read.php @@ -23,7 +23,7 @@ $configs = array( foreach ($configs as $config) { // Get a db instance. - $isis = new CinisisDb($config); + $isis = new Cinisis($config); // Test connection. if ($isis->db) { diff --git a/samples/test.php b/samples/test.php index 2b1fe5c..184df7c 100644 --- a/samples/test.php +++ b/samples/test.php @@ -10,7 +10,7 @@ require_once '../index.php'; $display = new CinisisDisplayHelper('Isis Test'); // Get a db instance. -$isis = new CinisisDb(); +$isis = new Cinisis(); // Test connection. if ($isis->db) { -- cgit v1.2.3