I recently ran across a situation where URLs where being stored in the database with the scheme and the hostname (e.g., http://www.rondobley.com/2011). The validator that was being used was allowing URLs like http://rondobley to pass validation. The validation code was:
if (!Zend_Uri::check($value)) {
$this->_error(self::INVALID_URL);
return false;
}
Zend_Uri::check() will parse the URI and return a scheme handler class based on the scheme and then validate the URI. Currently, the only schemes supported are http(s), however, in the future mailto will be supported as well. So, we end up with an instance of Zend_Uri_Http which then calls the valid() method. This is where we run into our problem. Zend_Uri_Http->valid() validates all parts of the URI, however, when it validates the hostname, it does so with:
// Check the host against the allowed values; delegated to Zend_Filter.
$validate = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_ALL);
Notice here it overrides the Zend_Validate_Hostname default option to Zend_Validate_Hostname::ALLOW_ALL which allows localhost names to validate.
In order to solve this problem and validate the URLs as we stored them I broke the process down into two steps. First, I create a new Zend_Uri_Http object using the fromString() method. This will ensure that we have a scheme of http(s). If not, we can invalidate the URL now.
//get a Zend_Uri_Http object for our URL, this will only accept http(s) schemes
try {
$uriHttp = Zend_Uri_Http::fromString($value);
} catch (Zend_Uri_Exception $e) {
$this->_error(self::INVALID_URL);
return false;
}
If we are valid at this point, we can move on to checking for a valid TLD hostname. First, we get an instance of Zend_Validate_Hostname and pass in the option Zend_Validate_Hostname::ALLOW_DNS. This really is not necessary, as that is the default option, but I just want to make it explicit that we do not want localhost names to validate. Next, we call the isValid() method passing in our hostname that we can conveniently access via our Zend_Uri_Http object.
//if we have a valid URI then we check the hostname for valid TLDs, and not local urls
$hostnameValidator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_DNS); //do not allow local hostnames, this is the default
if (!$hostnameValidator->isValid($uriHttp->getHost())) {
$this->_error(self::INVALID_URL);
return false;
}
return true;
So now we have a validator that will validate the schemes http(s) and TLD hostname. Here the complete validator:
/**
*
* @author Ron Dobley
*/
<?php class Rwd_Validate_IsUrl extends Zend_Validate_Abstract {
/**
* Error codes
* @const string
*/
const INVALID_URL = 'invalidUrl';
/**
* Error messages
* @var array
*/
protected $_messageTemplates = array(self::INVALID_URL = "'%value%' is not a valid URL. It must start with http(s):// and be valid.");
/**
* Defined by Zend_Validate_Interface
*
* Returns true if and only if the $value is a valid url that starts with http(s)://
* and the hostname is a valid TLD
*
* @param string $value
* @throws Zend_Validate_Exception if a fatal error occurs for validation process
* @return boolean
*/
public function isValid($value)
{
if (!is_string($value)) {
$this->_error(self::INVALID_URL);
return false;
}
$this->_setValue($value);
//get a Zend_Uri_Http object for our URL, this will only accept http(s) schemes
try {
$uriHttp = Zend_Uri_Http::fromString($value);
} catch (Zend_Uri_Exception $e) {
$this->_error(self::INVALID_URL);
return false;
}
//if we have a valid URI then we check the hostname for valid TLDs, and not local urls
$hostnameValidator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_DNS); //do not allow local hostnames, this is the default
if (!$hostnameValidator->isValid($uriHttp->getHost())) {
$this->_error(self::INVALID_URL);
return false;
}
return true;
}
}
Feel free to use this in your own projects, as well as leave comments for improvements.
You can find the complete class at https://github.com/rondobley/Zend-Framework-Validate-URL
How to validate a URL with a Scheme and Hostname in Zend Framework
I recently ran across a situation where URLs where being stored in the database with the scheme and the hostname (e.g., http://www.rondobley.com/2011). The validator that was being used was allowing URLs like http://rondobley to pass validation. The validation code was:
if (!Zend_Uri::check($value)) { $this->_error(self::INVALID_URL); return false; }Zend_Uri::check() will parse the URI and return a scheme handler class based on the scheme and then validate the URI. Currently, the only schemes supported are http(s), however, in the future mailto will be supported as well. So, we end up with an instance of Zend_Uri_Http which then calls the valid() method. This is where we run into our problem. Zend_Uri_Http->valid() validates all parts of the URI, however, when it validates the hostname, it does so with:
Notice here it overrides the Zend_Validate_Hostname default option to Zend_Validate_Hostname::ALLOW_ALL which allows localhost names to validate.
In order to solve this problem and validate the URLs as we stored them I broke the process down into two steps. First, I create a new Zend_Uri_Http object using the fromString() method. This will ensure that we have a scheme of http(s). If not, we can invalidate the URL now.
//get a Zend_Uri_Http object for our URL, this will only accept http(s) schemes try { $uriHttp = Zend_Uri_Http::fromString($value); } catch (Zend_Uri_Exception $e) { $this->_error(self::INVALID_URL); return false; }If we are valid at this point, we can move on to checking for a valid TLD hostname. First, we get an instance of Zend_Validate_Hostname and pass in the option Zend_Validate_Hostname::ALLOW_DNS. This really is not necessary, as that is the default option, but I just want to make it explicit that we do not want localhost names to validate. Next, we call the isValid() method passing in our hostname that we can conveniently access via our Zend_Uri_Http object.
//if we have a valid URI then we check the hostname for valid TLDs, and not local urls $hostnameValidator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_DNS); //do not allow local hostnames, this is the default if (!$hostnameValidator->isValid($uriHttp->getHost())) { $this->_error(self::INVALID_URL); return false; } return true;So now we have a validator that will validate the schemes http(s) and TLD hostname. Here the complete validator:
/** * * @author Ron Dobley */ <?php class Rwd_Validate_IsUrl extends Zend_Validate_Abstract { /** * Error codes * @const string */ const INVALID_URL = 'invalidUrl'; /** * Error messages * @var array */ protected $_messageTemplates = array(self::INVALID_URL = "'%value%' is not a valid URL. It must start with http(s):// and be valid."); /** * Defined by Zend_Validate_Interface * * Returns true if and only if the $value is a valid url that starts with http(s):// * and the hostname is a valid TLD * * @param string $value * @throws Zend_Validate_Exception if a fatal error occurs for validation process * @return boolean */ public function isValid($value) { if (!is_string($value)) { $this->_error(self::INVALID_URL); return false; } $this->_setValue($value); //get a Zend_Uri_Http object for our URL, this will only accept http(s) schemes try { $uriHttp = Zend_Uri_Http::fromString($value); } catch (Zend_Uri_Exception $e) { $this->_error(self::INVALID_URL); return false; } //if we have a valid URI then we check the hostname for valid TLDs, and not local urls $hostnameValidator = new Zend_Validate_Hostname(Zend_Validate_Hostname::ALLOW_DNS); //do not allow local hostnames, this is the default if (!$hostnameValidator->isValid($uriHttp->getHost())) { $this->_error(self::INVALID_URL); return false; } return true; } }Feel free to use this in your own projects, as well as leave comments for improvements.
You can find the complete class at https://github.com/rondobley/Zend-Framework-Validate-URL