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 --- 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 +- 16 files changed, 1237 insertions(+), 1096 deletions(-) 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 (limited to 'classes') 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 "
"; } -- cgit v1.2.3