* @copyright Copyright © 2010, Nick Pope * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0 */ namespace App\Util\Lexer; /** * Twitter Validator Class. * * Performs "validation" on tweets. * * Originally written by {@link http://github.com/mikenz Mike Cochrane}, this * is based on code by {@link http://github.com/mzsanford Matt Sanford} and * heavily modified by {@link http://github.com/ngnpope Nick Pope}. * * @author Nick Pope * @copyright Copyright © 2010, Nick Pope * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License v2.0 */ class Validator extends Regex { /** * The maximum length of a tweet. * * @var int */ const MAX_LENGTH = 140; /** * The length of a short URL beginning with http:. * * @var int */ protected $short_url_length = 23; /** * The length of a short URL beginning with http:. * * @var int */ protected $short_url_length_https = 23; /** * @var Extractor */ protected $extractor = null; /** * Provides fluent method chaining. * * @param string $tweet The tweet to be validated. * @param mixed $config Setup short URL length from Twitter API /help/configuration response. * * @see __construct() * * @return Validator */ public static function create($tweet = null, $config = null) { return new self($tweet, $config); } /** * Reads in a tweet to be parsed and validates it. * * @param string $tweet The tweet to validate. */ public function __construct($tweet = null, $config = null) { parent::__construct($tweet); if (!empty($config)) { $this->setConfiguration($config); } $this->extractor = Extractor::create(); } /** * Setup short URL length from Twitter API /help/configuration response. * * @param mixed $config * * @return Validator * * @link https://dev.twitter.com/docs/api/1/get/help/configuration */ public function setConfiguration($config) { if (is_array($config)) { // setup from array if (isset($config['short_url_length'])) { $this->setShortUrlLength($config['short_url_length']); } if (isset($config['short_url_length_https'])) { $this->setShortUrlLengthHttps($config['short_url_length_https']); } } elseif (is_object($config)) { // setup from object if (isset($config->short_url_length)) { $this->setShortUrlLength($config->short_url_length); } if (isset($config->short_url_length_https)) { $this->setShortUrlLengthHttps($config->short_url_length_https); } } return $this; } /** * Set the length of a short URL beginning with http:. * * @param mixed $length * * @return Validator */ public function setShortUrlLength($length) { $this->short_url_length = intval($length); return $this; } /** * Get the length of a short URL beginning with http:. * * @return int */ public function getShortUrlLength() { return $this->short_url_length; } /** * Set the length of a short URL beginning with https:. * * @param mixed $length * * @return Validator */ public function setShortUrlLengthHttps($length) { $this->short_url_length_https = intval($length); return $this; } /** * Get the length of a short URL beginning with https:. * * @return int */ public function getShortUrlLengthHttps() { return $this->short_url_length_https; } /** * Check whether a tweet is valid. * * @param string $tweet The tweet to validate. * * @return bool Whether the tweet is valid. */ public function isValidTweetText($tweet = null) { if (is_null($tweet)) { $tweet = $this->tweet; } $length = $this->getTweetLength($tweet); if (!$tweet || !$length) { return false; } if ($length > self::MAX_LENGTH) { return false; } if (preg_match(self::$patterns['invalid_characters'], $tweet)) { return false; } return true; } /** * Check whether a tweet is valid. * * @return bool Whether the tweet is valid. * * @deprecated since version 1.1.0 */ public function validateTweet() { return $this->isValidTweetText(); } /** * Check whether a username is valid. * * @param string $username The username to validate. * * @return bool Whether the username is valid. */ public function isValidUsername($username = null) { if (is_null($username)) { $username = $this->tweet; } $length = StringUtils::strlen($username); if (empty($username) || !$length) { return false; } $extracted = $this->extractor->extractMentionedScreennames($username); return count($extracted) === 1 && $extracted[0] === substr($username, 1); } /** * Check whether a username is valid. * * @return bool Whether the username is valid. * * @deprecated since version 1.1.0 */ public function validateUsername() { return $this->isValidUsername(); } /** * Check whether a list is valid. * * @param string $list The list name to validate. * * @return bool Whether the list is valid. */ public function isValidList($list = null) { if (is_null($list)) { $list = $this->tweet; } $length = StringUtils::strlen($list); if (empty($list) || !$length) { return false; } preg_match(self::$patterns['valid_mentions_or_lists'], $list, $matches); $matches = array_pad($matches, 5, ''); return isset($matches) && $matches[1] === '' && $matches[4] && !empty($matches[4]) && $matches[5] === ''; } /** * Check whether a list is valid. * * @return bool Whether the list is valid. * * @deprecated since version 1.1.0 */ public function validateList() { return $this->isValidList(); } /** * Check whether a hashtag is valid. * * @param string $hashtag The hashtag to validate. * * @return bool Whether the hashtag is valid. */ public function isValidHashtag($hashtag = null) { if (is_null($hashtag)) { $hashtag = $this->tweet; } $length = StringUtils::strlen($hashtag); if (empty($hashtag) || !$length) { return false; } $extracted = $this->extractor->extractHashtags($hashtag); return count($extracted) === 1 && $extracted[0] === substr($hashtag, 1); } /** * Check whether a hashtag is valid. * * @return bool Whether the hashtag is valid. * * @deprecated since version 1.1.0 */ public function validateHashtag() { return $this->isValidHashtag(); } /** * Check whether a URL is valid. * * @param string $url The url to validate. * @param bool $unicode_domains Consider the domain to be unicode. * @param bool $require_protocol Require a protocol for valid domain? * * @return bool Whether the URL is valid. */ public function isValidURL($url = null, $unicode_domains = true, $require_protocol = true) { if (is_null($url)) { $url = $this->tweet; } $length = StringUtils::strlen($url); if (empty($url) || !$length) { return false; } preg_match(self::$patterns['validate_url_unencoded'], $url, $matches); $match = array_shift($matches); if (!$matches || $match !== $url) { return false; } list($scheme, $authority, $path, $query, $fragment) = array_pad($matches, 5, ''); // Check scheme, path, query, fragment: if (($require_protocol && !( self::isValidMatch($scheme, self::$patterns['validate_url_scheme']) && preg_match('/^https?$/i', $scheme)) ) || !self::isValidMatch($path, self::$patterns['validate_url_path']) || !self::isValidMatch($query, self::$patterns['validate_url_query'], true) || !self::isValidMatch($fragment, self::$patterns['validate_url_fragment'], true)) { return false; } // Check authority: $authority_pattern = $unicode_domains ? 'validate_url_unicode_authority' : 'validate_url_authority'; return self::isValidMatch($authority, self::$patterns[$authority_pattern]); } /** * Check whether a URL is valid. * * @param bool $unicode_domains Consider the domain to be unicode. * @param bool $require_protocol Require a protocol for valid domain? * * @return bool Whether the URL is valid. * * @deprecated since version 1.1.0 */ public function validateURL($unicode_domains = true, $require_protocol = true) { return $this->isValidURL(null, $unicode_domains, $require_protocol); } /** * Determines the length of a tweet. Takes shortening of URLs into account. * * @param string $tweet The tweet to validate. * * @return int the length of a tweet. */ public function getTweetLength($tweet = null) { if (is_null($tweet)) { $tweet = $this->tweet; } $length = StringUtils::strlen($tweet); $urls_with_indices = $this->extractor->extractURLsWithIndices($tweet); foreach ($urls_with_indices as $x) { $length += $x['indices'][0] - $x['indices'][1]; $length += stripos($x['url'], 'https://') === 0 ? $this->short_url_length_https : $this->short_url_length; } return $length; } /** * Determines the length of a tweet. Takes shortening of URLs into account. * * @return int the length of a tweet. * * @deprecated since version 1.1.0 */ public function getLength() { return $this->getTweetLength(); } /** * A helper function to check for a valid match. Used in URL validation. * * @param string $string The subject string to test. * @param string $pattern The pattern to match against. * @param bool $optional Whether a match is compulsory or not. * * @return bool Whether an exact match was found. */ protected static function isValidMatch($string, $pattern, $optional = false) { $found = preg_match($pattern, $string, $matches); if (!$optional) { return ($string || $string === '') && $found && $matches[0] === $string; } else { return !(($string || $string === '') && (!$found || $matches[0] !== $string)); } } }