<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
// +----------------------------------------------------------------------+
// | Copyright (c) 2006 Tom Klingenberg                                   |
// +----------------------------------------------------------------------+
// | license info GPL/LGPL                                                |
// +----------------------------------------------------------------------+
// | Author: Tom Klingenberg <tklingenberg@lastflood.com>                 |
// +----------------------------------------------------------------------+
//
/**
 * ISBN
 *
 * Handle, Convert and Validate ISBN Numbers
 *
 * PHP version 5
 *
 * LICENSE: GPL/LGPL
 *
 * Package to handle, convert and validate ISBN numbers. It includes:
 *     - ISBN specifics:
 *        - EAN/Prefix (integer)
 *        - 'ISBNBody' (string)
 *        - Group/Registration Group [2001: Group identifier] (integer)
 *        (- GroupTitle/Registration Group Title (string))
 *        - 'ISBNSubbody'' (string)
 *        - Publisher/Registrant [2001: Publisher identifier] (string)
 *        - Title/Publication [2001: Title identifier] (string)
 *        - Checkdigit (string)
 *        - Version (integer) value as of a CONST
 *     - Syntactical Validation plus Validation based on real ISBN Data
 *   - ISBN-10 (ISO 2108)
 *      - checksum calculation (ISBN-10)
 *        - validation
 *      - conversion to ISBN-13-978
 *   - ISBN-13-978 (2005 Handbook, ISO pending; ISBN-13)
 *      - checksum calculation (EAN)
 *        - validation
 *
 * Based on standards published by international ISBN Agency
 * http://www.isbn-international.org/
 *
 *
 * @category    UNKNOWN
 * @package        ISBN
 * @author        Tom Klingenberg <tklingenberg@lastflood.com>
 * @copyright    2006 Tom Klingenberg
 * @license        GPL/LGPL
 * @version        0.1.0
 */

// {{{ constants
/**
 * 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 Additonal ISBN (formatting) Values
 */
define("ISBN_DEFAULT_INPUTVERSION"ISBN_VERSION_UNKNOWN);
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
 * Latin letters "ISBN".
 * This can be defined as a default value wihtin this constant.
 */
define("ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX""");
// }}}

// {{{ ISBN
/**
 * class ISBN
 *
 * @since      Class available since Release 0.0.0
 */
class ISBN
{
    
/**
     * @var string ISBN Registration Group
     */
    
private $isbn_group "";
    
/**
     * @var string ISBN Publisher
     */
    
private $isbn_publisher "";
    
/**
     * @var string ISBN Title
     */
    
private $isbn_title "";

    
/**
     * @var mixed ISBN number version
     */
    
private $ver ISBN_VERSION_NONE;

    
// {{{ __construct
    /**
     * Constructor
     *
     * @param array    $isbn    String of ISBN Value to use
     * @param mixed    $ver    Optional Version Constant
     *
     * @access public
     */
    
function __construct($isbn ""$ver ISBN_DEFAULT_INPUTVERSION) {
        
/* validate & handle optional isbn parameter */
        
if (is_string($isbn) == false ) {
            throw new 
Exception("ISBN parameter must be a string"4);
        }
        if (
strlen($isbn) == 0) {
            
$this->setISBN($isbn);
            return;
        }

        
/* validate version parameter */
        
if (self::_ISBNVersionIs($ver) == false) {
            throw new 
Exception("ISBN Version parameter is not an ISBN Version"3);
        }

        
/* ISBN has been passed, check the version now:
         *     if it is unknown, try to dertine it, if this fails
         *     throw an exception
         */
        
if ($ver == ISBN_VERSION_UNKNOWN) {
            
$verguess self::_ISBNVersionGuess($isbn);
            if (
self::_ISBNVersionIsValid($verguess)) {
                
$ver $verguess;
            } else {
                
// throw new Exception("Unknown ISBN Version could not be guessed.", 5);
                
$ver ISBN_VERSION_NONE;
            }
        }
        
/* version determined */
        
$this->ver $ver;

        
/* handle a complete invalid ISBN of which a version could
         * not be determined. */
        
if ($ver === ISBN_VERSION_NONE) {
            
$this->setISBN("");
            return;
        }

        try {
            
$this->setISBN($isbn);
        } catch(
Exception $e) {
            
/* the isbn is invalid and not set, sothat this
             * ISBN object will be set to a blank value. */
            
$this->setISBN("");
        }

    }
    
// }}}

    // {{{ __extractCheckdigit()
    /**
     *
     * extract Checkdigit of an ISBN-Number
     *
     * @param string    $isbnn    normalized ISBN string
     *
     * @return boolean    False if failed
     * @return string    string containing the ISBN-Body
     *
     */
    
private static function __extractCheckdigit($isbnn) {
        
$checkdigit false;
        
$checkdigit substr($isbnn,-1);
        return (string) 
$checkdigit;
    }
    
// }}}

    // {{{ __extractEANPrefix()
    /**
     * extracts EAN-Prefix of a normalized isbn string
     *
     *
     * @param string    $isbnn    normalized isbn string
     *
     * @return boolean    False if failed
     * @return string    Prefix
     */
    
private static function __extractEANPrefix($isbnn)
    {
        
$r settype($isbnn"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($isbnn) < 3) {
            return 
false;
        }
        
$prefix substr($isbnn03);
        return 
$prefix;
    }
    
// }}}

    // {{{ __extractGroup()
    /**
     * extract Registration Group of an ISBN-Body
     *
     * @param string    $isbnbody    ISBN-Body
     *
     * @return boolean    false if failed
     * @return integer    Value of Registration Group
     */
    
private static function __extractGroup($isbnbody) {
        
$group '';
        
$subbody '';
        
$r self::__ISBNBodyParts($isbnbody$group$subbody);
        if (
$r === false) {
            return 
false;
        }
        return 
$group;
    }
    
// }}}

    // {{{ __extractISBNBody()
    /**
     * extract ISBN-Body of an ISBN-Number
     *
     * @param string    $isbnn    normalized ISBN string
     *
     * @return boolean    False if failed
     * @return string    string containing the ISBN-Body
     */
    
private static function __extractISBNBody($isbnn) {
        
/* extract */
        
$body false;
        
$isbnn = (string) $isbnn;
        
$l strlen($isbnn);
        if (
$l == 10) {
            
$body =  substr($isbnn0, -1);
        } elseif (
$l == 13) {
            
$body =  substr($isbnn3, -1);
        } else {
            return 
false;
        }
        
/* verify */
        
$r settype($body"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($body) != 9) {
            return 
false;
        }
        if (
ctype_digit($body) === false) {
            return 
false;
        }
        return 
$body;
    }
    
// }}}

