Current Path : /web/htdocs/www.entinoprofit.org/home/cf/
Upload File :
Current File : /web/htdocs/www.entinoprofit.org/home/cf/codicefiscale.class.php

<?php

/**
 * Classe di calcolo codice fiscale
 * by Michele Brodoloni <mik.linux@gmail.com>
 *
 * *** UPDATE 02/03/2011 ***
 * - Fixed bug #1 on date calculation
 * - Added database and sql file download link
 *
 * *** IMPORTANT NOTE ***
 * SQLite database file, as well as the text file containing the 
 * SQL statements to recreate the table, can be downloaded from:
 *
 *   http://www.studiojnj.it/download/codfisc.zip  
 *   (courtesy of Gianfranco Panico)
 *
 */
class codicefiscale {
    
    const ERR_SQLITE_OPEN  = 'Errore nell\'apertura del database SQLite dei codici catastali';
    const ERR_SQLITE_QUERY = 'La query per ottenere il codice catastale non ha prodotto risultati';
    const ERR_SQLITE_FILE  = 'File del database di SQLite non trovato o non leggibile';
    const ERR_GENERIC      = 'Errore di calcolo del codice fiscale.';

    /**
     * Array delle consonanti
     */
    protected $_consonanti = array(
        'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K',
        'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
        'V', 'W', 'X', 'Y', 'Z'
    );

    /**
     * Array delle vocali
     */
    protected $_vocali = array(
        'A', 'E', 'I', 'O', 'U'
    );

    /**
     * Array per il calcolo della lettera del mese
     * Al numero del mese corrisponde una lettera
     */
    protected $_mesi = array( 
        1  => 'A',  2 => 'B',  3 => 'C',  4 => 'D',  5 => 'E',  
        6  => 'H',  7 => 'L',  8 => 'M',  9 => 'P', 10 => 'R', 
        11 => 'S', 12 => 'T'
    );

    
    protected $_pari = array(
        '0' =>  0, '1' =>  1, '2' =>  2, '3' =>  3, '4' =>  4, 
        '5' =>  5, '6' =>  6, '7' =>  7, '8' =>  8, '9' =>  9,
        'A' =>  0, 'B' =>  1, 'C' =>  2, 'D' =>  3, 'E' =>  4, 
        'F' =>  5, 'G' =>  6, 'H' =>  7, 'I' =>  8, 'J' =>  9,
        'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, 'O' => 14, 
        'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19,
        'U' => 20, 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 
        'Z' => 25
    );

    protected $_dispari = array(  
        '0' =>  1, '1' =>  0, '2' =>  5, '3' =>  7, '4' =>  9,
        '5' => 13, '6' => 15, '7' => 17, '8' => 19, '9' => 21,
        'A' =>  1, 'B' =>  0, 'C' =>  5, 'D' =>  7, 'E' =>  9, 
        'F' => 13, 'G' => 15, 'H' => 17, 'I' => 19, 'J' => 21,
        'K' =>  2, 'L' =>  4, 'M' => 18, 'N' => 20, 'O' => 11, 
        'P' =>  3, 'Q' =>  6, 'R' =>  8, 'S' => 12, 'T' => 14,
        'U' => 16, 'V' => 10, 'W' => 22, 'X' => 25, 'Y' => 24, 
        'Z' => 23
    );

    protected $_controllo = array( 
        '0'  => 'A', '1'  => 'B', '2'  => 'C', '3'  => 'D', 
        '4'  => 'E', '5'  => 'F', '6'  => 'G', '7'  => 'H', 
        '8'  => 'I', '9'  => 'J', '10' => 'K', '11' => 'L', 
        '12' => 'M', '13' => 'N', '14' => 'O', '15' => 'P', 
        '16' => 'Q', '17' => 'R', '18' => 'S', '19' => 'T',
        '20' => 'U', '21' => 'V', '22' => 'W', '23' => 'X', 
        '24' => 'Y', '25' => 'Z'
    );    

    /**
     * Stringa di errore
     */
    protected $_error = null;

