eZ Publish  [4.0]
ezisbn13.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Created on: <17-Apr-2007 11:07:06 bjorn>
00004 //
00005 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00006 // SOFTWARE NAME: eZ Publish
00007 // SOFTWARE RELEASE: 4.0.x
00008 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS
00009 // SOFTWARE LICENSE: GNU General Public License v2.0
00010 // NOTICE: >
00011 //   This program is free software; you can redistribute it and/or
00012 //   modify it under the terms of version 2.0  of the GNU General
00013 //   Public License as published by the Free Software Foundation.
00014 //
00015 //   This program is distributed in the hope that it will be useful,
00016 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 //   GNU General Public License for more details.
00019 //
00020 //   You should have received a copy of version 2.0 of the GNU General
00021 //   Public License along with this program; if not, write to the Free
00022 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00023 //   MA 02110-1301, USA.
00024 //
00025 //
00026 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00027 //
00028 
00029 /*! \file ezisbn13.php
00030 */
00031 
00032 /*!
00033   \class eZISBN13 ezisbn13.php
00034   \brief The class eZISBN13 handles ISBN-13 numbers.
00035 
00036   The class is containing an ISBN-13 number and extracts the different groups
00037   based on the information stored in the different ranges for Registration group
00038   and Registration elements. The Publication element will get the space left available.
00039 */
00040 
00041 //include_once( 'kernel/classes/datatypes/ezisbn/ezisbngroup.php' );
00042 //include_once( 'kernel/classes/datatypes/ezisbn/ezisbngrouprange.php' );
00043 //include_once( 'kernel/classes/datatypes/ezisbn/ezisbnregistrantrange.php' );
00044 
00045 class eZISBN13
00046 {
00047     const PREFIX_LENGTH = 3;
00048     const CHECK_LENGTH = 1;
00049     const LENGTH = 13;
00050     const PREFIX_978 = 978;
00051     const PREFIX_979 = 979;
00052 
00053     /*!
00054      Constructor
00055      \param $isbnNr is the ISBN-13 number. example is: 978-0-11-000222-4
00056      \param $separator is the hyphen used in the ISBN number to make the
00057                        ISBN number more visible.
00058     */
00059     function eZISBN13( $isbnNr = null, $separator = '-' )
00060     {
00061         if ( $isbnNr !== null )
00062         {
00063             $this->extractISBNNumber( $isbnNr, $separator );
00064         }
00065         else
00066         {
00067             $this->Prefix = false;
00068             $this->RegistrationGroup = false;
00069             $this->RegistrantElement = false;
00070             $this->PublicationElement = false;
00071             $this->CheckDigit = false;
00072         }
00073     }
00074 
00075     /*!
00076      Contains a list of all attributes for this class.
00077      \return the array with existing attributes.
00078     */
00079     function attributes()
00080     {
00081         return array( 'has_content',
00082                       'group_ranges',
00083                       'groups' );
00084     }
00085 
00086     /*!
00087      Fetch the attribute sent in $value.
00088      \param $value is the name of the attribute that should be fetched.
00089      \return the result of the attribute.
00090     */
00091     function attribute( $value )
00092     {
00093         switch ( $value )
00094         {
00095             case "has_content":
00096             {
00097                 return eZISBN13::hasRangeData();
00098             }break;
00099 
00100             case "groups":
00101             {
00102                 $groupList = eZISBNGroup::fetchList();
00103                 return array( 'group_list' => $groupList,
00104                               'count' => count(  $groupList ) );
00105             }break;
00106 
00107             case "group_ranges":
00108             {
00109                 $groupList = eZISBNGroupRange::fetchList();
00110                 return array( 'group_list' => $groupList,
00111                               'count' => count( $groupList ) );
00112             }break;
00113         }
00114         return null;
00115     }
00116 
00117     /*!
00118      Check if the attribute set in the string $value exists.
00119      \param $value is the attribute you want to see if exist.
00120      \return true if the attribute is found.
00121     */
00122     function hasAttribute( $value )
00123     {
00124         return in_array( $value, eZISBN13::attributes() );
00125     }
00126 
00127     /*!
00128      Check if any ISBN ranges exist.
00129      \return true if any ranges are found.
00130     */
00131     function hasRangeData()
00132     {
00133         $db = eZDB::instance();
00134         $tableList = $db->eZTableList();
00135         if ( array_key_exists( 'ezisbn_group', $tableList ) and
00136              array_key_exists( 'ezisbn_group_range', $tableList ) and
00137              array_key_exists( 'ezisbn_registrant_range', $tableList ) )
00138         {
00139             $query = "SELECT count( ezisbn_group.id ) AS count
00140                       FROM ezisbn_group, ezisbn_group_range, ezisbn_registrant_range
00141                       WHERE ezisbn_group.group_number >= ezisbn_group_range.from_number AND
00142                             ezisbn_group.group_number <= ezisbn_group_range.to_number AND
00143                             ezisbn_group.id=ezisbn_registrant_range.isbn_group_id";
00144             $countArray = $db->arrayQuery( $query );
00145             return ( $countArray[0]['count'] > 0 );
00146         }
00147         else
00148             return false;
00149     }
00150 
00151     /*!
00152      Receives an ISBN number and place hyphen on the correct place in the number.
00153      If the placement is not found, an error message will be set and false
00154 
00155      The different parts of the ISBN-13 number will be stored in separate class variables.
00156 
00157      \param $isbnNr is the ISBN-13 number. Should be 13 digits long and may contain space or hyphen as separator.
00158      \param $error is used to send back an error message that will be shown to the user if the ISBN number was
00159                    not extracted correctly.
00160      \param $separator is the separator used to make the ISBN number visible. Could be either a space or hyphen.
00161      \return A formated ISBN number or the original value if it was not possible to find the structure.
00162     */
00163     function formatedISBNValue( $isbnNr = false, &$error, $separator = '-' )
00164     {
00165         if ( $isbnNr !== false )
00166         {
00167             $formatedISBN13 = preg_replace( "/[\s|\-]+/", "-", $isbnNr );
00168             $status = $this->extractISBNNumber( $isbnNr, $error );
00169 
00170             if ( $status === false )
00171             {
00172                 $formatedISBN13 = substr( $isbnNr, 0, self::PREFIX_LENGTH );
00173                 if ( strlen( $this->RegistrationGroup ) > 0 )
00174                 {
00175                     $formatedISBN13 .= $separator . $this->RegistrationGroup;
00176                     if ( strlen( $this->RegistrantElement ) > 0 )
00177                     {
00178                         $formatedISBN13 .= $separator . $this->RegistrantElement . $separator .
00179                                            $this->PublicationElement . $separator;
00180                     }
00181                     else
00182                     {
00183                         $offset = strlen( $this->RegistrationGroup ) + self::PREFIX_LENGTH;
00184                         $length = strlen( $isbnNr ) - $offset - self::CHECK_LENGTH;
00185                         $originalValue = substr( $isbnNr, $offset, $length );
00186                         $formatedISBN13 .= $originalValue;
00187                     }
00188                 }
00189                 else
00190                 {
00191                     $offset = self::PREFIX_LENGTH;
00192                     $length = strlen( $isbnNr ) - $offset - self::CHECK_LENGTH;
00193                     $originalValue = substr( $isbnNr, $offset, $length );
00194                     $formatedISBN13 .= $originalValue;
00195                 }
00196 
00197                 $length = strlen( $isbnNr );
00198                 $formatedISBN13 .= substr( $isbnNr, $length - self::CHECK_LENGTH, $length );
00199                 return $formatedISBN13;
00200             }
00201         }
00202         else
00203         {
00204             $formatedISBN13 = $this->Prefix . $separator;
00205             if ( strlen( $this->RegistrationGroup ) > 0 )
00206             {
00207                 $formatedISBN13 .= $this->RegistrationGroup . $separator;
00208                 if ( strlen( $this->RegistrantElement ) > 0 )
00209                 {
00210                     $formatedISBN13 .= $this->RegistrantElement . $separator .
00211                          $this->PublicationElement . $separator;
00212                 }
00213                 else
00214                 {
00215                     $formatedISBN13 .= $this->RegistrantElement .
00216                          $this->PublicationElement . $separator;
00217                 }
00218             }
00219             else
00220             {
00221                 $formatedISBN13 .= $this->RegistrationGroup .
00222                      $this->RegistrantElement .
00223                      $this->PublicationElement . $separator;
00224             }
00225             $formatedISBN13 .= $this->CheckDigit;
00226         }
00227 
00228         if ( strlen( $this->Prefix . $this->RegistrationGroup . $this->RegistrantElement . $this->PublicationElement . $this->CheckDigit ) == self::LENGTH )
00229         {
00230             $formatedISBN13 = $this->Prefix . $separator .
00231                  $this->RegistrationGroup . $separator .
00232                  $this->RegistrantElement . $separator .
00233                  $this->PublicationElement . $separator .
00234                  $this->CheckDigit;
00235         }
00236         return $formatedISBN13;
00237     }
00238 
00239     /*!
00240       Extracts the ISBN-13 number and are setting the class variables for the different
00241       parts when the value is found. The class variables should be set as default false
00242       in the constructor.
00243 
00244       \param $isbnNr is the ISBN-13 number. Should be 13 digits long and may contain space or hyphen as separator.
00245       \param $error is used to send back an error message that will be shown to the user if the ISBN number was
00246                     not extracted correctly.
00247 
00248       \return true if the ISBN-13 number was successfully extracted and false if not.
00249     */
00250     function extractISBNNumber( $isbnNr = false, &$error )
00251     {
00252         $ini = eZINI::instance( 'content.ini' );
00253         $ean = preg_replace( "/[\s|\-]+/", "", $isbnNr );
00254         if ( is_numeric( $ean ) and strlen( $ean ) == self::LENGTH )
00255         {
00256             $prefix = substr( $ean, 0, self::PREFIX_LENGTH );
00257             $this->Prefix = $prefix;
00258 
00259             $checkDigit = substr( $ean, 12, self::CHECK_LENGTH );
00260             $this->CheckDigit = $checkDigit;
00261             if ( $prefix == self::PREFIX_978 )
00262             {
00263                 $registrantValue = false;
00264                 $groupValue = false;
00265                 $publicationValue = false;
00266                 $checkDigit = false;
00267 
00268                 $groupRange = eZISBNGroupRange::extractGroup( $ean );
00269                 $groupLength = false;
00270                 if ( $groupRange )
00271                 {
00272                     $groupLength = $groupRange->attribute( 'group_length' );
00273                 }
00274 
00275                 if ( $groupLength )
00276                 {
00277                     $groupValue = substr( $ean, self::PREFIX_LENGTH, $groupLength );
00278                     $this->RegistrationGroup = $groupValue;
00279 
00280                     $group = eZISBNGroup::fetchByGroup( $groupValue );
00281                     if ( $group instanceof eZISBNGroup )
00282                     {
00283                         $registrant = eZISBNRegistrantRange::extractRegistrant( $ean, $group, $groupRange, $registrantLength );
00284                         if ( $registrant instanceof eZISBNRegistrantRange and
00285                              $registrantLength > 0 )
00286                         {
00287                             $registrantOffset = self::PREFIX_LENGTH + $groupLength;
00288                             $registrantValue = substr( $ean, $registrantOffset, $registrantLength );
00289 
00290                             $this->RegistrantElement = $registrantValue;
00291 
00292                             $publicationOffset = $registrantOffset + $registrantLength;
00293                             $publicationLength = 12 - $publicationOffset;
00294                             $publicationValue = substr( $ean, $publicationOffset, $publicationLength );
00295                             $this->PublicationElement = $publicationValue;
00296                         }
00297                         else
00298                         {
00299                             $strictValidation = $ini->variable( 'ISBNSettings', 'StrictValidation' );
00300                             if ( $strictValidation == 'true' )
00301                             {
00302                                 $error = ezi18n( 'kernel/classes/datatypes', 'The registrant element of the ISBN number does not exist.' );
00303                                 return false;
00304                             }
00305                         }
00306                     }
00307                     else
00308                     {
00309                         $strictValidation = $ini->variable( 'ISBNSettings', 'StrictValidation' );
00310                         if ( $strictValidation == 'true' )
00311                         {
00312                             $error = ezi18n( 'kernel/classes/datatypes', 'The ISBN number has a incorrect registration group number.' );
00313                             return false;
00314                         }
00315                     }
00316                 }
00317                 else
00318                 {
00319                     $strictValidation = $ini->variable( 'ISBNSettings', 'StrictValidation' );
00320                     if ( $strictValidation == 'true' )
00321                     {
00322                         $error = ezi18n( 'kernel/classes/datatypes', 'The group element of the ISBN number does not exist.' );
00323                         return false;
00324                     }
00325                 }
00326             }
00327             else
00328             {
00329                 $strictValidation = $ini->variable( 'ISBNSettings', 'StrictValidation' );
00330                 if ( $strictValidation == 'true' )
00331                 {
00332                     $error = ezi18n( 'kernel/classes/datatypes', '%1 is not a valid prefix of the ISBN number.', null, array( $prefix ) );
00333                     return false;
00334                 }
00335             }
00336         }
00337         else
00338         {
00339             $error = ezi18n( 'kernel/classes/datatypes', 'All ISBN 13 characters need to be numeric' );
00340             return false;
00341         }
00342         return true;
00343     }
00344 
00345     /*!
00346      Validates the ISBN-13 number \a $isbnNr.
00347      \param $isbnNr A string containing the number without any dashes.
00348      \param $error is used to send back an error message that will be shown to the user if the ISBN number was
00349                    not extracted correctly.
00350      \return \c true if it is valid.
00351     */
00352     function validate( $isbnNr, &$error )
00353     {
00354         $valid = $this->validateISBN13Checksum( $isbnNr, $error );
00355         if ( $valid == true )
00356         {
00357             $valid = $this->extractISBNNumber( $isbnNr, $error );
00358         }
00359         return $valid;
00360     }
00361 
00362     /*!
00363      \private
00364      Validates the ISBN-13 number \a $isbnNr.
00365      \param $isbnNr A string containing the number without any dashes.
00366      \param $error is used to send back an error message that will be shown to the user if the
00367                    ISBN number validated.
00368      \return \c true if it is valid.
00369     */
00370     function validateISBN13Checksum ( $isbnNr, &$error )
00371     {
00372         if ( !$isbnNr )
00373             return false;
00374         $isbnNr = preg_replace( "/[\s|\-]+/", "", $isbnNr );
00375         if ( substr( $isbnNr, 0, self::PREFIX_LENGTH ) != self::PREFIX_978 and
00376              substr( $isbnNr, 0, self::PREFIX_LENGTH ) != self::PREFIX_979 )
00377         {
00378             $error = ezi18n( 'kernel/classes/datatypes',
00379                              '13 digit ISBN must start with 978 or 979' );
00380             return false;
00381         }
00382 
00383         $checksum13 = 0;
00384         $weight13 = 1;
00385         if ( strlen( $isbnNr ) != self::LENGTH )
00386         {
00387             $error = ezi18n( 'kernel/classes/datatypes', 'ISBN length is invalid' );
00388             return false;
00389         }
00390 
00391         //compute checksum
00392         $val = 0;
00393         for ( $i = 0; $i < self::LENGTH; $i++ )
00394         {
00395             $val = $isbnNr{$i};
00396             if ( !is_numeric( $isbnNr{$i} ) )
00397             {
00398                 $error = ezi18n( 'kernel/classes/datatypes', 'All ISBN 13 characters need to be numeric' );
00399                 return false;
00400             }
00401             $checksum13 = $checksum13 + $weight13 * $val;
00402             $weight13 = ( $weight13 + 2 ) % 4;
00403         }
00404         if ( ( $checksum13 % 10 ) != 0 )
00405         {
00406             // Calculate the last digit from the 12 first numbers.
00407             $checkDigit = ( 10 - ( ( $checksum13 - ( ( $weight13 + 2 ) % 4 ) * $val ) % 10 ) ) % 10;
00408             //bad checksum
00409             $error = ezi18n( 'kernel/classes/datatypes', 'Bad checksum, last digit should be %1', null, array( $checkDigit ) );
00410             return false;
00411         }
00412 
00413         return true;
00414     }
00415 
00416     public $Prefix;
00417     public $RegistrationGroup;
00418     public $RegistrantElement;
00419     public $PublicationElement;
00420     public $CheckDigit;
00421 }
00422 
00423 ?>