    // {{{ __ISBNBodyParts()
    /**
     * 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
     *
     * @access private
     */
    
private static function __ISBNBodyParts($isbnbody, &$registrationgroup, &$isbnsubbody) {
        
/* validate input (should not be needed, @access private) */
        
$r settype($isbnbody"string");
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($isbnbody) != 9) {
            return 
false;
        }
        if (
ctype_digit($isbnbody) === false) {
            return 
false;
        }
        
/* extract registraion group
         * boundaries see p.13 2005 handbook
         */
        
$boundaries = array();
        
$boundaries[] = array(    0599991);
        
$boundaries[] = array(60000600993); // Iran 2006-12-05
        
$boundaries[] = array(60100699990);
        
$boundaries[] = array(70000799991);
        
$boundaries[] = array(80000949992);
        
$boundaries[] = array(95000989993);
        
$boundaries[] = array(99000998994);
        
$boundaries[] = array(99900999995);
        
/* segment value */
        
$segment substr($isbnbody05);
        
$segmentvalue intval($segment);
        
/* test segment value against boundaries */
        
$r false;
        foreach (
$boundaries as $boundary) {
            if (
$segmentvalue >= $boundary[0] && $segmentvalue <= $boundary[1]) {
                
$r $boundary[2];
            }
        }
        if (
$r === false) {
            return 
false;
        }
        
/* $r is 0 when the boundary is not defined */
        
if ($r === 0) {
            return 
false;
        }
        
$registrationgroup substr($isbnbody0$r);
        
$isbnsubbody substr($isbnbody$r);
        return 
true;
    }
    
// }}}

    // {{{ __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
     *
     * @access private
     */
    
private static function __ISBNSubbodyParts($isbnsubbody$groupid, &$registrant, &$publication) {
        
/* validate input (should not be needed, @access private) */
        
$r settype($isbnsubbody"string");
        if (
$r === false) {
            return 
false;
        }
        
$l strlen($isbnsubbody);
        if ( 
$l || $l 8) {
            return 
false;
        }
        if (
ctype_digit($isbnsubbody) === false) {
            return 
false;
        }
        
$r settype($groupid'integer');
        if (
$r === false) {
            return 
false;
        }
        if (
$groupid || $groupid 99999) {
            return 
false;
        }
        
/* extract registrant based on group and registrant range
         * parse this specific group format: array("English speaking area",
         *   "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
         */
        
$group self::__getISBN10Group($groupid);
        if (
$group === false) {
            return 
false;
        }
        
$len self::__getRegistrantLength($isbnsubbody$group[1]);
        if (
$len === false) {
            return 
false;
        }
        
$registrant substr($isbnsubbody0$len);
        
$publication substr($isbnsubbody$len);
        return 
true;
    }
    
// }}}

    // {{{ __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
     * @param string    $grouprange        Grouprange in the Format "#a1-#z1;#a2-z2[...]"
     *
     * @return boolean    False if failed
     * @return int        Length (in chars) of Registrant
     *
     * @access private
     */
    
private static function __getRegistrantLength($isbnsubbody$grouprange) {
        
$r settype($grouprange'string');
        if (
$r === false) {
            return 
false;
        }
        if (
strlen($grouprange) < 3) {
            return 
false;
        }
        
$sl strlen($isbnsubbody);
        
$ranges explode(";"$grouprange);
        foreach(
$ranges as $range) {
            
$range trim($range);
            
$fromto explode("-"$range);
            if (
count($fromto) !== 2) {
                return 
false;
            }
            
/* validation:
             * 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
             * the publication.
             */

            
$l strlen($fromto[0]);
            if (
$l != strlen($fromto[1])) {
                return 
false;
            }
            if (
$l >= $sl) {
                return 
false;
            }

            
/* check that from/to values are in order */
            
if (strcmp($fromto[0], $fromto[1]) >= 0) {
                return 
false;
            }

            
/* compare and fire if matched */
            
$compare intval(substr($isbnsubbody0$l));

            if (
strcmp($fromto[0], $compare) < && strcmp($fromto[1], $compare) > -1) {
                return 
$l;
            }
        }
        return 
false;
    }
    
// }}}

    // {{{ __getISBN10Group()
    /**
     * 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) {
        
$r settype($id"integer");
        if (
$r === false) {
            return 
false;
        }
        
$groups self::__getISBN10Groups();
        if (
$groups === false) {
            return 
false;
        }
        if (isset(
$groups[$id]) === false) {
            return 
false;
        }
        
$group $groups[$id];
        return 
$group;
    }
    
// }}}

    // {{{ __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() {
        
$groups = array();
        
/* ISBN Code-Generator - Ranges */
        
