Source for file ISBN.php
Documentation is available at ISBN.php
* Handle, Convert and Validate ISBN Numbers
* LICENSE: LGPL (In cases LGPL is not appropriate, it is licensed under GPL)
* Package to handle, convert and validate ISBN numbers. It includes:
* - ISBN specifics: EAN/PrefixArrayAccess (integer)
* - ISBN specifics: Group/Registration Group [2001: Group identifier] (integer)
* - ISBN specifics: GroupTitle/Registration Group Title (string)
* - ISBN specifics: Publisher/Registrant [2001: Publisher identifier] (string)
* - ISBN specifics: Title/Publication [2001: Title identifier] (string)
* - ISBN specifics: Checkdigit (string)
* - ISBN specifics: 'ISBNBody' (string)
* - ISBN specifics: 'ISBNSubbody' (string)
* - ISBN Version handling
* - Syntactical Validation plus Validation based on real ISBN Data
* - ISBN-10 (ISO 2108) checksum calculation
* - Validation (ISBN-10 and ISBN-13-978)
* - Conversion to ISBN-13-978
* - ISBN-13-978 (2005 Handbook, ISO pending; ISBN-13)
* - ISBN-13 checksum calculation (EAN)
* Based on standards published by international ISBN Agency
* http://www.isbn-international.org/
* @author Tom Klingenberg <tkli-php@lastflood.net>
* @copyright 2006-2007 Tom Klingenberg
* @license LGPL http://www.gnu.org/licenses/lgpl.txt
* @version v 0.1.6 CVS: <cvs_id>
* @link http://isbn.lastflood.com online docs
* @todo License for .js file or remove it
* ISBN Versions supported
define('ISBN_VERSION_NONE', false);
* VERSION_UNKNOWN is by the caller only, this shall never
* be a value returned by a public function or getter
define('ISBN_VERSION_UNKNOWN', 0);
define('ISBN_VERSION_ISBN_10', 10);
define('ISBN_VERSION_ISBN_13', 13978);
define('ISBN_VERSION_ISBN_13_978', ISBN_VERSION_ISBN_13);
define('ISBN_VERSION_ISBN_13_979', 13979); /* reserved */
* Default ISBN Version for class input / usage
define('ISBN_DEFAULT_INPUTVERSION', ISBN_VERSION_UNKNOWN);
* Default ISBN Seperator string
define('ISBN_DEFAULT_COSMETIC_SEPERATOR', '-');
* ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX
* When printed, the ISBN is always preceded by the letters "ISBN".
* Note: In countries where the Latin alphabet is not used, an abbreviation
* in the characters of the local script may be used in addition to the
* This can be defined as a default value wihtin this constant.
define('ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX', '');
require_once 'PEAR/Exception.php';
* @author Tom Klingenberg <tkli-php@lastflood.net>
* @copyright 2006-2007 Tom Klingenberg
* @license LGPL http://www.gnu.org/licenses/lgpl.txt
* @link http://isbn.lastflood.com/
* @since Class available since Release 0.1.3
* Class to Handle, Convert and Validate ISBN Numbers
* @author Tom Klingenberg <tkli-php@lastflood.net>
* @copyright 2006-2007 Tom Klingenberg
* @license LGPL http://www.gnu.org/licenses/lgpl.txt
* @link http://isbn.lastflood.com/
* @since Class available since Release 0.0.0
* @var string ISBN Registration Group
private $isbn_group = '';
* @var string ISBN Publisher
private $isbn_publisher = '';
private $isbn_title = '';
* @var mixed ISBN number version
private $ver = ISBN_VERSION_NONE;
* @var array ISBN Groups Data acting as cache
* @see _getISBN10Groups()
private static $varISBN10Groups = array();
* @param array $isbn String of ISBN Value to use
* @param mixed $ver Optional Version Constant
* @throws ISBN_Exception in case it fails
function __construct($isbn = '', $ver = ISBN_DEFAULT_INPUTVERSION)
/* validate & handle optional isbn parameter */
/* validate version parameter */
if (self::_isbnVersionIs($ver) == false) {
'ISBN Version parameter is not an ISBN Version'
/* ISBN has been passed, check the version now:
* if it is unknown, try to dertine it, if this fails
$verguess = self::_isbnVersionGuess($isbn);
if (self::_isbnVersionIsValid($verguess)) {
/* throw new ISBN_Exception(
*'ISBN Version couldn\'t determined.');
/* handle a complete invalid ISBN of which a version could
/* the isbn is invalid and not set, sothat this
* ISBN object will be set to a blank value. */
// {{{ _extractCheckdigit()
* extract Checkdigit of an ISBN-Number
* @param string $isbnn normalized ISBN string
* @return string|falseISBN-Body or false if failed
private static function _extractCheckdigit($isbnn)
$checkdigit = substr($isbnn, - 1);
return (string) $checkdigit;
// {{{ _extractEANPrefix()
* extracts EAN-Prefix of a normalized isbn string
* @param string $isbnn normalized isbn string
* @return string|falsePrefix or false if failed
private static function _extractEANPrefix($isbnn)
$prefix = substr($isbnn, 0, 3);
* extract Registration Group of an ISBN-Body
* @param string $isbnbody ISBN-Body
* @return integer|false Registration Group or false if failed
private static function _extractGroup($isbnbody)
$r = self::_isbnBodyParts($isbnbody, $group, $subbody);
// {{{ _extractISBNBody()
* extract ISBN-Body of an ISBN-Number
* @param string $isbnn normalized ISBN string
* @return string|falseISBN-Body or false if failed
private static function _extractISBNBody($isbnn)
$isbnn = (string) $isbnn;
$body = substr($isbnn, 0, - 1);
$body = substr($isbnn, 3, - 1);
* Get the 2 Parts of the ISBN-Body (ISBN-10/ISBN-13-978)
* @param string $isbnbody ISBN-Body
* @param string &$registrationgroup Registration Group
* @param string &$isbnsubbody ISBN-Subbody
* @return boolean False if failed, True on success
private static function _isbnBodyParts($isbnbody,
/* validate input (should not be needed, @access private) */
/* extract registraion group
* boundaries see p.13 2005 handbook
$boundaries[] = array( 0, 59999, 1);
$boundaries[] = array(60000, 60099, 3); // Iran 2006-12-05
$boundaries[] = array(60100, 69999, 0);
$boundaries[] = array(70000, 79999, 1);
$boundaries[] = array(80000, 94999, 2);
$boundaries[] = array(95000, 98999, 3);
$boundaries[] = array(99000, 99899, 4);
$boundaries[] = array(99900, 99999, 5);
$segment = substr($isbnbody, 0, 5);
$segmentvalue = intval($segment);
/* test segment value against boundaries */
foreach ($boundaries as $boundary) {
if ($segmentvalue >= $boundary[0] && $segmentvalue <= $boundary[1]) {
/* $r is 0 when the boundary is not defined */
$registrationgroup = substr($isbnbody, 0, $r);
$isbnsubbody = substr($isbnbody, $r);
// {{{ _isbnSubbodyParts()
* Get the 2 Parts of the ISBN-Subbody (ISBN-10/ISBN-13)
* @param string $isbnsubbody ISBN-Subbody
* @param integer $groupid Registrationgroup
* @param string &$registrant Registrant
* @param string &$publication Publication
* @return boolean False if failed, true on success
private static function _isbnSubbodyParts($isbnsubbody,
/* validate input (should not be needed, @access private) */
$r = settype($isbnsubbody, 'string');
if ($groupid < 0 || $groupid > 99999) {
/* extract registrant based on group and registrant range
* parse this specific group format:
* 'English speaking area',
* '00-09;10-19;200-699;7000-8499;85000-89999;' .
* '900000-949999;9500000-9999999'
$group = self::_getISBN10Group($groupid);
$len = self::_getRegistrantLength($isbnsubbody, $group[1]);
$registrant = substr($isbnsubbody, 0, $len);
$publication = substr($isbnsubbody, $len);
// {{{ _getRegistrantLength()
* Return Length of Registrant part within an ISBNSubbody in a specific
* grouprange in this specific format:
* '00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999'
* Info: This function is compatible with Groupranges formatted in the
* .js file and might become obsolete if new formats are more fitting.
* @param string $isbnsubbody ISBN-Subbody
* @param string $grouprange Grouprange in the Format '#a1-#z1;#a2-z2[...]'
* @return boolean|int False if failed or Length (in chars) of Registrant
private static function _getRegistrantLength($isbnsubbody, $grouprange)
$r = settype($grouprange, 'string');
if (strlen($grouprange) < 3) {
$ranges = explode(';', $grouprange);
foreach ($ranges as $range) {
if (count($fromto) !== 2) {
* from and to need to be in the same class,
* having the same length.
* registrant can not be bigger or same then the
* whole subbody, at least there is one digit for
if ($l != strlen($fromto[1])) {
/* check that from/to values are in order */
if (strcmp($fromto[0], $fromto[1]) >= 0) {
/* compare and fire if matched */
$comparec = substr($isbnsubbody, 0, $l);
if (strcmp($fromto[0], $comparec) < 1 &&
strcmp($fromto[1], $comparec) > - 1) {
* Get ISBN-10 Registration Group Data by its numeric ID
* @param integer $id Registration Group Identifier
* @return mixed array: group array
* boolean: False if failed
private static function _getISBN10Group($id)
$groups = self::_getISBN10Groups();
if (isset ($groups[$id]) === false) {
// {{{ _getISBN10Groups()
* Get all ISBN-10 Registration Groups
* @return array groups array
* Info: This function connects outer world data into this class logic
* which can be generated with the supplied tools.
* A user should not alter the array data. This data should be altered
* together with the international ISBN Agency only.
private static function _getISBN10Groups()
/* check if data has been already loaded */
if (sizeof(self::$varISBN10Groups) > 0 ) {
return self::$varISBN10Groups;
$t = file_get_contents('data/groups.csv', true, null);
'Unable to read ISBN Groups Data file.'
/* parse external data */
if (isset ($groups[$index])) {
'ISBN Groups Data is invalid, Group ' .
$index . 'is a duplicate.'
/* edit+ mature: sanitize external
$groups[$index] = array($tlp[1],$tlp[2]);
'ISBN Groups Data is malformed on line #' . $line .
|