    /**
     * Separatore per la data di nascita
     */
    protected $_dateSeparator = '/';


    /**
     * Percorso del file del database SQLite
     * dei codici catastali
     */
    protected $_dbCatastali = null;

    
    /**
     * Trasforma la stringa passata in un array di lettere
     * e lo incrocia con un ulteriore array 
     */
    protected function _getLettere($string, array $haystack) { 
        $letters = array();
        foreach(str_split($string) as $needle) {
            if (in_array($needle, $haystack)) {
                $letters[] = $needle;
            }
        }
        return $letters;
    }

    /**
     * Ritorna un array con le vocali di una data stringa
     */
    protected function _getVocali($string) {
        return $this->_getLettere($string, $this->_vocali);
    }

    /**
     * Ritorna un array con le consonanti di una data stringa
     */
    protected function _getConsonanti($string) {
        return $this->_getLettere($string, $this->_consonanti);
    }

    /**
     * Pulisce la stringa filtrando tutti i caratteri che
     * non sono lettere. Lo switch $toupper se impostato a TRUE
     * converte la stringa risultante in MAIUSCOLO.
     */
    protected function _sanitize($string, $toupper = true) {
        $result = preg_replace('/[^A-Za-z]*/', '', $string);
        return ($toupper) ? strtoupper($result) : $result;
    }

    /**
     * Se la stringa passata a funzione e' costituita
     * da meno di 3 caratteri, rimpiazza le lettere
     * mancanti con la lettera X.
     */
    protected function _addMissingX($string) {
        $code = $string;
        while(strlen($code) < 3) {
            $code .= 'X';
        }    
        return $code;
    }
    
    /**
     * Ottiene il codice identificativo del nome
     */
    protected function _calcolaNome($string) {
        $nome = $this->_sanitize($string);

        // Se il nome inserito e' piu' corto di 3 lettere
        // si aggiungono tante X quanti sono i caratteri
        // mancanti.
        if (strlen($nome) < 3) {
            return $this->_addMissingX($nome);
        } 
        
        $nome_cons = $this->_getConsonanti($nome);

        // Se le consonanti contenute nel nome sono minori 
        // o uguali a 3 vengono considerate nell'ordine in cui
        // compaiono.
        if (count($nome_cons) <= 3) {
            $code = implode('', $nome_cons);
        } else {
            // Se invece abbiamo almeno 4 consonanti, prendiamo
            // la prima, la terza e la quarta.
            for($i=0; $i<4; $i++) {
                if ($i == 1) continue;
                if (!empty($nome_cons[$i])) {
                    $code .= $nome_cons[$i];
                }
            }
        }
        
        // Se compaiono meno di 3 consonanti nel nome, si
        // utilizzano le vocali, nell'ordine in cui compaiono
        // nel nome.
        if (strlen($code) < 3) {
            $nome_voc = $this->_getVocali($nome);
            while (strlen($code) < 3) {
               $code .= array_shift($nome_voc); 
            }
        }
        
        return $code;
    }

    protected function _calcolaCognome($string) {
        $cognome = $this->_sanitize($string);
    
        // Se il cognome inserito e' piu' corto di 3 lettere
        // si aggiungono tante X quanti sono i caratteri
        // mancanti.
        if (strlen($cognome) < 3) {
            return $this->_addMissingX($cognome);
        }

        $cognome_cons = $this->_getConsonanti($cognome);
        
        // Per il calcolo del cognome si prendono le prime
        // 3 consonanti. 
        for ($i=0; $i<3; $i++) {
            if (array_key_exists($i, $cognome_cons)) {
                $code .= $cognome_cons[$i];
            }
        }

        // Se le consonanti non bastano, vengono prese
        // le vocali nell'ordine in cui compaiono.
        if (strlen($code) < 3) {
            $cognome_voc = $this->_getVocali($cognome);
            while (strlen($code) < 3) {
                $code .= array_shift($cognome_voc);
            }
        }

        return $code;   
    }

