-
David Maus authored
* src/HAB/Pica/Record/Field.php: Declare $_subfields property. ...after all this years... ;)
David Maus authored* src/HAB/Pica/Record/Field.php: Declare $_subfields property. ...after all this years... ;)
Field.php 10.13 KiB
<?php
/**
* Pica+ Field.
*
* This file is part of PicaRecord.
*
* PicaRecord is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PicaRecord is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PicaRecord. If not, see <http://www.gnu.org/licenses/>.
*
* @package PicaRecord
* @author David Maus <maus@hab.de>
* @copyright Copyright (c) 2012-2016 by Herzog August Bibliothek Wolfenbüttel
* @license http://www.gnu.org/licenses/gpl.html GNU General Public License v3
*/
namespace HAB\Pica\Record;
use InvalidArgumentException;
class Field
{
/**
* Regular expression matching a valid Pica+ field tag.
*
* @var string
*/
const TAG_RE = '|^[012][0-9]{2}[A-Z@]$|D';
/**
* Return TRUE if argument is a valid field tag.
*
* @param mixed $arg Variable to check
* @return boolean TRUE if argument is a valid field tag
*/
public static function isValidFieldTag ($arg)
{
return (bool)preg_match(self::TAG_RE, $arg);
}
/**
* Return TRUE if argument is a valid field occurrence.
*
* Argument is casted to int iff it is either null or a numeric string.
*
* @param mixed $arg Variable to check
* @return boolean TRUE if argument is a valid field occurrence
*/
public static function isValidFieldOccurrence ($arg)
{
if ($arg === null || ctype_digit($arg)) {
$arg = (int)$arg;
}
return is_int($arg) && $arg >= 0 && $arg < 100;
}
/**
* Return predicate that matches a field shorthand against a regular
* expression.
*
* @param string $reBody Body of regular expression
* @return callback Predicate
*/
public static function match ($reBody)
{
if (strpos($reBody, '#') !== false) {
$reBody = str_replace('#', '\#', $reBody);
}
$regexp = "#{$reBody}#D";
return function (Field $field) use ($regexp) {
return (bool)preg_match($regexp, $field->getShorthand());
};
}
/**
* Return a new field based on its array representation.
*
* @throws InvalidArgumentException Missing `tag', `occurrene', or `subfields' index
* @param array $field Array representation of a field
* @return \HAB\Pica\Record\Field A shiny new field
*/
public static function factory (array $field)
{
foreach (array('tag', 'occurrence', 'subfields') as $index) {
if (!array_key_exists($index, $field)) {
throw new InvalidArgumentException("Missing '{$index}' index in field array");
}
}
return new Field($field['tag'],
$field['occurrence'],
array_map(array('HAB\Pica\Record\Subfield', 'factory'), $field['subfields']));
}
///
/**
* The field tag.
*
* @var string
*/
protected $_tag;
/**
* The field level.
*
* @var integer
*/
protected $_level;
/**
* The field occurrence.
*
* @var integer
*/
protected $_occurrence;
/**
* The field shorthand.
*
* @var string
*/
protected $_shorthand;
/**
* Subfields.
*
* @var array
*/
protected $_subfields;
/**
* Constructor.
*
* @throws InvalidArgumentException Invalid field tag or occurrence
* @param string $tag Field tag
* @param integer $occurrence Field occurrence
* @param array $subfields Initial set of subfields
* @return void
*/
public function __construct ($tag, $occurrence, array $subfields = array())
{
if (!self::isValidFieldTag($tag)) {
throw new InvalidArgumentException("Invalid field tag: $tag");
}
if (!self::isValidFieldOccurrence($occurrence)) {
throw new InvalidArgumentException("Invalid field occurrence: $occurrence");
}
$this->_tag = $tag;
$this->_occurrence = (int)$occurrence;
$this->_shorthand = sprintf('%4s/%02d', $tag, $occurrence);
$this->_level = (int)$tag[0];
$this->setSubfields($subfields);
}
/**
* Set the field subfields.
*
* Replaces the subfield list with subfields in argument.
*
* @param array $subfields Subfields
* @return void
*/
public function setSubfields (array $subfields)
{
$this->_subfields = array();
foreach ($subfields as $subfield) {
$this->addSubfield($subfield);
}
}
/**
* Add a subfield to the end of the subfield list.
*
* @throws InvalidArgumentException Subfield already present in subfield list
* @param \HAB\Pica\Record\Subfield $subfield Subfield to add
* @return void
*/
public function addSubfield (\HAB\Pica\Record\Subfield $subfield)
{
if (in_array($subfield, $this->getSubfields(), true)) {
throw new InvalidArgumentException("Cannot add subfield: Subfield already part of the subfield list");
}
$this->_subfields []= $subfield;
}
/**
* Remove a subfield.
*
* @throws InvalidArgumentException Subfield is not part of the subfield list
* @param \HAB\Pica\Record\Subfield $subfield Subfield to delete
* @return void
*/
public function removeSubfield (\HAB\Pica\Record\Subfield $subfield)
{
$index = array_search($subfield, $this->_subfields, true);
if ($index === false) {
throw new InvalidArgumentException("Cannot remove subfield: Subfield not part of the subfield list");
}
unset($this->_subfields[$index]);
}
/**
* Return the field's subfields.
*
* Returns all subfields when called with no arguments.
*
* Otherwise the returned array is constructed as follows:
*
* Each argument is interpreted as a subfield code. The nth element of the
* returned array maps to the nth argument in the function call and contains
* NULL if the field does not have a subfield with the selected code, or the
* subfield if it exists. In order to retrieve multiple subfields with an
* identical code you repeat the subfield code in the argument list.
*
* @return array Subfields
*/
public function getSubfields ()
{
if (func_num_args() === 0) {
return $this->_subfields;
} else {
$selected = array();
$codes = array();
$subfields = $this->getSubfields();
array_walk($subfields, function ($value, $index) use (&$codes) { $codes[$index] = $value->getCode(); });
foreach (func_get_args() as $arg) {
$index = array_search($arg, $codes, true);
if ($index === false) {
$selected []= null;
} else {
$selected []= $subfields[$index];
unset($codes[$index]);
}
}
return $selected;
}
}
/**
* Return the nth occurrence of a subfield with specified code.
*
* @param string $code Subfield code
* @param integer $n Zero-based subfield index
* @return \HAB\Pica\record\Subfield|null The requested subfield or NULL if
* none exists
*/
public function getNthSubfield ($code, $n)
{
$count = 0;
foreach ($this->getSubfields() as $subfield) {
if ($subfield->getCode() == $code) {
if ($count == $n) {
return $subfield;
}
$count++;
}
}
return null;
}
/**
* Return true if a subfield with given code exists.
*
* If optional argument $n return true if the field has subfield
* at the specified position.
*
* @param string $code Subfield code
* @param integer $n Optional zero-based subfield index
* @return boolean
*/
public function hasSubfield ($code, $n = 0)
{
return (boolean)$this->getNthSubfield($code, $n);
}
/**
* Return all subfields with the specified code.
*
* @param string $code Subfield code
* @return array
*/
public function getSubfieldsWithCode ($code)
{
return array_filter($this->_subfields, function (Subfield $s) use ($code) { return $s->getCode() == $code; });
}
/**
* Return the field tag.
*
* @return string Field tag
*/
public function getTag ()
{
return $this->_tag;
}
/**
* Return the field occurrence.
*
* @return integer Field occurrence
*/
public function getOccurrence ()
{
return $this->_occurrence;
}
/**
* Return the field level.
*
* @return integer Field level
*/
public function getLevel () {
return $this->_level;
}
/**
* Return the field shorthand.
*
* @return string Field shorthand
*/
public function getShorthand ()
{
return $this->_shorthand;
}
/**
* Return TRUE if the field is empty.
*
* A field is empty if it contains no subfields.
*
* @return boolean TRUE if the field is empty
*/
public function isEmpty ()
{
return empty($this->_subfields);
}
/**
* Finalize the clone() operation.
*
* @return void
*/
public function __clone ()
{
$this->_subfields = Helper::mapClone($this->_subfields);
}
/**
* Return a printable representation of the field.
*
* The printable representation of a field is its shorthand.
*
* @return string Printable representation of the field
*/
public function __toString ()
{
return $this->getShorthand();
}
}