. */ class Nickname { /** * Regex fragment for pulling a formated nickname *OR* ID number. * Suitable for router def of 'id' parameters on API actions. * * Not guaranteed to be valid after normalization; run the string through * Nickname::normalize() to get the canonical form, or Nickname::isValid() * if you just need to check if it's properly formatted. * * This, DISPLAY_FMT, and CANONICAL_FMT replace the old NICKNAME_FMT, * but be aware that these should not be enclosed in []s. * * @fixme would prefer to define in reference to the other constants */ const INPUT_FMT = '(?:[0-9]+|[0-9a-zA-Z_]{1,64})'; /** * Regex fragment for acceptable user-formatted variant of a nickname. * This includes some chars such as underscore which will be removed * from the normalized canonical form, but still must fit within * field length limits. * * Not guaranteed to be valid after normalization; run the string through * Nickname::normalize() to get the canonical form, or Nickname::isValid() * if you just need to check if it's properly formatted. * * This and CANONICAL_FMT replace the old NICKNAME_FMT, but be aware * that these should not be enclosed in []s. */ const DISPLAY_FMT = '[0-9a-zA-Z_]{1,64}'; /** * Regex fragment for checking a canonical nickname. * * Any non-matching string is not a valid canonical/normalized nickname. * Matching strings are valid and canonical form, but may still be * unavailable for registration due to blacklisting et. * * Only the canonical forms should be stored as keys in the database; * there are multiple possible denormalized forms for each valid * canonical-form name. * * This and DISPLAY_FMT replace the old NICKNAME_FMT, but be aware * that these should not be enclosed in []s. */ const CANONICAL_FMT = '[0-9a-z]{1,64}'; /** * Maximum number of characters in a canonical-form nickname. */ const MAX_LEN = 64; /** * Nice simple check of whether the given string is a valid input nickname, * which can be normalized into an internally canonical form. * * Note that valid nicknames may be in use or reserved. * * @param string $str * @return boolean */ public static function isValid($str) { try { self::normalize($str); return true; } catch (NicknameException $e) { return false; } } /** * Validate an input nickname string, and normalize it to its canonical form. * The canonical form will be returned, or an exception thrown if invalid. * * @param string $str * @return string Normalized canonical form of $str * * @throws NicknameException (base class) * @throws NicknameInvalidException * @throws NicknameEmptyException * @throws NicknameTooLongException */ public static function normalize($str) { $str = trim($str); $str = str_replace('_', '', $str); $str = mb_strtolower($str); $len = mb_strlen($str); if ($len < 1) { throw new NicknameEmptyException(); } else if ($len > self::MAX_LEN) { throw new NicknameTooLongException(); } if (!self::isCanonical($str)) { throw new NicknameInvalidException(); } return $str; } /** * Is the given string a valid canonical nickname form? * * @param string $str * @return boolean */ public static function isCanonical($str) { return preg_match('/^(?:' . self::CANONICAL_FMT . ')$/', $str); } } class NicknameException extends ClientException { function __construct($msg=null, $code=400) { if ($msg === null) { $msg = $this->defaultMessage(); } parent::__construct($msg, $code); } /** * Default localized message for this type of exception. * @return string */ protected function defaultMessage() { return null; } } class NicknameInvalidException extends NicknameException { /** * Default localized message for this type of exception. * @return string */ protected function defaultMessage() { // TRANS: Validation error in form for registration, profile and group settings, etc. return _('Nickname must have only lowercase letters and numbers and no spaces.'); } } class NicknameEmptyException extends NicknameException { /** * Default localized message for this type of exception. * @return string */ protected function defaultMessage() { // TRANS: Validation error in form for registration, profile and group settings, etc. return _('Nickname cannot be empty.'); } } class NicknameTooLongException extends NicknameInvalidException { /** * Default localized message for this type of exception. * @return string */ protected function defaultMessage() { // TRANS: Validation error in form for registration, profile and group settings, etc. return sprintf(_m('Nickname cannot be more than %d character long.', 'Nickname cannot be more than %d characters long.', Nickname::MAX_LEN), Nickname::MAX_LEN); } }