ISBN
[ class tree: ISBN ] [ index: ISBN ] [ all elements ]

Source for file ISBN.php

Documentation is available at ISBN.php

  1. <?php
  2. /**
  3.  * ISBN
  4.  *
  5.  * Handle, Convert and Validate ISBN Numbers
  6.  *
  7.  * PHP version 5
  8.  *
  9.  * LICENSE: LGPL (In cases LGPL is not appropriate, it is licensed under GPL)
  10.  *
  11.  * Package to handle, convert and validate ISBN numbers. It includes:
  12.  *
  13.  *  - ISBN specifics: EAN/PrefixArrayAccess (integer)
  14.  *  - ISBN specifics: Group/Registration Group [2001: Group identifier] (integer)
  15.  *  - ISBN specifics: GroupTitle/Registration Group Title (string)
  16.  *  - ISBN specifics: Publisher/Registrant [2001: Publisher identifier] (string)
  17.  *  - ISBN specifics: Title/Publication [2001: Title identifier] (string)
  18.  *  - ISBN specifics: Checkdigit (string)
  19.  *  - ISBN specifics: 'ISBNBody' (string)
  20.  *  - ISBN specifics: 'ISBNSubbody' (string)
  21.  *  - ISBN Version handling
  22.  *  - Syntactical Validation plus Validation based on real ISBN Data
  23.  *  - ISBN-10 (ISO 2108) checksum calculation
  24.  *  - Validation (ISBN-10 and ISBN-13-978)
  25.  *  - Conversion to ISBN-13-978
  26.  *  - ISBN-13-978 (2005 Handbook, ISO pending; ISBN-13)
  27.  *  - ISBN-13 checksum calculation (EAN)
  28.  *
  29.  * Based on standards published by international ISBN Agency
  30.  * http://www.isbn-international.org/
  31.  *
  32.  * @category  Pending
  33.  * @package   ISBN
  34.  * @author    Tom Klingenberg <tkli-php@lastflood.net>
  35.  * @copyright 2006-2007 Tom Klingenberg
  36.  * @license   LGPL http://www.gnu.org/licenses/lgpl.txt
  37.  * @version   v 0.1.6 CVS: <cvs_id>
  38.  * @link      http://isbn.lastflood.com online docs
  39.  * 
  40.  * @todo      License for .js file or remove it
  41.  * @todo      GroupTitle
  42.  * @todo      PEAR Package.xml
  43.  *
  44.  */
  45.  
  46. // {{{ constants
  47. /**
  48.  * ISBN Versions supported
  49.  */
  50. define('ISBN_VERSION_NONE', false);
  51. /**
  52.  * VERSION_UNKNOWN is by the caller only, this shall never
  53.  * be a value returned by a public function or getter
  54.  */
  55. define('ISBN_VERSION_UNKNOWN', 0);
  56. define('ISBN_VERSION_ISBN_10', 10);
  57. define('ISBN_VERSION_ISBN_13', 13978);
  58. define('ISBN_VERSION_ISBN_13_978', ISBN_VERSION_ISBN_13);
  59. define('ISBN_VERSION_ISBN_13_979', 13979);  /* reserved */
  60.  
  61. /*
  62.  * Default ISBN Version for class input / usage
  63.  */
  64. define('ISBN_DEFAULT_INPUTVERSION', ISBN_VERSION_UNKNOWN);
  65. /*
  66.  * Default ISBN Seperator string
  67.  */
  68. define('ISBN_DEFAULT_COSMETIC_SEPERATOR', '-');
  69.  
  70. /*
  71.  * ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX
  72.  *
  73.  * When printed, the ISBN is always preceded by the letters "ISBN".
  74.  * Note: In countries where the Latin alphabet is not used, an abbreviation
  75.  * in the characters of the local script may be used in addition to the
  76.  * Latin letters "ISBN".
  77.  * This can be defined as a default value wihtin this constant.
  78.  */
  79. define('ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX', '');
  80. // }}}
  81.  
  82. require_once 'PEAR/Exception.php';
  83.  
  84. // {{{ ISBN_Exception
  85. /**
  86.  * ISBN_Exception class
  87.  *
  88.  * @category  Pending
  89.  * @package   ISBN
  90.  * @author    Tom Klingenberg <tkli-php@lastflood.net>
  91.  * @copyright 2006-2007 Tom Klingenberg
  92.  * @license   LGPL http://www.gnu.org/licenses/lgpl.txt
  93.  * @link      http://isbn.lastflood.com/
  94.  * @since     Class available since Release 0.1.3
  95.  */
  96. class ISBN_Exception Extends PEAR_Exception
  97. {
  98. }
  99. // }}}
  100.  
  101. // {{{ ISBN
  102. /**
  103.  * ISBN class
  104.  *
  105.  * Class to Handle, Convert and Validate ISBN Numbers
  106.  *
  107.  * @category  Pending
  108.  * @package   ISBN
  109.  * @author    Tom Klingenberg <tkli-php@lastflood.net>
  110.  * @copyright 2006-2007 Tom Klingenberg
  111.  * @license   LGPL http://www.gnu.org/licenses/lgpl.txt
  112.  * @link      http://isbn.lastflood.com/
  113.  * @since     Class available since Release 0.0.0
  114.  */
  115. class ISBN
  116. {
  117.     /**
  118.      * @var string ISBN Registration Group
  119.      */
  120.     private $isbn_group = '';
  121.     /**
  122.      * @var string ISBN Publisher
  123.      */
  124.     private $isbn_publisher = '';
  125.     /**
  126.      * @var string ISBN Title
  127.      */
  128.     private $isbn_title = '';
  129.  
  130.     /**
  131.      * @var mixed ISBN number version
  132.      */
  133.     private $ver = ISBN_VERSION_NONE;
  134.     
  135.     /**
  136.      * @var array ISBN Groups Data acting as cache
  137.      * @see _getISBN10Groups()
  138.      */    
  139.     private static $varISBN10Groups = array();    
  140.  
  141.     // {{{ __construct
  142.         /**
  143.      * Constructor
  144.      *
  145.      * @param array $isbn String of ISBN Value to use
  146.      * @param mixed $ver  Optional Version Constant
  147.      *
  148.      * @access public
  149.      * 
  150.      * @throws ISBN_Exception in case it fails
  151.      */
  152.     function __construct($isbn = '', $ver = ISBN_DEFAULT_INPUTVERSION)
  153.     {
  154.         /* validate & handle optional isbn parameter */
  155.         if (is_string($isbn) == false ) {
  156.             throw new ISBN_Exception('ISBN parameter must be a string');
  157.         }
  158.         if (strlen($isbn) == 0) {
  159.             $this->setISBN($isbn);
  160.             return;
  161.         }
  162.  
  163.         /* validate version parameter */
  164.         if (self::_isbnVersionIs($ver) == false) {
  165.             throw new ISBN_Exception(
  166.                 'ISBN Version parameter is not an ISBN Version'
  167.             );
  168.         }
  169.  
  170.         /* ISBN has been passed, check the version now:
  171.          *  if it is unknown, try to dertine it, if this fails
  172.          *  throw an exception
  173.          */
  174.         if ($ver == ISBN_VERSION_UNKNOWN) {
  175.             $verguess = self::_isbnVersionGuess($isbn);
  176.             if (self::_isbnVersionIsValid($verguess)) {
  177.                 $ver = $verguess;
  178.             } else {
  179.                 /* throw new ISBN_Exception(
  180.                  *'ISBN Version couldn\'t determined.');
  181.                  */                                  
  182.                 $ver = ISBN_VERSION_NONE;
  183.             }
  184.         }
  185.         /* version determined */
  186.         $this->ver = $ver;
  187.  
  188.         /* handle a complete invalid ISBN of which a version could
  189.          * not be determined. */
  190.         if ($ver === ISBN_VERSION_NONE) {
  191.             $this->setISBN('');
  192.             return;
  193.         }
  194.  
  195.         try {
  196.             $this->setISBN($isbn);
  197.         } catch(Exception $e) {
  198.             /* the isbn is invalid and not set, sothat this
  199.              * ISBN object will be set to a blank value. */
  200.             $this->setISBN('');
  201.         }
  202.  
  203.     }
  204.     // }}}
  205.  
  206.     // {{{ _extractCheckdigit()
  207.         /**
  208.      * extract Checkdigit of an ISBN-Number
  209.      *
  210.      * @param string $isbnn normalized ISBN string
  211.      *
  212.      * @return string|falseISBN-Body or false if failed
  213.      *
  214.      */
  215.     private static function _extractCheckdigit($isbnn)
  216.     {
  217.         $checkdigit = false;
  218.         $checkdigit = substr($isbnn, -1);
  219.         return (string) $checkdigit;
  220.     }
  221.     // }}}
  222.  
  223.     // {{{ _extractEANPrefix()
  224.         /**
  225.      * extracts EAN-Prefix of a normalized isbn string
  226.      *
  227.      * @param string $isbnn normalized isbn string
  228.      *
  229.      * @return string|falsePrefix or false if failed
  230.      */
  231.     private static function _extractEANPrefix($isbnn)
  232.     {
  233.         $r = settype($isbnn, 'string');
  234.         if ($r === false) {
  235.             return false;
  236.         }
  237.         if (strlen($isbnn) < 3) {
  238.             return false;
  239.         }
  240.         $prefix = substr($isbnn, 0, 3);
  241.         return $prefix;
  242.     }
  243.     // }}}
  244.  
  245.     // {{{ _extractGroup()
  246.         /**
  247.      * extract Registration Group of an ISBN-Body
  248.      *
  249.      * @param string $isbnbody ISBN-Body
  250.      *
  251.      * @return integer|false   Registration Group or false if failed
  252.      */
  253.     private static function _extractGroup($isbnbody)
  254.     {
  255.         $group   = '';
  256.         $subbody = '';
  257.  
  258.         $r = self::_isbnBodyParts($isbnbody, $group, $subbody);
  259.         if ($r === false) {
  260.             return false;
  261.         }
  262.         return $group;
  263.     }
  264.     // }}}
  265.  
  266.     // {{{ _extractISBNBody()
  267.         /**
  268.      * extract ISBN-Body of an ISBN-Number
  269.      *
  270.      * @param string $isbnn normalized ISBN string
  271.      *
  272.      * @return string|falseISBN-Body or false if failed
  273.      */
  274.     private static function _extractISBNBody($isbnn)
  275.     {
  276.         /* extract */
  277.         $body  = false;
  278.         $isbnn = (string) $isbnn;
  279.  
  280.         $l = strlen($isbnn);
  281.         if ($l == 10) {
  282.             $body =  substr($isbnn, 0, -1);
  283.         } elseif ($l == 13) {
  284.             $body =  substr($isbnn, 3, -1);
  285.         } else {
  286.             return false;
  287.         }
  288.         /* verify */
  289.         $r = settype($body, 'string');
  290.         if ($r === false) {
  291.             return false;
  292.         }
  293.         if (strlen($body) != 9) {
  294.             return false;
  295.         }
  296.         if (ctype_digit($body) === false) {
  297.             return false;
  298.         }
  299.         return $body;
  300.     }
  301.     // }}}
  302.  
  303.     // {{{ _isbnBodyParts()
  304.         /**
  305.      * Get the 2 Parts of the ISBN-Body (ISBN-10/ISBN-13-978)
  306.      *
  307.      * @param string $isbnbody           ISBN-Body
  308.      * @param string &$registrationgroup Registration Group
  309.      * @param string &$isbnsubbody       ISBN-Subbody
  310.      *
  311.      * @return boolean  False if failed, True on success
  312.      *
  313.      * @access private
  314.      */
  315.     private static function _isbnBodyParts($isbnbody, 
  316.                                            &$registrationgroup, 
  317.                                            &$isbnsubbody)
  318.     {
  319.         /* validate input (should not be needed, @access private) */
  320.         $r = settype($isbnbody, 'string');
  321.         if ($r === false) {
  322.             return false;
  323.         }
  324.         if (strlen($isbnbody) != 9) {
  325.             return false;
  326.         }
  327.         if (ctype_digit($isbnbody) === false) {
  328.             return false;
  329.         }
  330.         /* extract registraion group
  331.          * boundaries see p.13 2005 handbook
  332.          */
  333.         $boundaries = array();
  334.  
  335.         $boundaries[] = array(    0, 59999, 1);
  336.         $boundaries[] = array(60000, 60099, 3); // Iran 2006-12-05
  337.         $boundaries[] = array(60100, 69999, 0);
  338.         $boundaries[] = array(70000, 79999, 1);
  339.         $boundaries[] = array(80000, 94999, 2);
  340.         $boundaries[] = array(95000, 98999, 3);
  341.         $boundaries[] = array(99000, 99899, 4);
  342.         $boundaries[] = array(99900, 99999, 5);
  343.         /* segment value */
  344.         $segment      = substr($isbnbody, 0, 5);
  345.         $segmentvalue = intval($segment);
  346.         /* test segment value against boundaries */
  347.         $r = false;
  348.         foreach ($boundaries as $boundary) {
  349.             if ($segmentvalue >= $boundary[0] && $segmentvalue <= $boundary[1]) {
  350.                 $r = $boundary[2];
  351.             }
  352.         }
  353.         if ($r === false) {
  354.             return false;
  355.         }
  356.         /* $r is 0 when the boundary is not defined */
  357.         if ($r === 0) {
  358.             return false;
  359.         }
  360.         $registrationgroup = substr($isbnbody, 0, $r);
  361.         $isbnsubbody       = substr($isbnbody, $r);
  362.         return true;
  363.     }
  364.     // }}}
  365.  
  366.     // {{{ _isbnSubbodyParts()
  367.         /**
  368.      * Get the 2 Parts of the ISBN-Subbody (ISBN-10/ISBN-13)
  369.      *
  370.      * @param string  $isbnsubbody  ISBN-Subbody
  371.      * @param integer $groupid      Registrationgroup
  372.      * @param string  &$registrant  Registrant
  373.      * @param string  &$publication Publication
  374.      *
  375.      * @return boolean  False if failed, true on success
  376.      *
  377.      * @access private
  378.      */
  379.     private static function _isbnSubbodyParts($isbnsubbody, 
  380.                                               $groupid, 
  381.                                               &$registrant, 
  382.                                               &$publication)
  383.     {
  384.         /* validate input (should not be needed, @access private) */
  385.         $r = settype($isbnsubbody, 'string');
  386.         if ($r === false) {
  387.             return false;
  388.         }
  389.         $l = strlen($isbnsubbody);
  390.         if ( $l < 1 || $l > 8) {
  391.             return false;
  392.         }
  393.         if (ctype_digit($isbnsubbody) === false) {
  394.             return false;
  395.         }
  396.         $r = settype($groupid, 'integer');
  397.         if ($r === false) {
  398.             return false;
  399.         }
  400.         if ($groupid < 0 || $groupid > 99999) {
  401.             return false;
  402.         }
  403.         /* extract registrant based on group and registrant range
  404.          * parse this specific group format: 
  405.          *  array(
  406.          *      'English speaking area',
  407.          *      '00-09;10-19;200-699;7000-8499;85000-89999;' .
  408.          *         '900000-949999;9500000-9999999'
  409.          *      );
  410.          */ 
  411.         
  412.         $group = self::_getISBN10Group($groupid);
  413.         if ($group === false) {
  414.             return false;
  415.         }
  416.  
  417.         $len = self::_getRegistrantLength($isbnsubbody, $group[1]);
  418.         if ($len === false) {
  419.             return false;
  420.         }        
  421.         $registrant  = substr($isbnsubbody, 0, $len);
  422.         $publication = substr($isbnsubbody, $len);
  423.         return true;
  424.     }
  425.     // }}}
  426.  
  427.     // {{{ _getRegistrantLength()
  428.         /**
  429.      * Return Length of Registrant part within an ISBNSubbody in a specific
  430.      * grouprange in this specific format:
  431.      *
  432.      * '00-09;10-19;200-699;7000-8499;85000-89999;900000-949999;9500000-9999999'
  433.      *
  434.      * Info: This function is compatible with Groupranges formatted in the
  435.      * .js file and might become obsolete if new formats are more fitting.
  436.      *
  437.      * @param string $isbnsubbody ISBN-Subbody
  438.      * @param string $grouprange  Grouprange in the Format '#a1-#z1;#a2-z2[...]'
  439.      *
  440.      * @return boolean|int False if failed or Length (in chars) of Registrant
  441.      *
  442.      * @access private
  443.      */
  444.     private static function _getRegistrantLength($isbnsubbody, $grouprange)
  445.     {
  446.         $r = settype($grouprange, 'string');
  447.         if ($r === false) {
  448.             return false;
  449.         }
  450.         if (strlen($grouprange) < 3) {
  451.             return false;
  452.         }
  453.         
  454.         $sl     = strlen($isbnsubbody);
  455.         $ranges = explode(';', $grouprange);
  456.         foreach ($ranges as $range) {
  457.             $range  = trim($range);
  458.             $fromto = explode('-', $range);
  459.             if (count($fromto) !== 2) {
  460.                 return false;
  461.             }
  462.             /* validation:
  463.              * from and to need to be in the same class,
  464.              * having the same length.
  465.              * registrant can not be bigger or same then the
  466.              * whole subbody, at least there is one digit for
  467.              * the publication.
  468.              */
  469.  
  470.             $l = strlen($fromto[0]);
  471.             if ($l != strlen($fromto[1])) {
  472.                 return false;
  473.             }
  474.             if ($l >= $sl) {
  475.                 return false;
  476.             }
  477.  
  478.             /* check that from/to values are in order */
  479.             if (strcmp($fromto[0], $fromto[1]) >= 0) {
  480.                 return false;
  481.             }
  482.  
  483.             /* compare and fire if matched */
  484.             $comparec = substr($isbnsubbody, 0, $l);
  485.  
  486.             if (strcmp($fromto[0], $comparec) < 1 && 
  487.                 strcmp($fromto[1], $comparec) > -1) {
  488.                 return $l;
  489.             }
  490.         }        
  491.         return false;
  492.     }
  493.     // }}}
  494.  
  495.     // {{{ _getISBN10Group()
  496.         /**
  497.      * Get ISBN-10 Registration Group Data by its numeric ID
  498.      *
  499.      * @param integer $id Registration Group Identifier
  500.      *
  501.      * @return mixed    array:   group array
  502.      *                   boolean: False if failed
  503.      */
  504.     private static function _getISBN10Group($id)
  505.     {
  506.         $r = settype($id, 'integer');
  507.         if ($r === false) {
  508.             return false;
  509.         }
  510.         $groups = self::_getISBN10Groups();
  511.         if ($groups === false) {
  512.             return false;
  513.         }
  514.         if (isset($groups[$id]) === false) {
  515.             return false;
  516.         }
  517.         $group = $groups[$id];
  518.         return $group;
  519.     }
  520.     // }}}
  521.  
  522.     // {{{ _getISBN10Groups()
  523.         /**
  524.      * Get all ISBN-10 Registration Groups
  525.      *
  526.      * @return array    groups array
  527.      *
  528.      *  Info: This function connects outer world data into this class logic
  529.      *        which can be generated with the supplied tools.
  530.      *        A user should not alter the array data. This data should be altered
  531.      *        together with the international ISBN Agency only.
  532.      */
  533.     private static function _getISBN10Groups()
  534.     {
  535.         /* check if data has been already loaded */
  536.         if (sizeof(self::$varISBN10Groups) > 0 ) {
  537.                 return self::$varISBN10Groups;      
  538.         }
  539.         
  540.         /* load external data */
  541.         try {       
  542.             $t = file_get_contents('data/groups.csv', true, null);
  543.         } catch(Exception $e) {
  544.             throw new ISBN_Exception(
  545.                 'Unable to read ISBN Groups Data file.'
  546.             );          
  547.         }
  548.         
  549.         /* parse external data */
  550.         $groups = array();      
  551.         $tls    = split("\n", $t);
  552.         $line   = 0;
  553.         foreach ($tls as $tl) {
  554.             $line++;            
  555.             $tlp = split(',', $tl);
  556.             if (sizeof($tlp) == 3) {
  557.                 $index = intval($tlp[0]);
  558.                 if (isset($groups[$index])) {
  559.                     throw new ISBN_Exception(
  560.                         'ISBN Groups Data is invalid, Group ' .
  561.                         $index . 'is a duplicate.'
  562.                     );                  
  563.                 }
  564.                 /* edit+ mature: sanitize external 
  565.                    data */
  566.                 $groups[$index] = array($tlp[1],$tlp[2]);                       
  567.             } else {
  568.                 throw new ISBN_Exception(
  569.                     'ISBN Groups Data is malformed on line #' . $line . 
  570.                     ' (' . sizeof($tlp) . ').'
  571.                 );
  572.             }               
  573.         }
  574.         
  575.         /* verify minimum */    
  576.         if (sizeof($groups) == 0 ) {
  577.             throw new ISBN_Exception(
  578.                 'ISBN Groups Data does not contain any valid data.'
  579.             );          
  580.         }
  581.         
  582.         self::$varISBN10Groups = $groups;
  583.         
  584.         /* return groups */
  585.         return $groups;
  586.     }
  587.     // }}}
  588.  
  589.     // {{{ _getVersion()
  590.         /**
  591.      * Get the Version of am ISBN Number
  592.      *
  593.      * @param string $isbn ISBN Number ofwhich the version to get
  594.      *
  595.      * @return mixed false for no, or fully identifyable ISBN
  596.      *                               Version Constant
  597.      *
  598.      * @access private
  599.      */
  600.     private static function _getVersion($isbn)
  601.     {
  602.         $ver = self::_isbnVersionGuess($isbn);
  603.         $r   = self::_isbnVersionIsValid($ver);
  604.         return $r;
  605.     }
  606.     // }}}
  607.  
  608.     // {{{ _checkdigitISBN10()
  609.           /**
  610.      * Calculate checkdigit of an ISBN-10 string (ISBN-Body)
  611.      * as documented on pp.4-5 2001 handbook.
  612.      *
  613.      * @param string $isbnbody ISBN-Body
  614.      *
  615.      * @return string|falseCheckdigit [0-9,X] or false if failed
  616.      *
  617.      * @access private
  618.      */
  619.     private static function _checkdigitISBN10($isbnbody)
  620.     {
  621.         /* The check digit is the last digit of an ISBN. It is calculated
  622.          * on a modulus 11 with weights 10-2, using X in lieu of 10 where
  623.          * ten would occur as a check digit.
  624.          * This means that each of the first nine digits of the ISBN ï¿½
  625.          * excluding the check digit itself ï¿½ is multiplied by a number
  626.          * ranging from 10 to 2 and that the resulting sum of the products,
  627.          * plus the check digit, must be divisible by 11 without a
  628.          * remainder. (pp.4-5 2001 Handbook)
  629.          */
  630.         if (strlen($isbnbody) != 9) {
  631.             return false;
  632.         }
  633.         $sum = 0;
  634.         for ($i = 0; $i < 10; $i++) {
  635.             $v    = intval(substr($isbnbody, $i, 1));
  636.             $sum += $v * (10 - $i);
  637.         }
  638.         $remainder  = $sum % 11;
  639.         $checkdigit = 11 - $remainder;
  640.         if ($remainder == 0) {
  641.             $checkdigit = 0;
  642.         }
  643.         if ($checkdigit == 10) {
  644.             $checkdigit = 'X';
  645.         }
  646.         return (string) $checkdigit;
  647.     }
  648.     // }}}
  649.  
  650.     // {{{ _checkdigitISBN13()
  651.           /**
  652.      * Calculate checkdigit of an ISBN-13 string (Prefix + ISBN-Body)
  653.      * as documented on pp.10-11 2005 handbook.
  654.      *
  655.      * @param string $isbnbody ISBN-Body
  656.      * @param string $prefix   EAN-Prefix (Default 978 for ISBN13-978)
  657.      *
  658.      * @return string|falseCheckdigit [0-9] or false if failed
  659.      *
  660.      * @access private
  661.      */
  662.     private static function _checkdigitISBN13($isbnbody, $prefix = '978')
  663.     {
  664.         $prefixandisbnbody = $prefix . $isbnbody;
  665.  
  666.         $t = $prefixandisbnbody;
  667.         $l = strlen($t);
  668.         if ($l != 12) {
  669.             return false;
  670.         }
  671.         /* Step 1: Determine the sum of the weighted products for the first 12
  672.         *  digits of the ISBN (see p.10 2005 handbook)
  673.         */
  674.         $ii = 1;
  675.         $sum = 0;
  676.         for ($i = 0; $i < 13; $i++) {
  677.             $ii   = 1 - $ii;
  678.             $sum += intval(substr($t, $i, 1)) * ($ii * 2 + 1);
  679.         }
  680.         /* Step 2: Divide the sum of the weighted products of the first 12
  681.          * digits of the ISBN calculated in step 1 by 10, determining the
  682.          * remainder. (see p.11 2005 handbook)
  683.          */
  684.         $remainder = $sum % 10;
  685.  
  686.         /* Step 3: Subtract the remainder calculated in step 2 from 10. The
  687.          * resulting difference is the value of the check digit with one
  688.          * exception. If the remainder from step 2 is 0, the check
  689.          * digit is 0. (ebd.)
  690.          */
  691.         $checkdigit = 10 - $remainder;
  692.         if ($remainder == 0) {
  693.             $checkdigit = 0;
  694.         }
  695.         /* return string value */
  696.         if (is_int($checkdigit)) {
  697.             $checkdigit = (string) $checkdigit;
  698.         }
  699.         if (is_string($checkdigit) == false) {
  700.             return false;
  701.         }
  702.         return $checkdigit;
  703.     }
  704.     // }}}
  705.  
  706.     // {{{ _isIsbnValid()
  707.         /**
  708.      * Validate an ISBN value
  709.      *
  710.      * @param string $isbn Number to validate
  711.      * @param string $ver  Version to validate against
  712.      *
  713.      * @return integer|false   Returns the Version to signal validity or false if
  714.      *                             ISBN number is not valid
  715.      *
  716.      * @access private
  717.      */
  718.     private static function _isIsbnValid($isbn, $ver = ISBN_DEFAULT_INPUTVERSION)
  719.     {
  720.         /* version handling */
  721.         $r = self::_isbnVersionIs($ver);
  722.         if ($r === false) {
  723.             return false;
  724.         }
  725.         if ($ver === ISBN_VERSION_UNKNOWN) {
  726.             $ver = self::_isbnVersionGuess($isbn);
  727.         }
  728.         if (self::_isbnVersionIsValid($ver) === false) {
  729.             return false;
  730.         }
  731.         /* since a version is available now, normalise the ISBN input */
  732.         $isbnn = self::_normaliseISBN($isbn);
  733.         if ($isbnn === false) {
  734.             return false;
  735.         }
  736.         /* normalzied ISBN and Version available, it's ok now
  737.          * to perform indepth checks per version */
  738.         switch ($ver) {
  739.         case ISBN_VERSION_ISBN_10:
  740.  
  741.             /* check syntax against checkdigit */
  742.             $isbnbody = self::_extractISBNBody($isbnn);
  743.             $check    = self::_extractCheckdigit($isbnn);
  744.             if ($check === false) {
  745.                 return false;
  746.             }
  747.             $checkdigit = self::_checkdigitISBN10($isbnbody);
  748.             if ($checkdigit === false) {
  749.                 return false;
  750.             }
  751.             if ($checkdigit !== $check) {
  752.                 return false;
  753.             }
  754.  
  755.             /* check registrationgroup validity */
  756.             $registrationgroup = false;
  757.             $subbody           = false;
  758.  
  759.             $r = self::_isbnBodyParts($isbnbody, $registrationgroup, $subbody);
  760.             if ($r == false) {
  761.                 return false;
  762.             }
  763.  
  764.             /* check for undefined registrationgroup */
  765.             if (strlen($registrationgroup) == 0) {
  766.                 return false;
  767.             }
  768.  
  769.             /* check registrant validity */
  770.             $groupid     = intval($registrationgroup);
  771.             $registrant  = false;
  772.             $publication = false;
  773.  
  774.             $r = self::_isbnSubbodyParts($subbody, $groupid, 
  775.                                          $registrant, $publication);
  776.             if ($r == false) {
  777.                 return false;
  778.             }
  779.             return true;
  780.  
  781.         case ISBN_VERSION_ISBN_13:
  782.         case ISBN_VERSION_ISBN_13_978:
  783.  
  784.             /* validate EAN Prefix */
  785.             $ean = self::_extractEANPrefix($isbnn);
  786.             if ($ean !== '978') {
  787.                 return false;
  788.             }
  789.  
  790.             /* check syntax against checkdigit */
  791.             $isbnbody = self::_extractISBNBody($isbnn);
  792.             $check    = self::_extractCheckdigit($isbnn);
  793.             if ($check === false) {
  794.                 return false;
  795.             }
  796.             $checkdigit = self::_checkdigitISBN13($isbnbody);
  797.             if ($checkdigit === false) {
  798.                 return false;
  799.             }
  800.             if ($check !== $checkdigit) {
  801.                 return false;
  802.             }
  803.  
  804.             /* validate group */
  805.             $isbnbody = self::_extractISBNBody($isbnn);
  806.             if ($isbnbody === false) {
  807.                 return false;
  808.             }
  809.  
  810.             $registrationgroup = false;
  811.             $subbody           = false;
  812.  
  813.             $r = self::_isbnBodyParts($isbnbody, $registrationgroup, $subbody);
  814.             if ($r === false) {
  815.                 return false;
  816.             }
  817.  
  818.             /* check for undefined registrationgroup */
  819.             if (strlen($registrationgroup) == 0) {
  820.                 return false;
  821.             }
  822.  
  823.             /* validate publisher */
  824.             $registrant  = false;
  825.             $publication = false;
  826.  
  827.             $r = self::_isbnSubbodyParts($subbody, $registrationgroup, 
  828.                                          $registrant, $publication);
  829.             if ($r === false) {
  830.                 return false;
  831.             }
  832.             return $ver;
  833.  
  834.         case ISBN_VERSION_ISBN_13_979:
  835.             /* not yet standarized */
  836.             return false;
  837.  
  838.         }
  839.         return false;
  840.     }
  841.     // }}}
  842.  
  843.     // {{{ _isbnVersionGuess()
  844.         /**
  845.      * Guesses the version of an ISBN
  846.      *
  847.      * @param string $isbn ISBN Number of which the Version to guess
  848.      *
  849.      * @return integer|falseVersion Value or false (ISBN_VERSION_NONE) if failed
  850.      * @access private
  851.      */
  852.     private static function _isbnVersionGuess($isbn)
  853.     {
  854.         $isbn = self::_normaliseISBN($isbn);
  855.         if ($isbn === false) {
  856.             return ISBN_VERSION_NONE;
  857.         }
  858.         if ( strlen($isbn) == 10) {
  859.             return ISBN_VERSION_ISBN_10;
  860.         } else {
  861.             return ISBN_VERSION_ISBN_13;
  862.         }
  863.     }
  864.     // }}}
  865.  
  866.     // {{{ _isbnVersionIs()
  867.         /**
  868.      * Validate an ISBN Version value
  869.      *
  870.      * @param mixed $ver version to be checked being a valid ISBN Version
  871.      *
  872.      * @return bool true if value is valid, false if not
  873.      *
  874.      * @access private
  875.      */
  876.     private static function _isbnVersionIs($ver)
  877.     {
  878.         if (is_bool($ver) === false && is_integer($ver) === false) {
  879.             return false;
  880.         }
  881.         switch ($ver) {
  882.         case ISBN_VERSION_NONE:
  883.         case ISBN_VERSION_UNKNOWN:
  884.         case ISBN_VERSION_ISBN_10:
  885.         case ISBN_VERSION_ISBN_13:
  886.         case ISBN_VERSION_ISBN_13_978:
  887.         case ISBN_VERSION_ISBN_13_979:
  888.             return true;
  889.  
  890.         default:
  891.             return false;
  892.  
  893.         }
  894.     }
  895.     // }}}
  896.  
  897.     // {{{ _isbnVersionIsValid()
  898.         /**
  899.      * Validate an ISBN value being a valid (identifyable -10 / -13) value
  900.      *
  901.      * @param mixed $ver value to be checked being a valid ISBN Version
  902.      *
  903.      * @return bool true if value is valid, false if not
  904.      *
  905.      * @access private
  906.      */
  907.     private static function _isbnVersionIsValid($ver)
  908.     {
  909.         $r = self::_isbnVersionIs($ver);
  910.         if ($r === false) {
  911.             return false;
  912.         }
  913.  
  914.         switch ($ver) {
  915.         case ISBN_VERSION_ISBN_10:
  916.         case ISBN_VERSION_ISBN_13_978:
  917.             return true;
  918.         default:
  919.             return false;
  920.         }
  921.     }
  922.     // }}}
  923.  
  924.     // {{{ _normaliseISBN()
  925.         /**
  926.      * downformat "any" ISBN Number to the very basics
  927.      * an isbn number is a 10 or 13 digit. with the
  928.      * 10 digit string, the last digit can be 0-9 and
  929.      * X as well, all other are 0-9 only
  930.      * additionally this fucntion can be used to validate
  931.      * the isbn against correct length and chars
  932.      *
  933.      * @param string $isbn ISBN String to normalise
  934.      *
  935.      * @return string|falsenormalised ISBN Number or false if the function was
  936.      *                         not able to normalise the input
  937.      *
  938.      * @access private
  939.      */
  940.     private static function _normaliseISBN($isbn)
  941.     {
  942.         /* validate input */
  943.         $r = settype($isbn, 'string');
  944.         if ($r === false) {
  945.             return false;
  946.         }
  947.  
  948.         /* normalize (trim & case)*/
  949.         $isbn = trim($isbn);
  950.         $isbn = strtoupper($isbn);
  951.  
  952.         /* remove lang specific prefix (if any) */
  953.         $isbn = self::_normaliseISBNremoveLangSpecific($isbn);
  954.  
  955.         /* remove ISBN-10: or ISBN-13: prefix (if any) */
  956.         if (strlen($isbn > 8)) {
  957.             $prefix = substr($isbn, 0, 8);
  958.             if ($prefix == 'ISBN-10:' || $prefix == 'ISBN-13:') {
  959.                 $isbn = substr($isbn, 8);
  960.                 $isbn = ltrim($isbn);
  961.             }
  962.         }
  963.  
  964.         /* remove lang specific prefix again (if any) */
  965.         $isbn = self::_normaliseISBNremoveLangSpecific($isbn);
  966.  
  967.         /* remove "ISBN" prefix (if any)*/
  968.         if (substr($isbn, 0, 4) == 'ISBN') {
  969.             $isbn = substr($isbn, 4);
  970.         }
  971.  
  972.         /* remove cosmetic chars and different type of spaces */
  973.         $isbn = str_replace(array('-', ' ', '\t', '\n'), '', $isbn);
  974.  
  975.         /* take the length to check and differ between versions
  976.          * sothat a syntaxcheck can be made */
  977.         $l = strlen($isbn);
  978.         if ($l != 10 && $l != 13) {
  979.             return false;
  980.         } elseif ($l == 10) {
  981.             if (!preg_match('/^[0-9]{9}[0-9X]$/', $isbn)) {
  982.                 return false;
  983.             }
  984.         } elseif ($l == 13) {
  985.             if (!ctype_digit($isbn)) {
  986.                 return false;
  987.             }
  988.         }
  989.         return $isbn;
  990.     }
  991.     // }}}
  992.  
  993.     // {{{ _normaliseISBNremoveLangSpecific()
  994.         /**
  995.      * helper function for _normaliseISBN to
  996.      * remove lang sepcific ISBN prefix
  997.      *
  998.      * @param string $isbn ISBN String to check (partially normalised)
  999.      *
  1000.      * @return string   input value passed through helper
  1001.      *
  1002.      * @access private
  1003.      */
  1004.     private static function _normaliseISBNremoveLangSpecific($isbn)
  1005.     {
  1006.         $l    = strlen($lang);
  1007.         if ($l > 0 ) {
  1008.             if (substr($isbn, 0, $l) == $lang) {
  1009.                 $isbn = substr($isbn, $l);
  1010.             }
  1011.         }
  1012.         return $isbn;
  1013.     }
  1014.     // }}}
  1015.  
  1016.     // {{{ convert()
  1017.         /**
  1018.      * converts an ISBN number from one version to another
  1019.      * can convert ISBN-10 to ISBN-13 and ISBN-13 to ISBN-10
  1020.      * 
  1021.      * @param string  $isbnin  ISBN to convert, must be a valid ISBN Number
  1022.      * @param integer $verfrom version value of the input ISBN
  1023.      * @param integer $verto   version value to convert to
  1024.      *
  1025.      * @return string|falseconverted ISBN Number or false if conversion failed
  1026.      */
  1027.     public static function convert($isbnin, $verfrom = ISBN_VERSION_ISBN_10, 
  1028.                                    $verto = ISBN_VERSION_ISBN_13)
  1029.     {
  1030.         /* validate input */
  1031.         if (!self::_isbnVersionIsValid($verfrom)) {
  1032.             return false;
  1033.         }
  1034.         if (!self::_isbnVersionIsValid($verto)) {
  1035.             return false;
  1036.         }
  1037.         $r = self::validate($isbnin, $verfrom);
  1038.         if ($r === false) {
  1039.             return false;
  1040.         }
  1041.         /* normalize input */
  1042.         $isbnn = self::_normaliseISBN($isbnin);
  1043.         /* input is ok now, let's convert */
  1044.         switch(true) {
  1045.         case $verfrom == ISBN_VERSION_ISBN_10 && $verto == ISBN_VERSION_ISBN_13:
  1046.             /* convert 10 to 13 */
  1047.             $isbnbody = self::_extractISBNBody($isbnn);
  1048.             if ($isbnbody === false) {
  1049.                 return false;
  1050.             }
  1051.             $isbnout = '978' . $isbnbody . self::_checkdigitISBN13($isbnbody);
  1052.             return $isbnout;
  1053.         case $verfrom == ISBN_VERSION_ISBN_13 && $verto == ISBN_VERSION_ISBN_10:
  1054.             /* convert 13 to 10 */
  1055.             $isbnbody = self::_extractISBNBody($isbnn);
  1056.             if ($isbnbody === false) {
  1057.                 return false;
  1058.             }
  1059.             $isbnout = $isbnbody . self::_checkdigitISBN10($isbnbody);
  1060.             return $isbnout;
  1061.         case $verfrom == $verto:
  1062.             /* version is the same so there is no need to convert */
  1063.             /* hej, praktisch! */           
  1064.             return $isbnn;              
  1065.         }        
  1066.         return false;
  1067.     }
  1068.     // }}}
  1069.  
  1070.     // {{{ getCheckdigit()
  1071.         /**
  1072.      * Get the Checkdigit Part of ISBN Number
  1073.      *
  1074.      * @return string|falseCheckdigit or false if failed
  1075.      */
  1076.     public function getCheckdigit()
  1077.     {
  1078.         $ver   = $this->getVersion();
  1079.         $check = false;
  1080.  
  1081.         switch ($ver) {
  1082.         case ISBN_VERSION_ISBN_10:
  1083.             $check = self::_checkdigitISBN10($this->_getISBNBody());
  1084.             break;
  1085.  
  1086.         case ISBN_VERSION_ISBN_13:
  1087.             $check = self::_checkdigitISBN13($this->_getISBNBody());
  1088.             break;
  1089.  
  1090.         }
  1091.  
  1092.         return $check;
  1093.     }
  1094.     // }}}
  1095.  
  1096.     // {{{ getEAN()
  1097.         /**
  1098.      * Get the EAN Prefix of ISBN Number (ISBN-13)
  1099.      *
  1100.      * @return string|falseEAN Prefix or false if failed
  1101.      */
  1102.     public function getEAN()
  1103.     {
  1104.         $ver = $this->getVersion();
  1105.         if ($ver === false ) {
  1106.             return false;
  1107.         }
  1108.         if ($ver == ISBN_VERSION_ISBN_13_978) {
  1109.             return '978';
  1110.         }
  1111.         if ($ver == ISBN_VERSION_ISBN_13_979) {
  1112.             return '979';
  1113.         }
  1114.         return '';
  1115.     }
  1116.     // }}}
  1117.  
  1118.     // {{{ getGroup()
  1119.         /**
  1120.      * Get the Registrationgroup Part of the ISBN Number
  1121.      *
  1122.      * @return string|falseGroup Identifier or false if failed
  1123.      */
  1124.     public function getGroup()
  1125.     {
  1126.         return $this->isbn_group;
  1127.     }
  1128.     // }}}
  1129.  
  1130.     // {{{ _setGroup()
  1131.         /**
  1132.      * Setter for the Registrationgroup Part of the ISBN Number
  1133.      *
  1134.      * @param string $group Registrationsgroup to set
  1135.      * 
  1136.      * @return void 
  1137.      * 
  1138.      * @throws ISBN_Exception in case it fails
  1139.      */
  1140.     private function _setGroup($group)
  1141.     {
  1142.         if (is_string($group) == false) {
  1143.             throw new ISBN_Exception('Wrong Vartype');
  1144.         }
  1145.         $l = strlen($group);
  1146.         if ($l < 1 || $l > 5) {
  1147.             throw new ISBN_Exception('Wrong Group Length (' . $l . ')');
  1148.         }
  1149.         $testbody  = substr($group . '000000000', 0, 9);
  1150.         $testgroup = self::_extractGroup($testbody);
  1151.         if ($testgroup === false ) {
  1152.             throw new ISBN_Exception('Invalid Group');
  1153.         }
  1154.         if ($testgroup != $group) {
  1155.             throw new ISBN_Exception('Invalid Group');
  1156.         }        
  1157.         $this->isbn_group = $group;
  1158.     }
  1159.  
  1160.     // {{{ getISBN()
  1161.         /**
  1162.      * Get whole ISBN Number
  1163.      *
  1164.      * @return string ISBN Number (unformatted); empty string if this is
  1165.      *                 not a valid ISBN
  1166.      */
  1167.     public function getISBN()
  1168.     {
  1169.         $ver = $this->getVersion();
  1170.         if ($ver === false ) {
  1171.             return '';
  1172.         }
  1173.  
  1174.         $isbn = '';
  1175.  
  1176.         $r = self::_isbnVersionIsValid($ver);
  1177.         if ($r === false ) {
  1178.             return $isbn;
  1179.         }
  1180.  
  1181.         $isbn .= $this->getEAN();
  1182.         $isbn .= $this->_getISBNBody();
  1183.         $isbn .= $this->getCheckdigit();
  1184.  
  1185.         return $isbn;
  1186.     }
  1187.     // }}}
  1188.     
  1189.     // {{{ getISBNDisplayable()
  1190.         /**
  1191.      * Get whole ISBN Number in a displayable fashion (see Handbook p. 15)
  1192.      *
  1193.      * @param string $format Formatstring 1-4 Chars:
  1194.      *                each character is a control char:
  1195.      *                #1 i or not: use international pre-prefix
  1196.      *                #2 i or not: "ISBN" in front or v: incl. version
  1197.      *                #3 : or not: insert a ":"
  1198.      *                #4 - or not: use - after EAN (ISBN 13 only)
  1199.      *                #4 or =: use - between each ISBN part
  1200.      *                Example 1:
  1201.      *                   '   --' 978-0-385-33941-4
  1202.      *                   classic displayable ISBN
  1203.      *                Example 2:
  1204.      *                   ' v:-' ISBN-13: 978-0385339414
  1205.      *                   ISBN-Format used by amazon
  1206.      *                Example 3:
  1207.      *                   'iv:=' ISBN-13: 978-0-385-33941-4
  1208.      *                   full blown: more is more!
  1209.      *   
  1210.      * @return string ISBN Number (formatted); empty string if this is
  1211.      *                 not a valid ISBN
  1212.      */    
  1213.     public function getISBNDisplayable($format = '')
  1214.     {
  1215.         if ( strlen($format)==0 ) {
  1216.             $format = 'iv:='; //edit $this->ISBNFormatstring;
  1217.         }
  1218.         $format = substr($format . '    ', 0, 4);
  1219.         
  1220.         $ver = $this->getVersion();
  1221.         if ($ver === false ) {
  1222.             return '';
  1223.         }
  1224.  
  1225.         $isbn = '';
  1226.  
  1227.         $r = self::_isbnVersionIsValid($ver);
  1228.         if ($r === false ) {
  1229.             return $isbn;
  1230.         }
  1231.  
  1232.         if ($format[0] == 'i') {
  1233.             $isbn .= ISBN_DEFAULT_PRINT_LANG_SPECIFIC_PREFIX;
  1234.             if (strlen($isbn)) $isbn .= ' ';
  1235.         }
  1236.         
  1237.         if ($format[1] == 'i' || $format[1] == 'v') {
  1238.             $isbn .= 'ISBN';
  1239.             if ($format[1] == 'v') {
  1240.                 switch ($ver) {
  1241.                 case ISBN_VERSION_ISBN_10:
  1242.                     $isbn .= '-10';
  1243.                     break;
  1244.                 case ISBN_VERSION_ISBN_13:
  1245.                     $isbn .= '-13';
  1246.                     break;
  1247.                 }               
  1248.             }
  1249.         }
  1250.         
  1251.         if ($format[2] == ':') {
  1252.             $isbn .= ':';
  1253.         }
  1254.         
  1255.         if (strlen($isbn)) {
  1256.             $isbn .= ' ';
  1257.         }
  1258.         
  1259.         if ($ver == ISBN_VERSION_ISBN_13_978 || $ver == ISBN_VERSION_ISBN_13_979) {
  1260.             $isbn .= $this->getEAN();
  1261.             if ($format[3] == '-' || $format[3] == '=') {
  1262.                 $isbn .= ISBN_DEFAULT_COSMETIC_SEPERATOR;
  1263.             }
  1264.         }
  1265.         
  1266.         $seperator = ($format[3] == '=') ? ISBN_DEFAULT_COSMETIC_SEPERATOR : '';
  1267.                 
  1268.         $isbn .= $this->getGroup() . $seperator;
  1269.         $isbn .= $this->getPublisher() . $seperator;
  1270.         $isbn .= $this->getTitle() . $seperator;        
  1271.         $isbn .= $this->getCheckdigit();
  1272.  
  1273.         return $isbn;
  1274.         
  1275.     }
  1276.     // }}}
  1277.  
  1278.     // {{{ setISBN()
  1279.         /**
  1280.      * Setter for ISBN
  1281.      *
  1282.      * @param string $isbn ISBN Number
  1283.      *           this is a valid ISBN Number or it is an Empty string
  1284.      *           which will reset the class
  1285.      * 
  1286.      * @return void 
  1287.      * 
  1288.      * @throws ISBN_Exception in case it fails
  1289.      * 
  1290.      */
  1291.     public function setISBN($isbn)
  1292.     {
  1293.         if ($isbn == '') {
  1294.             $this->ver            = ISBN_VERSION_NONE;
  1295.             $this->isbn_group     = '';
  1296.             $this->isbn_publisher = '';
  1297.             $this->isbn_title     = '';
  1298.         } else {
  1299.             $isbnn = self::_normaliseISBN($isbn);
  1300.             $ver   = self::_getVersion($isbnn);
  1301.             if ($ver === false) {
  1302.                 throw new ISBN_Exception('Invalid ISBN');
  1303.             }
  1304.             if ($ver != $this->ver and $this->ver !== ISBN_VERSION_NONE) {
  1305.                 throw new ISBN_Exception(
  1306.                   'ISBN Version of passed ISBN (' . $ver . ') '.
  1307.                   'does not match existing (' . $this->ver . ').'
  1308.                 );
  1309.             } elseif ($this->ver === ISBN_VERSION_NONE) {
  1310.                 $this->ver = $ver;
  1311.             }
  1312.             $body = self::_extractISBNBody($isbnn);
  1313.             if ($body === false) {
  1314.                 throw new ISBN_Exception('Invalid ISBN (could not extract body)');
  1315.             }
  1316.             try {
  1317.                 $this->_setISBNBody($body);
  1318.             } catch (ISBN_Exception $e) {
  1319.                 throw new ISBN_Exception(
  1320.                     'Invalid ISBN (invalid body "' . $body . '")', $e
  1321.                 );
  1322.             }
  1323.         }
  1324.     }
  1325.     // }}}
  1326.  
  1327.     // {{{ _getISBNBody()
  1328.         /**
  1329.      * _getISBNBody()
  1330.      *
  1331.      * @return string ISBN Body (not an offical term)
  1332.      */
  1333.     private function _getISBNBody()
  1334.     {
  1335.         $body  = '';
  1336.         $body .= $this->getGroup();
  1337.         $body .= $this->_getISBNSubbody();
  1338.         return $body;
  1339.     }
  1340.     // }}}
  1341.  
  1342.     // {{{ _setISBNBody()
  1343.         /**
  1344.      * _setISBNBody()
  1345.      *
  1346.      * Setter for ISBNBody
  1347.      *
  1348.      * @param string $body ISBNBody
  1349.      * 
  1350.      * @return void 
  1351.      * 
  1352.      * @throws ISBN_Exception in case it fails
  1353.      */
  1354.     private function _setISBNBody($body)
  1355.     {
  1356.         /* validate parameter */
  1357.         if (is_string($body) == false) {
  1358.             throw new ISBN_Exception('Not a Body: wrong variabletype');
  1359.         }
  1360.         if (strlen($body) != 9) {
  1361.             throw new ISBN_Exception('Not a Body: wrong body length');
  1362.         }
  1363.         if (ctype_digit($body) !== true) {
  1364.             throw new ISBN_Exception('Not a Body: syntactically not a body');
  1365.         }
  1366.  
  1367.         /* validate body by extracting and validating parts */
  1368.         $group   = false;
  1369.         $subbody = false;
  1370.  
  1371.         $r = self::_isbnBodyParts($body, $group, $subbody);
  1372.         if ($r == false) {
  1373.             throw new ISBN_Exception('Invalid Body');
  1374.         }
  1375.         
  1376.         try {
  1377.             $this->_setGroup($group);
  1378.         } catch (ISBN_Exception $e) {
  1379.             throw new Exception('Invalid Body: Group is invalid', $e);
  1380.         }
  1381.  
  1382.         try {
  1383.             $this->_setISBNSubbody($subbody);
  1384.         } catch (ISBN_Exception $e) {
  1385.             throw new ISBN_Exception(
  1386.                 'Invalid Body: Subbody is invalid (' . $e->getMessage() . ')', $e
  1387.             );
  1388.         }
  1389.     }
  1390.     // }}}
  1391.  
  1392.     // {{{ _getISBNSubbody()
  1393.         /**
  1394.      * Get ISBNSubbody ()
  1395.      *
  1396.      * @return ISBN Subbody
  1397.      */
  1398.     private function _getISBNSubbody()
  1399.     {
  1400.         $subbody  = '';
  1401.         $subbody .= $this->getPublisher();
  1402.         $subbody .= $this->getTitle();
  1403.         return $subbody;
  1404.     }
  1405.     // }}}
  1406.  
  1407.     // {{{ _setISBNSubbody()
  1408.         /**
  1409.      * _setISBNSubbody
  1410.      *
  1411.      * Setter for the ISBN Subbody
  1412.      *
  1413.      * @param string $subbody ISBN Subbody
  1414.      * 
  1415.      * @return void 
  1416.      * 
  1417.      * @throws ISBN_Exception in case it fails
  1418.      */
  1419.     private function _setISBNSubbody($subbody)
  1420.     {
  1421.         /* validate parameter */
  1422.         if (is_string($subbody) == false) {
  1423.             throw new ISBN_Exception('Wrong Vartype');
  1424.         }
  1425.         $l = strlen($subbody);
  1426.         if ( $l < 4 || $l > 8) {
  1427.             throw new ISBN_Exception('Not a Subbody by length');
  1428.         }
  1429.         /* validate by setting apart */
  1430.         $registrant  = false;
  1431.         $publication = false;
  1432.         $groupid     = intval($this->isbn_group);
  1433.         
  1434.         $r = self::_isbnSubbodyParts($subbody, $groupid, $registrant, $publication);
  1435.         if ($r === false) {
  1436.             throw new ISBN_Exception('Can\'t break');
  1437.         }
  1438.         /* edit+ setter/getter for Registrant/Publisher and Title/Publication */
  1439.         $this->isbn_publisher = $registrant;
  1440.         $this->isbn_title     = $publication;
  1441.     }
  1442.  
  1443.     // {{{ getPublisher()
  1444.         /**
  1445.      * Get the Publication Part of the ISBN Number
  1446.      *
  1447.      * @return string|falsePublisher or false if failed
  1448.      */
  1449.     public function getPublisher()
  1450.     {
  1451.         return $this->isbn_publisher;
  1452.     }
  1453.     // }}}
  1454.  
  1455.     // {{{ getTitle()
  1456.         /**
  1457.      * Get the Title Part of the ISBN Number
  1458.      *
  1459.      * @return string|falseTitle or false if failed
  1460.      */
  1461.     public function getTitle()
  1462.     {
  1463.         return $this->isbn_title;
  1464.     }
  1465.     // }}}
  1466.  
  1467.  
  1468.     // {{{ isValid()
  1469.         /**
  1470.      * Returns this ISBN validity
  1471.      *
  1472.      * @return boolean 
  1473.      */
  1474.     public function isValid()
  1475.     {
  1476.         $isbn = $this->getISBN();
  1477.         $r    = self::validate($this->getISBN(), $this->getVersion());
  1478.         return (bool) $r;
  1479.     }
  1480.  
  1481.     // {{{ validate()
  1482.         /**
  1483.      * Validates an ISBN
  1484.      * 
  1485.      * @param string  $isbn ISBN to validate
  1486.      * @param integer $ver  ISBN-Version to validate against
  1487.      *
  1488.      * @return integer|false   Version value of a valid ISBN or false
  1489.      *                           if it did not validate
  1490.      */
  1491.     public static function validate($isbn, $ver = ISBN_DEFAULT_INPUTVERSION)
  1492.     {
  1493.         $r = self::_isbnVersionIs($ver);
  1494.         if ($r === false) {
  1495.             return false;
  1496.         }
  1497.         if ($ver === ISBN_VERSION_UNKNOWN) {
  1498.             $ver = self::_isbnVersionGuess($isbn);
  1499.         }
  1500.         if (self::_isbnVersionIsValid($ver) === false) {
  1501.             return false;
  1502.         }
  1503.         $r = self::_isIsbnValid($isbn, $ver);
  1504.         if ($r === false) {
  1505.             return false;
  1506.         }
  1507.         return $ver;
  1508.     }
  1509.     // }}}
  1510.  
  1511.     // {{{ getVersion()
  1512.         /**
  1513.      * Returns version of this objects ISBN
  1514.      *
  1515.      * @return integer|false Version value or ISBN_VERSION_NONE
  1516.      */
  1517.     public function getVersion()
  1518.     {
  1519.         return $this->ver;
  1520.     }
  1521.  
  1522.  
  1523.     // {{{ guessVersion()
  1524.         /**
  1525.      * Guesses ISBN version of passed string
  1526.      *
  1527.      * Note: This is not Validation. To get the validated
  1528.      * version of an ISBN Number use self::validate();
  1529.      *     
  1530.      * @param string $isbn ISBN Number to guess Version of
  1531.      * 
  1532.      * @return integer|false   Version Value or false if failed
  1533.      * 
  1534.      * @see validate();
  1535.      */
  1536.     public static function guessVersion($isbn)
  1537.     {
  1538.         $r = self::_isbnVersionGuess($isbn);
  1539.         return $r;
  1540.     }
  1541.     // }}}
  1542.  
  1543. }
  1544. ?>

Documentation generated on Tue, 06 Mar 2007 01:03:06 +0100 by phpDocumentor 1.3.1