    /** 
     * Imposta il separatore di data ( default: / )
     */
    public function setDateSeparator($char) {
        $this->_dateSeparator = $char;
        return $this; 
    }

    /**
     * Ritorna la parte di codice fiscale corrispondente
     * alla data di nascita del soggetto (Forma: AAMGG)
     */
    protected function _calcolaDataNascita($data, $sesso) {
        $dn = explode($this->_dateSeparator, $data); 

        $giorno = (int) @$dn[0];
        $mese   = (int) @$dn[1];
        $anno   = (int) @$dn[2];

        // Le ultime due cifre dell'anno di nascita
        $aa = substr($anno, -2);
        
        // La lettera corrispondente al mese di nascita
        $mm = $this->_mesi[$mese];

        // Il giorno viene calcolato a seconda del sesso
        // del soggetto di cui si calcola il codice:
        // se e' Maschio si mette il giorno reale, se e' 
        // Femmina viene aggiungo 40 a questo numero.
        $gg = (strtoupper($sesso) == 'M') ? $giorno : ($giorno + 40);

        // Bug #1: Thanks to Luca 
        if (strlen($gg) < 2) $gg = '0' . $gg;


        return $aa . $mm . $gg;        
    }

    /**
     * Ritorna il codice catastale del comune richiesto
     */
    protected function _calcolaCatastale($comune, $provincia) {
        $db = sqlite_open($this->_dbCatastali, 0666);
        if (!$db) {
            $this->_setError(self::ERR_SQLITE_OPEN);
            return false;
        }
        
        $sql_comune    = strtoupper(sqlite_escape_string($comune));
        $sql_provincia = strtoupper(sqlite_escape_string($provincia));

        $query = sprintf("SELECT codice FROM catastali WHERE comune = '%s' AND provincia = '%s'",
                         $sql_comune, $sql_provincia);

        $result = sqlite_query($db, $query);
        $entry = sqlite_fetch_array($result, SQLITE_ASSOC);

        if (is_null($entry['codice'])) {
            $this->_setError(self::ERR_SQLITE_QUERY);
            return false;
        }
        
        return $entry['codice'];
    }

    /**
     * Ritorna la cifra di controllo sulla base dei
     * 15 caratteri del codice fiscale calcolati.
     */
    protected function _calcolaCifraControllo($codice) {
        $code = str_split($codice);
        $sum  = 0;

        for($i=1; $i <= count($code); $i++) {
            $cifra = $code[$i-1];
            $sum += ($i % 2) ? $this->_dispari[$cifra] : $this->_pari[$cifra];
        }

        $sum %= 26;

        return $this->_controllo[$sum];
    }

    /**
     * Imposta il percorso del database dei codici catastali
     */
    public function setDatabase($filename) {
        if (!is_readable($filename)) {
            $this->_setError(self::ERR_SQLITE_FILE);
            return false;
        }
        $this->_dbCatastali = $filename;
        return $this;
    }

    /**
     * Imposta il messaggio di errore
     */
    protected function _setError($string) {
        $this->_error = $string;
    }    

    /**
     * Verifica la presenza di un errore.
     * Ritorna TRUE se presente, FALSE altrimenti.
     */
    public function hasError() {
        return !is_null($this->_error);
    }   
    
    /**
     * Ritorna la stringa di errore
     */
    public function getError() {
        return $this->_error;
    }

    /**
     * Ritorna il codice fiscale utilizzando i parametri
     * passati a funzione. Se si verifica
     */
    public function calcola($nome, $cognome, $data, $sesso, $comune, $provincia) {
        $codice = $this->_calcolaCognome($cognome) . 
                  $this->_calcolaNome($nome) . 
                  $this->_calcolaDataNascita($data, $sesso) . 
                  $this->_calcolaCatastale($comune, $provincia);
       
        if ($this->hasError()) {
            return false;
        }
 
        $codice .= $this->_calcolaCifraControllo($codice);

        if (strlen($codice) != 16) {
            $this->_setError(self::ERR_GENERIC);
            return false;
        }

        return $codice;
    } 
}