0, 'timeout' => 10 ]; protected $validLocationUrlCodes = [301, 302, 200]; protected $warningLocationUrlCodes = [301]; protected $index; protected $line; protected $columns = [ 'line' => [ 'title' => 'Line', 'width' => 6, 'align' => 'center' ], 'testType' => [ 'title' => 'Test Type', 'width' => 15, ], 'url' => [ 'title' => 'URLs', 'width' => 122, ], 'message' => [ 'title' => 'Message', 'width' => 42, ], 'status' => [ 'title' => 'Status', 'width' => 9, ], ]; /** * @var ConsoleColorText */ protected $text; protected $_user; protected $_password; protected $youTrackFormat = false; function __construct($options = array()) { $this->client = new Client(null, array_merge($this->clientDefaultOptions, $options)); $this->client->getRequest()->setMethod(Request::METHOD_HEAD); $this->text = new ConsoleColorText(); } /** * @param boolean $youTrackFormat * @return $this */ public function setYouTrackFormat($youTrackFormat) { $this->youTrackFormat = (bool) $youTrackFormat; return $this; } /** * @return boolean */ public function getYouTrackFormat() { return $this->youTrackFormat; } public function head($url) { $this->_initCredentials(); return $this->client->setUri($url)->send(); } public function validateRedirect($sourceUrl, $destinationUrl, array $flags = array()) { if (empty($flags['R'])) { $this->logError(self::TYPE_DST_URL, $destinationUrl, 'R flag is undefined'); return false; } $response = $this->head($sourceUrl); // Validate Source URL Redirect Code if ($flags['R'] != ($code = $response->getStatusCode())) { $this->logError( self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), 'HTTP ' . $code . ' (' . $flags['R'] . ' expected)' ); return false; } $this->logSuccess(self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), 'HTTP ' . $code); if ($this->areUrlEqual($sourceUrl, $destinationUrl)) { $this->logError(self::TYPE_DST_URL, $destinationUrl, 'infinite loop'); return false; } /** @var $location Zend\Http\Header\Location */ if (!($location = $response->getHeaders()->get('Location')) || !($locationUrl = $location->getFieldValue())) { $this->logError(self::TYPE_SRC_URL, $this->_formatUrl($sourceUrl), '"Location" header is undefined'); return false; } // Validate Location $locationResponse = $this->head($locationUrl); if (!($isValidLocationCode = in_array($locationResponse->getStatusCode(), $this->validLocationUrlCodes))) { $this->logError( self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), 'unexpected HTTP code ' . $locationResponse->getStatusCode() ); } else { $message = 'HTTP ' . $locationResponse->getStatusCode(); if (in_array($locationResponse->getStatusCode(), $this->warningLocationUrlCodes)) { $this->logWarning(self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), $message); } else { $this->logSuccess(self::TYPE_DST_URL, ' --> ' . $this->_formatUrl($locationUrl), $message); } } if (!$this->areUrlEqual($locationUrl, $destinationUrl)) { $this->logError(self::TYPE_DST_URL_MATCH, ' --> ' . $this->_formatUrl($destinationUrl)); } else { $this->logSuccess(self::TYPE_DST_URL_MATCH, ' --> ' . $this->_formatUrl($destinationUrl)); } return true; } protected function logWarning($testType, $url, $message = null) { return $this->log($testType, $url, $this->_formatWarning($message), $this->_formatWarning(self::STATUS_WARNING)); } protected function logError($testType, $url, $message = null) { return $this->log($testType, $url, $this->_formatError($message), $this->_formatError(self::STATUS_FAILED)); } protected function logSuccess($testType, $url, $message = null) { return $this->log($testType, $url, $this->_formatSuccess($message), $this->_formatSuccess(self::STATUS_SUCCESS)); } protected function _formatError($text) { if ($this->getYouTrackFormat()) { return '{color:red}' . $text . '{color}'; } return $this->text->paint($text, 'red'); } protected function _formatWarning($text) { if ($this->getYouTrackFormat()) { return '{color:orange}' . $text . '{color}'; } return $this->text->paint($text, 'orange'); } protected function _formatSuccess($text) { if ($this->getYouTrackFormat()) { return '{color:green}' . $text . '{color}'; } return $this->text->paint($text, 'green'); } protected function areUrlEqual($locationUrl, $destinationUrl) { if ('?' == substr($destinationUrl, -1)) { $destinationUrl = substr($destinationUrl, 0, -1); } return rawurldecode($locationUrl) == rawurldecode($destinationUrl); } public function log($testType, $url, $message, $status) { $separator = '|'; $line = $this->line + 1; $values = compact('line', 'testType', 'url', 'message', 'status'); $result = ''; foreach ($this->columns as $name => $column) { $align = array_key_exists('align', $column) ? $column['align'] : null; $value = $values[$name]; $result .= $separator . $this->sprint($value, $column['width'], $align); } echo $result . $separator. PHP_EOL; return $this; } protected function _formatUrl($url) { if (!$this->getYouTrackFormat()) { return $url; } if (strlen($url) > 80) { $text = substr($url, 0, 10) . '...' . substr($url, -68); return '[' . $url . ' ' . $text . ']'; } return $url; } protected function renderHeaders() { $separator = $this->getYouTrackFormat() ? '||' : '|'; $header = ''; foreach ($this->columns as $column) { $align = array_key_exists('align', $column) ? $column['align'] : null; $header .= $separator . $this->sprint($column['title'], $column['width'], $align); } $header .= $separator . PHP_EOL; return $header; } protected function sprint($text, $width, $align = null) { if (false !== (strpos($text, ConsoleColorText::START))) { $plainText = preg_replace("/\033\\[[\\d;]+m/", '', $text); $length = strlen($plainText); } else { $length = strlen($text); } switch ($align) { case 'center': $paddingLeft = floor(max($width - $length, 0) / 2); $paddingRight = ceil(max($width - $length, 0) / 2); break; case 'right': $paddingRight = ($width - $length > 1) ? 1 : 0; $paddingLeft = floor(max($width - $length - $paddingRight, 0)); break; case null: // break was intentionally omitted case 'left': // break was intentionally omitted default: $paddingLeft = ($width - $length > 1) ? 1 : 0; $paddingRight = max($width - $length - $paddingLeft, 0); } return str_repeat(' ', $paddingLeft) . $text . str_repeat(' ', $paddingRight); } public function run($file) { $srcUrls = []; $lines = file($file); $prevLine = null; echo $this->renderHeaders(); $this->index = 0; foreach ($lines as $this->line => $line) { $line = trim($line); if (empty($line)) { $srcUrls = []; continue; } if ($line[0] == '#' && ($url = trim(ltrim($line, '# '), "\n")) && 0 === strpos($url, 'http')) { $srcUrls[] = str_replace(' ','%20', $url); } if (empty($srcUrls) || 0 !== strncmp($line, 'RewriteRule', 11)) { continue; } $matches = array(); if (!preg_match('/^RewriteRule\s+\^([^\$]+)\$\s+([^\s]+)(?:\s+\[([^\s]+)\])/', $line, $matches)) { continue; } list(,, $substitution, $flags) = $matches; $this->index++; foreach ($srcUrls as $sourceUrl) { $this->validateRedirect($sourceUrl, $substitution, $this->_parseFlags($flags)); } $srcUrls = []; } } protected function _parseFlags($value) { $fields = array(); $parts = explode(',', $value); foreach ($parts as $part) { $list = explode('=', $part); $fields[$list[0]] = isset($list[1]) ? $list[1] : null; } return $fields; } protected function _initCredentials() { if (!empty($this->_user) && !empty($this->_password)) { $this->client->setAuth($this->_user, $this->_password); } return $this; } public function setCredentials($user, $password) { $this->_user = $user; $this->_password = $password; return $this; } }