I have a table full of URLs, some of which will occasionally become invalid. As part of a system I'm writing to go through the links periodically, and test if they are valid (or redirect to valid sites), I wrote the following class. The feedback I'm looking for is primarily related to clean code and best practices--how should the URL be passed into this class, for example? (I have not included the test cases, but they were written first. :) )
Originally, I wrote this with the URL passed in on the constructor, but I realized that if I set it later, I could both make the class a dependency of another class without needing to know the URL ahead of time, and use the same object to test multiple URLs.
But as it is, my URLisValid function is doing two things: setting a class variable and evaluating the URL. That doesn't seem ideal to me, but neither does having a separate Setter, nor does passing the URL around the class as an argument, nor does passing it in on the constructor.
(obviously, I think this works fine as-is, although feel free to point out any real bugs. I'm improving this class as a lesson to myself in making my code as clean as possible).
class URLChecker
{
private $curl;
private $url;
const HTTP_RESPONSE_OK = 200;
public function __construct()
{
$this->curl = curl_init();
}
public function __destruct()
{
curl_close( $this->curl );
}
public function URLisValid( $url )
{
$this->url = $url;
$httpcode = $this->getHttpCodeForURL();
return $this->httpCodeIsValid( $httpcode );
}
private function getHttpCodeForURL()
{
$this->executeCurlSessionForHeadersOnly();
return $this->getHttpCodeFromLastTransfer();
}
private function executeCurlSessionForHeadersOnly()
{
$this->setCurlOptions();
curl_exec( $this->curl );
}
private function getHttpCodeFromLastTransfer()
{
return curl_getinfo( $this->curl, CURLINFO_HTTP_CODE );
}
private function setCurlOptions()
{
curl_setopt($this->curl, CURLOPT_URL, $this->url);
curl_setopt($this->curl, CURLOPT_NOBODY, true);
curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, true);
}
private function httpCodeIsValid( $httpcode )
{
return $httpcode == self::HTTP_RESPONSE_OK;
}
}
EDIT: version 2, based in part on some feedback:
class URLChecker
{
private $session;
const HTTP_RESPONSE_OK = 200;
public function __construct()
{
$this->session = curl_init();
$this->initializeSessionOptions();
}
public function urlIsValid( $url )
{
$this->setSessionUrl( $url );
$this->executeSession();
$httpcode = $this->getHttpCodeFromLastSession();
return $this->httpCodeIsValid( $httpcode );
}
private function executeSession()
{
curl_exec( $this->session );
}
private function getHttpCodeFromLastSession()
{
return curl_getinfo( $this->session, CURLINFO_HTTP_CODE );
}
private function setSessionUrl( $url )
{
curl_setopt($this->session, CURLOPT_URL, $url);
}
private function initializeSessionOptions()
{
curl_setopt($this->session, CURLOPT_NOBODY, true);
curl_setopt($this->session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->session, CURLOPT_FOLLOWLOCATION, true);
}
private function httpCodeIsValid( $httpcode )
{
return $httpcode == self::HTTP_RESPONSE_OK;
}
function __destruct()
{
curl_close( $this->session );
}
}
I've removed $url as a class variable entirely, I've changed some method names to remove references to "curl" where the implementation of the session is not relevant to the logic. I kept the one line functions, because I want someone who does not know exactly how curl works to still be able to read the code. It is now clear that $url is not a property of the class--it is rather a variable to be passed in once, and tested.