$groups[0]     = array("English speaking area"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[1]     = array("English speaking area"    "00-09;100-399;4000-5499;55000-86979;869800-998999");
        
$groups[2]     = array("French speaking area"    "00-09;10-19;200-349;35000-39999;400-699;7000-8399;84000-89999;900000-949999;9500000-9999999");
        
$groups[3]     = array("German speaking area"    "00-02;030-033;0340-0369;03700-03999;04-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[4]     = array("Japan"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999");
        
$groups[5]     = array("Russian Federation"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-909999;91000-91999;9200-9299;93000-94999;9500-9799;98000-98999;9900000-9909999;9910-9999");
        
$groups[600]   = array("Iran"    "00-09;100-499;5000-8999;90000-99999");
        
$groups[7]     = array("China, People's Republic"    "00-09;100-499;5000-7999;80000-89999;900000-999999");
        
$groups[80]    = array("Czech Republic; Slovakia"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-999999");
        
$groups[81]    = array("India"    "00-09;10-19;200-699;7000-8499;85000-89999;900000-999999");
        
$groups[82]    = array("Norway"    "00-09;10-19;200-699;7000-8999;90000-98999;990000-999999");
        
$groups[83]    = array("Poland"    "00-09;10-19;200-599;60000-69999;7000-8499;85000-89999;900000-999999");
        
$groups[84]    = array("Spain"    "00-09;10-19;200-699;7000-8499;85000-89999;9000-9199;920000-923999;92400-92999;930000-949999;95000-96999;9700-9999");
        
$groups[85]    = array("Brazil"    "00-09;10-19;200-599;60000-69999;7000-8499;85000-89999;900000-979999;98000-99999");
        
$groups[86]    = array("Serbia and Montenegro"    "00-09;10-29;300-599;6000-7999;80000-89999;900000-999999");
        
$groups[87]    = array("Denmark"    "00-09;10-29;400-649;7000-7999;85000-94999;970000-999999");
        
$groups[88]    = array("Italian speaking area"    "00-09;10-19;200-599;6000-8499;85000-89999;900000-949999;95000-99999");
        
$groups[89]    = array("Korea"    "00-09;10-24;250-549;5500-8499;85000-94999;950000-999999");
        
$groups[90]    = array("Netherlands, Belgium (Flemish)"    "00-09;10-19;200-499;5000-6999;70000-79999;800000-849999;8500-8999;900000-909999;940000-949999");
        
$groups[91]    = array("Sweden"    "0-1;20-49;500-649;7000-7999;85000-94999;970000-999999");
        
$groups[92]    = array("International Publishers (Unesco, EU), European Community Organizations"    "0-5;60-79;800-899;9000-9499;95000-98999;990000-999999");
        
$groups[93]    = array("India - no ranges fixed yet"    "");
        
$groups[950]   = array("Argentina"    "00-09;10-49;500-899;9000-9899;99000-99999");
        
$groups[951]   = array("Finland"    "0-1;20-54;550-889;8900-9499;95000-99999");
        
$groups[952]   = array("Finland"    "00-19;200-499;5000-5999;60-65;6600-6699;67000-69999;7000-7999;80-94;9500-9899;99000-99999");
        
$groups[953]   = array("Croatia"    "0-0;10-14;150-599;6000-9499;95000-99999");
        
$groups[954]   = array("Bulgaria"    "00-09;10-29;300-799;8000-8999;90000-92999;9300-9999");
        
$groups[955]   = array("Sri Lanka"    "0-0;1000-1999;20-54;550-799;8000-9499;95000-99999");
        
$groups[956]   = array("Chile"    "00-09;10-19;200-699;7000-9999");
        
$groups[957]   = array("Taiwan, China"    "00-02;0300-0499;05-19;2000-2099;21-27;28000-30999;31-43;440-819;8200-9699;97000-99999");
        
$groups[958]   = array("Colombia"    "00-09;10-59;600-799;8000-9499;95000-99999");
        
$groups[959]   = array("Cuba"    "00-09;10-19;200-699;7000-8499");
        
$groups[960]   = array("Greece"    "00-09;10-19;200-659;6600-6899;690-699;7000-8499;85000-99999");
        
$groups[961]   = array("Slovenia"    "00-09;10-19;200-599;6000-8999;90000-94999");
        
$groups[962]   = array("Hong Kong"    "00-09;10-19;200-699;7000-8499;85000-86999;8700-8999;900-999");
        
$groups[963]   = array("Hungary"    "00-09;10-19;200-699;7000-8499;85000-89999;9000-9999");
        
$groups[964]   = array("Iran"    "00-09;10-14;150-249;2500-2999;300-549;5500-8999;90000-96999;970-989;9900-9999");
        
$groups[965]   = array("Israel"    "00-09;10-19;200-599;7000-7999;90000-99999");
        
$groups[966]   = array("Ukraine"    "00-19;2000-2999;300-699;7000-8999;90000-99999");
        
$groups[967]   = array("Malaysia"    "0-5;60-89;900-989;9900-9989;99900-99999");
        
$groups[968]   = array("Mexico"    "01-09;10-39;400-499;5000-7999;800-899");
        
$groups[969]   = array("Pakistan"    "0-1;20-39;400-799;8000-9999");
        
$groups[970]   = array("Mexico"    "01-09;10-59;600-899;9000-9099;91000-96999;9700-9999");
        
$groups[971]   = array("Philippines?"    "000-019;02-02;0300-0599;06-09;10-49;500-849;8500-9099;91000-99999");
        
$groups[972]   = array("Portugal"    "0-1;20-54;550-799;8000-9499;95000-99999");
        
$groups[973]   = array("Romania"    "0-0;100-169;1700-1999;20-54;550-759;7600-8499;85000-88999;8900-9499;95000-99999");
        
$groups[974]   = array("Thailand"    "00-09;10-19;200-699;7000-8499;85000-89999;90000-94999;9500-9999");
        
$groups[975]   = array("Turkey"    "00000-00999;01-09;10-24;250-599;6000-9199;92000-98999;990-999");
        
$groups[976]   = array("Caribbean Community"    "0-3;40-59;600-799;8000-9499;95000-99999");
        
$groups[977]   = array("Egypr"    "00-09;10-19;200-499;5000-6999;700-999");
        
$groups[978]   = array("Nigeria"    "000-199;2000-2999;30000-79999;8000-8999;900-999");
        
$groups[979]   = array("Indonesia"    "000-099;1000-1499;15000-19999;20-29;3000-3999;400-799;8000-9499;95000-99999");
        
$groups[980]   = array("Venezuela"    "00-09;10-19;200-599;6000-9999");
        
$groups[981]   = array("Singapore"    "200-299;3000-9999;00-09");
        
$groups[982]   = array("South Pacific"    "00-09;100-699;70-89;9000-9999");
        
$groups[983]   = array("Malaysia"    "00-01;020-199;2000-3999;40000-49999;50-79;800-899;9000-9899;99000-99999");
        
$groups[984]   = array("Bangladesh"    "00-09;10-39;400-799;8000-8999;90000-99999");
        
$groups[985]   = array("Belarus"    "00-09;10-39;400-599;6000-8999;90000-99999");
        
$groups[986]   = array("Taiwan, China"    "120-559;5600-7999;80000-99999;00-09;10-11");
        
$groups[987]   = array("Argentina"    "00-09;1000-1999;20000-29999;30-49;500-899;9000-9499;95000-99999");
        
$groups[988]   = array("Hongkong"    "00-09;10-19;200-799;8000-9699;97000-99999");
        
$groups[989]   = array("Portugal"    "0-1;20-54;550-799;8000-9499;95000-99999");
        
$groups[9940]  = array("Montenegro"    "0-1;20-49;500-899;9000-9999");
        
$groups[9941]  = array("Georgia"    "0-0;10-39;400-899;9000-9999");
        
$groups[9942]  = array("Ecuador"    "00-89;900-994;9950-9999");
        
$groups[9943]  = array("Uzbekistan"    "00-29;300-399;4000-9999");
        
$groups[9944]  = array("Turkey"    "0-2;300-499;5000-5999;60-89;900-999");
        
$groups[9945]  = array("Dominican Republic"    "00-09;10-39;400-849;8500-9999");
        
$groups[9946]  = array("Korea, P.D.R."    "0-1;20-39;400-899;9000-9999");
        
$groups[9947]  = array("Algeria"    "0-1;20-79;800-999");
        
$groups[9948]  = array("United Arab Emirates"    "00-09;10-39;400-849;8500-9999");
        
$groups[9949]  = array("Estonia"    "0-0;10-39;400-899;9000-9999");
        
$groups[9950]  = array("Palestine"    "00-29;300-840;8500-9999");
        
$groups[9951]  = array("Kosova"    "00-09;10-39;400-849;8500-9999");
        
$groups[9952]  = array("Azerbaijan"    "0-1;20-39;400-799;8000-9999");
        
$groups[9953]  = array("Lebanon"    "0-0;10-39;400-599;60-89;9000-9999");
        
$groups[9954]  = array("Morocco"    "0-1;20-39;400-799;8000-9999");
        
$groups[9955]  = array("Lithuania"    "00-09;10-39;400-929;9300-9999");
        
$groups[9956]  = array("Cameroon"    "0-0;10-39;400-899;9000-9999");
        
$groups[9957]  = array("Jordan"    "00-09;10-39;400-849;8500-9999");
        
$groups[9958]  = array("Bosnia and Herzegovina"    "0-0;10-49;500-899;9000-9999");
        
$groups[9959]  = array("Libya"    "0-1;20-79;800-949;9500-9999");
        
$groups[9960]  = array("Saudi Arabia"    "00-09;10-59;600-899;9000-9999");
        
$groups[9961]  = array("Algeria"    "0-2;30-69;700-949;9500-9999");
        
$groups[9962]  = array("Panama"    "00-09;10-54;5500-5599;56-59;600-849;8500-9999");
        
$groups[9963]  = array("Cyprus"    "0-2;30-54;550-749;7500-9999");
        
$groups[9964]  = array("Ghana"    "0-6;70-94;950-999");
        
$groups[9965]  = array("Kazakhstan"    "00-09;10-39;400-899;9000-9999");
        
$groups[9966]  = array("Kenya"    "00-09;10-69;7000-7499;750-959;9600-9999");
        
$groups[9967]  = array("Kyrgyzstan"    "00-09;10-39;400-899;9000-9999");
        
$groups[9968]  = array("Costa Rica"    "00-09;10-49;500-939;9400-9999");
        
$groups[9970]  = array("Uganda"    "00-09;10-39;400-899;9000-9999");
        
$groups[9971]  = array("Singapore"    "0-5;60-89;900-989;9900-9999");
        
$groups[9972]  = array("Peru"    "00-09;1;200-249;2500-2999;30-59;600-899;9000-9999");
        
$groups[9973]  = array("Tunisia"    "0-0;10-69;700-969;9700-9999");
        
$groups[9974]  = array("Uruguay"    "0-2;30-54;550-749;7500-9499;95-99");
        
$groups[9975]  = array("Moldova"    "0;100-399;4000-4499;45-89;900-949;9500-9999");
        
$groups[9976]  = array("Tanzania"    "0-5;60-89;900-989;9990-9999");
        
$groups[9977]  = array("Costa Rica"    "00-09;10-89;900-989;9900-9999");
        
$groups[9978]  = array("Ecuador"    "00-09;10-29;300-399;40-94;950-989;9900-9999");
        
$groups[9979]  = array("Iceland"    "0-4;50-75;760-899;9000-9999");
        
$groups[9980]  = array("Papua New Guinea"    "0-3;40-89;900-989;9900-9999");
        
$groups[9981]  = array("Morocco"    "00-09;100-159;1600-1999;20-79;800-949;9500-9999");
        
$groups[9982]  = array("Zambia"    "00-09;10-79;800-889;9900-9999");
        
$groups[9983]  = array("Gambia"    "80-94;950-989;9900-9999");
        
$groups[9984]  = array("Latvia"    "00-09;10-49;500-899;9000-9999");
        
$groups[9985]  = array("Estonia"    "0-4;50-79;800-899;9000-9999");
        
$groups[9986]  = array("Lithuania"    "00-09;10-39;400-899;9000-9399;940-969;97-99");
        
$groups[9987]  = array("Tanzania"    "00-09;10-39;400-879;8800-9999");
        
$groups[9988]  = array("Ghana"    "0-2;30-54;550-749;7500-9999");
        
$groups[9989]  = array("Macedonia"    "0-0;100-199;2000-2999;30-59;600-949;9500-9999");
        
$groups[99901] = array("Bahrain"    "00-09;10-49;500-799;80-99");
        
$groups[99902] = array("Gabon - no ranges fixed yet"    "");
        
$groups[99903] = array("Mauritius"    "0-1;20-89;900-999");
        
$groups[99904] = array("Netherlands Antilles; Aruba, Neth. Ant"    "0-5;60-89;900-999");
        
$groups[99905] = array("Bolivia"    "0-3;40-79;800-999");
        
$groups[99906] = array("Kuwait"    "0-2;30-59;600-699;70-89;9-9");
        
$groups[99908] = array("Malawi"    "0-0;10-89;900-999");
        
$groups[99909] = array("Malta"    "0-3;40-94;950-999");
        
$groups[99910] = array("Sierra Leone"    "0-2;30-89;900-999");
        
$groups[99911] = array("Lesotho"    "00-09;10-59;600-999");
        
$groups[99912] = array("Botswana"    "0-3;400-599;60-89;900-999");
        
$groups[99913] = array("Andorra"    "0-2;30-35;600-604");
        
$groups[99914] = array("Suriname"    "0-4;50-89;900-949");
        
$groups[99915] = array("Maldives"    "0-4;50-79;800-999");
        
$groups[99916] = array("Namibia"    "0-2;30-69;700-999");
        
$groups[99917] = array("Brunei Darussalam"    "0-2;30-89;900-999");
        
$groups[99918] = array("Faroe Islands"    "0-3;40-79;800-999");
        
$groups[99919] = array("Benin"    "0-2;40-69;900-999");
        
$groups[99920] = array("Andorra"    "0-4;50-89;900-999");
        
$groups[99921] = array("Qatar"    "0-1;20-69;700-799;8-8;90-99");
        
$groups[99922] = array("Guatemala"    "0-3;40-69;700-999");
        
$groups[99923] = array("El Salvador"    "0-1;20-79;800-999");
        
$groups[99924] = array("Nicaragua"    "0-2;30-79;800-999");
        
$groups[99925] = array("Paraguay"    "0-3;40-79;800-999");
        
$groups[99926] = array("Honduras"    "0-0;10-59;600-999");
        
$groups[99927] = array("Albania"    "0-2;30-59;600-999");
        
$groups[99928] = array("Georgia"    "0-0;10-79;800-999");
        
$groups[99929] = array("Mongolia"    "0-4;50-79;800-999");
        
$groups[99930] = array("Armenia"    "0-4;50-79;800-999");
        
$groups[99931] = array("Seychelles"    "0-4;50-79;800-999");
        
$groups[99932] = array("Malta"    "0-0;10-59;600-699;7-7;80-99");
        
$groups[99933] = array("Nepal"    "0-2;30-59;600-999");
        
$groups[99934] = array("Dominican Republic"    "0-1;20-79;800-999");
        
$groups[99935] = array("Haiti"    "0-2;7-8;30-59;600-699;90-99");
        
$groups[99936] = array("Bhutan"    "0-0;10-59;600-999");
        
$groups[99937] = array("Macau"    "0-1;20-59;600-999");
        
$groups[99938] = array("Srpska"    "0-1;20-59;600-899;90-99");
        
$groups[99939] = array("Guatemala"    "0-5;60-89;900-999");
        
$groups[99940] = array("Georgia"    "0-0;10-69;700-999");
        
$groups[99941] = array("Armenia"    "0-2;30-79;800-999");
        
$groups[99942] = array("Sudan"    "0-4;50-79;800-999");
        
$groups[99943] = array("Alsbania"    "0-2;30-59;600-999");
        
$groups[99944] = array("Ethiopia"    "0-4;50-79;800-999");
        
$groups[99945] = array("Namibia"    "0-5;60-89;900-999");
        
$groups[99946] = array("Nepal"    "0-2;30-59;600-999");
        
$groups[99947] = array("Tajikistan"    "0-2;30-69;700-999");
        
$groups[99948] = array("Eritrea"    "0-4;50-79;800-999");
        
$groups[99949] = array("Mauritius"    "0-1;20-80;900-999");
        
$groups[99950] = array("Cambodia"    "0-4;50-79;800-999");
        
$groups[99951] = array("Congo - no ranges fixed yet"    "");
        
$groups[99952] = array("Mali"    "0-4;50-79;800-999");
        
$groups[99953] = array("Paraguay"    "0-2;30-79;800-999");
        
$groups[99954] = array("Bolivia"    "0-2;30-69;700-999");
        
$groups[99955] = array("Srpska"    "0-1;20-59;600-899;90-99");
        return 
$groups;
    }
    
// }}}

    // {{{ __getVersion()
    /**
     * Get the Version of am ISBN Number
     *
     * @param string    $isbn    ISBN Number ofwhich the version to get
     *
     * @return mixed    false for no, or fully identifyable ISBN
     *                                Version Constant
     *
     * @access private
     */
    
private static function __getVersion($isbn) {
        
$ver self::_ISBNVersionGuess($isbn);
        
$r self::_ISBNVersionIsValid($ver);
        return 
$r;
    }
    
// }}}

    // {{{ _checkdigitISBN10()
     /**
     * Calculate checkdigit of an ISBN-10 string (ISBN-Body)
     * as documented on pp.4-5 2001 handbook.
     *
     * @param string    $isbnbody    ISBN-Body
     *
     * @return boolean    False if failed
     * @return string    Checkdigit [0-9,X]
     *
     * @access private
     */
    
private static function _checkdigitISBN10($isbnbody) {
        
/* The check digit is the last digit of an ISBN. It is calculated
         * on a modulus 11 with weights 10-2, using X in lieu of 10 where
         * ten would occur as a check digit.
         * This means that each of the first nine digits of the ISBN –
         * excluding the check digit itself – is multiplied by a number
         * ranging from 10 to 2 and that the resulting sum of the products,
         * plus the check digit, must be divisible by 11 without a
         * remainder. (pp.4-5 2001 Handbook)
         */
        
if (strlen($isbnbody) != 9) {
            return 
false;
        }
        
$sum 0;
        for(
$i 0$i 10$i++) {
            
$v intval(substr($isbnbody$i1));
            
$sum += $v * (10 $i);
        }
        
$remainder $sum 11;
        
$checkdigit 11 $remainder;
        if (
$remainder == 0) {
            
$checkdigit 0;
        }
        if (
$checkdigit == 10) {
            
$checkdigit "X";
        }
        return (string) 
$checkdigit;
    }
    
// }}}

    // {{{ _checkdigitISBN13()
     /**
     * Calculate checkdigit of an ISBN-13 string (Prefix + ISBN-Body)
     * as documented on pp.10-11 2005 handbook.
     *
     * @param string    $isbnbody    ISBN-Body
     * @param string    $prefix        EAN-Prefix (Default 978 for ISBN13-978)
     *
     * @return boolean    False if failed
     * @return string    Checkdigit [0-9]
     *
     * @access private
     */
    
private static function _checkdigitISBN13($isbnbody$prefix "978") {
        
$prefixandisbnbody $prefix $isbnbody;
        
$t $prefixandisbnbody;
        
$l strlen($t);
        if (
$l != 12) {
            return 
false;
        }
        
/* Step 1: Determine the sum of the weighted products for the first 12
        *  digits of the ISBN (see p.10 2005 handbook)
        */
        
$ii 1;
        
$sum 0;
        for(
$i 0$i 13$i++) {
            
$ii $ii;
            
$sum += intval(substr($t$i1)) * ($ii 1);
        }
        
/* Step 2: Divide the sum of the weighted products of the first 12
         * digits of the ISBN calculated in step 1 by 10, determining the
         * remainder. (see p.11 2005 handbook)
         */
        
$remainder $sum 10;

        
/* Step 3: Subtract the remainder calculated in step 2 from 10. The
         * resulting difference is the value of the check digit with one
         * exception. If the remainder from step 2 is 0, the check
         * digit is 0. (ebd.)
         */
        
$checkdigit 10 $remainder;
        if (
$remainder == 0) {
            
$checkdigit 0;
        }
        
/* return string value */
        
if (is_int($checkdigit)) {
            
$checkdigit = (string) $checkdigit;
        }
        if (
is_string($checkdigit) == false) {
            return 
false;
        }
        return 
$checkdigit;
    }
    
// }}}

    // {{{ _isISBNValid()
    /**
     * Validate an ISBN value
     *
     * @param string    $isbn     Number to validate
     *
     * @return boolean    False if ISBN number is not valid
     * @return mixed    Returns the Version to signal validity
     *
     * @access private
     */
    
private static function _isISBNValid($isbn$ver ISBN_DEFAULT_INPUTVERSION) {
        
/* version handling */
        
$r self::_ISBNVersionIs($ver);
        if (
$r === false) {
            return 
false;
        }
        if (
$ver === ISBN_VERSION_UNKNOWN) {
            
$ver self::_ISBNVersionGuess($isbn);
        }
        if (
self::_ISBNVersionIsValid($ver) === false) {
            return 
false;
        }
        
/* since a version is available now, normalise the ISBN input */
        
$isbnn self::_normaliseISBN($isbn);
        if (
$isbnn === false) {
            return 
false;
        }
        
/* normalzied ISBN and Version available, it's ok now
         * to perform indepth checks per version */
        
switch ($ver) {
            case 
ISBN_VERSION_ISBN_10:

                
/* check syntax against checkdigit */
                
$isbnbody self::__extractISBNBody($isbnn);
                
$check self::__extractCheckdigit($isbnn);
                if (
$check === false) {
                    return 
false;
                }
                
$checkdigit self::_checkdigitISBN10($isbnbody);
                if (
$checkdigit === false) {
                    return 
false;
                }
                if (
$checkdigit !== $check) {
                    return 
false;
                }

                
/* check registrationgroup validity */
                
$registrationgroup false;
                
$subbody false;
                
$r self::__ISBNBodyParts($isbnbody$registrationgroup$subbody);
                if (
$r == false) {
                    return 
false;
                }

                
/* check for undefined registrationgroup */
                
if (strlen($registrationgroup) == 0) {
                    return 
false;
                }

                
/* check registrant validity */
                
$groupid intval($registrationgroup);
                
$registrant false;
                
$publication false;
                
$r self::__ISBNSubbodyParts($subbody$groupid$registrant$publication);
                if (
$r == false) {
                    return 
false;
                }
                return 
true;

            case 
ISBN_VERSION_ISBN_13:
            case 
ISBN_VERSION_ISBN_13_978:

                
/* validate EAN Prefix */
                
$ean self::__extractEANPrefix($isbnn);
                if (
$ean !== "978") {
                    return 
false;
                }

                
/* check syntax against checkdigit */
                
$isbnbody self::__extractISBNBody($isbnn);
                
$check self::__extractCheckdigit($isbnn);
                if (
$check === false) {
                    return 
false;
                }
                
$checkdigit self::_checkdigitISBN13($isbnbody);
                if (
$checkdigit === false) {
                    return 
false;
                }
                if (
$check !== $checkdigit) {
                    return 
false;
                }

                
/* validate group */
                
$isbnbody self::__extractISBNBody($isbnn);
                if (
$isbnbody === false) {
                    return 
false;
                }

                
$registrationgroup false;
                
$subbody false;
                
$r self::__ISBNBodyParts($isbnbody$registrationgroup$subbody);
                if (
$r === false) {
                    return 
false;
                }

                
/* check for undefined registrationgroup */
                
if (strlen($registrationgroup) == 0) {
                    return 
false;
                }

                
/* validate publisher */
                
$registrant false;
                
$publication false;
                
$r self::__ISBNSubbodyParts($subbody$registrationgroup$registrant$publication);
                if (
$r === false) {
                    return 
false;
                }
                return 
$ver;

            case 
ISBN_VERSION_ISBN_13_979:
                
/* not yet standarized */
                
return false;
        }
        return 
false;
    }
    
// }}}

    // {{{ _ISBNVersionGuess()
    /**
     * Guesses the version of an ISBN
     *
     * @param string $isbn    ISBN Number of which the Version to guess
     *
     * @return bool        False/ISBN_VERSION_NONE if failed
     * @return integer    Value of any ISBN_VERSION_* Constant
     *                    excl. ISBN_VERSION_NONE
     * @access private
     */
    
private static function _ISBNVersionGuess($isbn) {
        
$isbn self::_normaliseISBN($isbn);
        if (
$isbn === false) {
            return 
ISBN_VERSION_NONE;
        }
        if ( 
strlen($isbn) == 10) {
            return 
ISBN_VERSION_ISBN_10;
        } else {
            return 
ISBN_VERSION_ISBN_13;
        }
    }
    
// }}}

    // {{{ _ISBNVersionIs()
    /**
     * Validate an ISBN Version value
     *
     * @param mixed $ver    version to be checked being a valid ISBN Version
     *
     * @return bool    true if value is valid, false if not
     *
     * @access private
     */
    
private static function _ISBNVersionIs($ver) {
        if (
is_bool($ver) === false && is_integer($ver) === false) {
            return 
false;
        }
        switch (
$ver) {
            case 
ISBN_VERSION_NONE:
            case 
ISBN_VERSION_UNKNOWN:
            case 
ISBN_VERSION_ISBN_10:
            case 
ISBN_VERSION_ISBN_13:
            case 
ISBN_VERSION_ISBN_13_978:
            case 
ISBN_VERSION_ISBN_13_979:
                return 
true;
            default:
                return 
false;
        }
    }
    
// }}}

    // {{{ _ISBNVersionIsValid()
    /**
     * Validate an ISBN value being a valid (identifyable -10 / -13) value
     *
     * @param mixed $ver    value to be checked being a valid ISBN Version
     *
     * @return bool    true if value is valid, false if not
     *
     * @access private
     */
    
private static function _ISBNVersionIsValid($ver) {
        
$r self::_ISBNVersionIs($ver);
        if (
$r === false) {
            return 
false;
        }
        switch (
$ver) {
            case 
ISBN_VERSION_ISBN_10:
            case 
ISBN_VERSION_ISBN_13_978:
                return 
true;
            default:
                return 
false;
        }
    }
    
// }}}

    // {{{ _normaliseISBN()
    /**
     * downformat "any" ISBN Number to the very basics
     * an isbn number is a 10 or 13 digit. with the
     * 10 digit string, the last digit can be 0-9 and
     * X as well, all other are 0-9 only
     * additionally this fucntion can be used to validate
     * the isbn against correct length and chars
     *
     * @param string    $number     ISBN String to normalise
     *
     * @return boolean    false if this function was not able
     *                    to normalise the input
     * @return string    normalised ISBN Number
     *
     * @access private
     */
    
private static function _normaliseISBN($isbn) {
        
/* validate input */
        
$r settype($isbn'string');
        if (
$r === false) {
            return 
false;
        }

        
/* normalize (trim & case)*/
        
$isbn trim($isbn);
        
$isbn strtoupper($isbn);

        
/* remove lang specific prefix (if any) */
        
$isbn self::_normaliseISBN_removeLangSpecific($isbn);

        
/* remove ISBN-10: or ISBN-13: prefix (if any) */
        
if (strlen($isbn 8)) {
            
$prefix substr($isbn08);
            if (
$prefix == "ISBN-10:" || $prefix == "ISBN-13:") {
                
$isbn substr($isbn8);
                
$isbn ltrim($isbn);
            }
        }

        
/* remove lang specific prefix again (if any) */
        
$isbn self::_normaliseISBN_removeLangSpecific($isbn);

        
/* remove "ISBN" prefix (if any)*/
        
if (substr($isbn04) == 'ISBN') {
            
$isbn substr($isbn4);
        }

        
/* remove cosmetic chars and different type of spaces */
        
$isbn str_replace(array("-"" ""\t""\n"), ""$isbn);

        
/* take the length to check and differ between versions
         * sothat a syntaxcheck can be made */
        
$l strlen($isbn);
        if (
$l != 10 && $l != 13) {
            return 
false;
        } elseif (
$l == 10) {
            if (!
preg_match("/^[0-9]{9}[0-9X]$/"$isbn)) {
                return 
false;
            }
        } elseif (
$l == 13) {
            if (!
ctype_digit($isbn)) {
                return 
false;
            }
        }
        return 
$isbn;
    }
    
// }}}

    // {{{ _normaliseISBN_removeLangSpecific()
    /**
     * helper function for _normaliseISBN to
     * remove lang sepcific ISBN prefix
     *
     * @param string    $isbn     ISBN String to check (partially normalised)
     *
     * @return string    input value passed through helper
     *
     * @access private
     */
    
private static function _normaliseISBN_removeLangSpecific($isbn) {
        
$lang strtoupper(ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX);
        
$l strlen($lang);
        if (
$l ) {
            if (
substr($isbn0$l) == $lang) {
                
$isbn substr($isbn$l);
            }
        }
        return 
$isbn;
    }
    
// }}}

    // {{{ convert()
    /**
     * converts an ISBN number from one version to another
     *
     * @param string     $isbnin     ISBN to convert, must be a valid ISBN Number
     * @param integer     $verfrom     version value of the input ISBN
     * @param integer    $verto        version value to convert to
     *
     * @return bool     false if conversion failed
     * @return string     converted ISBN Number
     */
    
public static function convert($isbnin$verfrom ISBN_VERSION_ISBN_10$verto ISBN_VERSION_ISBN_13) {
        
/* validate input */
        
if (!self::_ISBNVersionIsValid($verfrom)) {
            return 
false;
        }
        if (!
self::_ISBNVersionIsValid($verto)) {
            return 
false;
        }
        
$r self::validate($isbnin$verfrom);
        if (
$r === false) {
            return 
false;
        }
        
/* normalize input */
        
$isbnn self::_normaliseISBN($isbnin);
        
/* input is ok now, let's convert */
        
switch(true) {
            case 
$verfrom == ISBN_VERSION_ISBN_10 && $verto == ISBN_VERSION_ISBN_13:
                
/* convert 10 to 13 */
                
$isbnbody self::__extractISBNBody($isbnn);
                if (
$isbnbody === false) {
                    return 
false;
                }
                
$isbnout "978" $isbnbody self::_checkdigitISBN13($isbnbody);
                return 
$isbnout;
        }
        return 
false;
    }
    
// }}}

    // {{{ getCheckdigit()
    /**
     * Get the Checkdigit Part of ISBN Number
     *
     * @return mixed    (bool) False if failed, (string) Checkdigit
     */
    
public function getCheckdigit() {
        
$ver $this->getVersion();
        
$check false;
        switch (
$ver) {
            case 
ISBN_VERSION_ISBN_10:
                
$check self::_checkdigitISBN10($this->getISBNBody());
                break;
            case 
ISBN_VERSION_ISBN_13:
                
$check self::_checkdigitISBN13($this->getISBNBody());
                break;
        }
        return 
$check;
    }
    
// }}}

    // {{{ getEAN()
    /**
     * Get the EAN Prefix of ISBN Number
     *
     * @return mixed    (bool) False if failed, (string) EAN Prefix
     */
    
public function getEAN() {
        
$ver $this->getVersion();
        if (
$ver === false ) {
            return 
false;
        }
        if (
$ver == ISBN_VERSION_ISBN_13_978) {
            return 
"978";
        }
        if (
$ver == ISBN_VERSION_ISBN_13_979) {
            return 
"979";
        }
        return 
"";
    }
    
// }}}

    // {{{ getGroup()
    /**
     * Get the Registrationgroup Part of the ISBN Number
     *
     * @return mixed    (bool) False if failed, (string) Group
     */
    
public function getGroup() {
        return 
$this->isbn_group;
    }
    
// }}}

    // {{{ setGroup()
    /**
     * Setter for the Registrationgroup Part of the ISBN Number
     *
     * @access private
     */
    
private function setGroup($group) {
        if (
is_string($group) == false) {
            throw new 
Exception("Wrong Vartype"13);
        }
        
$l strlen($group);
        if (
$l || $l 5) {
            throw new 
Exception("Wrong Group"11);
        }
        
$testbody substr($group "000000000"09);
        
$testgroup self::__extractGroup($testbody);
        if (
$testgroup === false ) {
            throw new 
Exception("Invalid Group"12);
        }
        if (
$testgroup != $group) {
            throw new 
Exception("Invalid Group"12);
        }
        
$this->isbn_group $group;
    }

    
// {{{ getISBN()
    /**
     * Get whole ISBN Number
     *
     * @return string ISBN Number (unformatted); empty string if this is not a valid ISBN
     */
    
public function getISBN() {
        
$ver $this->getVersion();
        if (
$ver === false ) {
            return 
"";
        }

        
$isbn "";

        
$r self::_ISBNVersionIsValid($ver);
        if (
$r === false ) {
            return 
$isbn;
        }

        
$isbn .= $this->getEAN();
        
$isbn .= $this->getISBNBody();
        
$isbn .= $this->getCheckdigit();

        return 
$isbn;
    }
    
// }}}

    // {{{ setISBN()
    /**
     * Setter for ISBN
     *
     * @param strign ISBN Number
     *             this is a valid ISBN Number or it is an Empty string
     *             which will reset the class
     *
     * @access public
     */
    
public function setISBN($isbn) {
        if (
$isbn == "") {
            
$this->ver ISBN_VERSION_NONE;
            
$this->isbn_group "";
            
$this->isbn_publisher "";
            
$this->isbn_title "";
        } else {
            
$isbnn self::_normaliseISBN($isbn);
            
$ver self::__getVersion($isbnn);
            if (
$ver === false) {
                throw new 
Exception("Invalid ISBN"1);
            }
            if (
$ver != $this->ver) {
                throw new 
Exception("ISBN Version of passed ISBN is not the same like this class-ISBN ISBN Version."2);
            }
            
$body self::__extractISBNBody($isbnn);
            if (
$body === false) {
                throw new 
Exception("Invalid ISBN (could not extract body)"15);
            }
            try {
                
$this->setISBNBody($body);
            } catch (
Exception $e) {
                throw new 
Exception("Invalid ISBN (invalid body)"16);
            }
        }
    }
    
// }}}

    // {{{ getISBNBody()
    /**
     * GetISBNBody()
     *
     * @return string ISBN Body (not an offical term)
     */
    
private function getISBNBody() {
        
$body "";
        
$body .= $this->getGroup();
        
$body .= $this->getISBNSubbody();
        return 
$body;
    }
    
// }}}

    // {{{ setISBNBody()
    /**
     * setISBNBody()
     *
     * Setter for ISBNBody
     *
     * @param string $body ISBNBody
     */
     
private function setISBNBody($body) {
         
/* validate parameter */
         
if (is_string($body) == false) {
             throw new 
Exception("Wrong Vartype"13);
         }
        if (
strlen($body) != 9) {
            throw new 
Exception("Not a Body"7);
        }
        if (
ctype_digit($body) !== true) {
            throw new 
Exception("Not a Body"7);
        }

        
/* validate body by extracting and validating parts */
        
$group false;
        
$subbody false;
        
$r self::__ISBNBodyParts($body$group$subbody);
        if (
$r == false) {
            throw new 
Exception("Invalid Body"8);
        }

        try {
            
$this->setGroup($group);
        } catch (
Exception $e) {
            throw new 
Exception("Invalid Body (The Group Part is invalid)"9);
        }

        try {
            
$this->setISBNSubbody($subbody);
        } catch (
Exception $e) {
            throw new 
Exception("Invalid Body (The Subbody Part is invalid)"10);
        }
     }
    
// }}}

    // {{{ getISBNSubbody()
    /**
     * Get ISBNSubbody ()
     *
     * @return ISBN Subbody
     */
    
private function getISBNSubbody() {
        
$subbody "";
        
$subbody .= $this->getPublisher();
        
$subbody .= $this->getTitle();
        return 
$subbody;
    }
    
// }}}

    // {{{ setISBNSubbody()
    /**
     * setISBNSubbody
     *
     * Setter for the ISBN Subbody
     *
     * @param string $subbody ISBN Subbody
     */
    
private function setISBNSubbody($subbody) {
        
/* validate parameter */
        
if (is_string($subbody) == false) {
            throw new 
Exception("Wrong Vartype"13);
        }
        
$l strlen($subbody);
        if ( 
$l || $l 8) {
            throw new 
Exception("Not a Subbody"14);
        }
        
/* validate by setting apart */
        
$registrant false;
        
$publication false;
        
$groupid intval($this->isbn_group);
        
$r self::__ISBNSubbodyParts($subbody$groupid$registrant$publication);
        if (
$r === false) {
            throw new 
Exception("Invalid Subbody"15);
        }
        
/* edit+ setter/getter for Registrant/Publisher and Title/Publication */
        
$this->isbn_publisher $registrant;
        
$this->isbn_title $publication;
    }

    
// {{{ getPublisher()
    /**
     * Get the Publication Part of the ISBN Number
     *
     * @return mixed    (bool) False if failed, (string) Publisher
     */
    
public function getPublisher() {
        return 
$this->isbn_publisher;
    }
    
// }}}

    // {{{ getTitle()
    /**
     * Get the Title Part of the ISBN Number
     *
     * @return mixed    (bool) False if failed, (string) Title
     */
    
public function getTitle() {
        return 
$this->isbn_title;
    }
    
// }}}


    // {{{ isValid()
    /**
     * Returns this ISBN validity
     *
     * @return
     * @return boolean
     */
    
public function isValid() {
        
$isbn $this->getISBN();
        
$r self::validate($this->getISBN(), $this->getVersion());
        return (bool) 
$r;
    }

    
// {{{ validate()
    /**
     * Validates an ISBN
     *
     * @return
     * @return boolean    False if failed
     * @return integer    Version Value
     */
    
public static function validate($isbn$ver ISBN_DEFAULT_INPUTVERSION) {
        
$r self::_ISBNVersionIs($ver);
        if (
$r === false) {
            return 
false;
        }
        if (
$ver === ISBN_VERSION_UNKNOWN) {
            
$ver self::_ISBNVersionGuess($isbn);
        }
        if (
self::_ISBNVersionIsValid($ver) === false) {
            return 
false;
        }
        
$r self::_isISBNValid($isbn$ver);
        if (
$r === false) {
            return 
false;
        }
        return 
$ver;
    }
    
// }}}

    // {{{ getVersion()
    /**
     * Returns version of this objects ISBN
     *
     * @return boolean    Version Value (ISBN_VERSION_NONE)
     * @return integer    Version Value
     */
    
public function getVersion() {
        return 
$this->ver;
    }


    
// {{{ version()
    /**
     * Returns guessed ISBN version of passed string
     *
     * Note: This is not Validation. To get the validated
     * version of an ISBN Number use self::validate();
     *
     * @see    validate();
     *
     * @param string    $isbn    ISBN Number to guess Version of
     *
     * @return boolean    False if failed
     * @return integer    Version Value
     */
    
public static function guessVersion($isbn) {
        
/* if the optional parameter is missing, use
         * this objects own isbn number (not static then)
         */
        
$r self::_ISBNVersionGuess($isbn);
        return 
$r;
    }
    
// }}}

}
?>