9 ) { $digit -= 9; } } $sum += $digit; } return 0 === $sum % 10; } /** * Validate a SIREN number. * * @param string $value Raw input. * @return bool */ public static function is_valid_siren( $value ) { $value = self::normalize( $value ); return (bool) preg_match( '/^\d{9}$/', $value ) && self::luhn( $value ); } /** * Validate a SIRET number. * * @param string $value Raw input. * @return bool */ public static function is_valid_siret( $value ) { $value = self::normalize( $value ); if ( ! preg_match( '/^\d{14}$/', $value ) ) { return false; } if ( self::luhn( $value ) ) { return true; } // Many La Poste establishments do not pass Luhn; their digit sum is a multiple of 5 instead. return '356000000' === substr( $value, 0, 9 ) && 0 === array_sum( str_split( $value ) ) % 5; } /** * Compute the French VAT number for a SIREN. * * @param string $siren Valid SIREN. * @return string e.g. FR40303265045 */ public static function vat_from_siren( $siren ) { $siren = self::normalize( $siren ); $key = ( 12 + 3 * ( (int) $siren % 97 ) ) % 97; return 'FR' . str_pad( (string) $key, 2, '0', STR_PAD_LEFT ) . $siren; } /** * Validate a French VAT number. * * Numeric keys are checked against the SIREN. Alphanumeric keys (issued to * some companies) are accepted on format only. * * @param string $value Raw input. * @return bool */ public static function is_valid_fr_vat( $value ) { $value = self::normalize( $value ); if ( ! preg_match( '/^FR([0-9A-HJ-NP-Z]{2})(\d{9})$/', $value, $m ) ) { return false; } if ( ctype_digit( $m[1] ) ) { return self::vat_from_siren( $m[2] ) === $value; } return true; } /** * Basic format check for EU VAT numbers outside France (2-letter prefix + 2-13 chars). * * @param string $value Raw input. * @return bool */ public static function looks_like_eu_vat( $value ) { $value = self::normalize( $value ); if ( 0 === strpos( $value, 'FR' ) ) { return self::is_valid_fr_vat( $value ); } return (bool) preg_match( '/^(AT|BE|BG|CY|CZ|DE|DK|EE|EL|ES|FI|HR|HU|IE|IT|LT|LU|LV|MT|NL|PL|PT|RO|SE|SI|SK|XI)[0-9A-Z]{2,13}$/', $value ); } /** * Return the SIREN contained in a SIRET. * * @param string $siret Valid SIRET. * @return string */ public static function siren_from_siret( $siret ) { return substr( self::normalize( $siret ), 0, 9 ); } }