Script to Download Windows PHP Binaries
Posted: Wed Oct 24, 2007 10:01 pm
Requires CURL and PHP5.
Usage:
Valid values for version are:
- Standard 'x.y.z'
- Development 'x.y-dev'
- Release candidate 'x.y.zRC#'
- Release candidate alias 'RC'
- Latest version alias 'latest'
Code: Select all
<?php
/**
* Object-oriented wrapper for CURL, see http://php.net/manual/en/ref.curl.php
*/
class CURL
{
public $handle;
public $fileHandle;
// basic constructs
public function __construct($url = null) {
$this->handle = curl_init();
if ($url) $this->setopt(CURLOPT_URL, $url);
}
public function __destruct() {
curl_close($this->handle);
}
// baton methods
public function setopt($option, $value) {
return curl_setopt($this->handle, $option, $value);
}
public function setoptArray($options) {
return curl_setopt_array($this->handle, $options);
}
public function exec() {
return curl_exec($this->handle);
}
public function errno() {
return curl_errno($this->handle);
}
public function error() {
return curl_error($this->handle);
}
// extra methods
public function throwException($message) {
$errno = $this->errno();
$error = $this->error();
throw new Exception("$message ($errno): $error");
}
}
/**
* Makes CURL classes, used primarily for unit testing purposes
* @todo Generalize this into some sort of downloader interface abstract factory
*/
class CURLFactory
{
public function make($url = false) {
return new CURL($url);
}
/**
* Generates a CURL object ready to download a file
* @param $url URL of item to download
* @param $filename Filename of location to download to, set to false
* to have contents returned by CURL->exec();
* @return Instance of CURL ready to be executed
*/
public function makeDownload($url, $filename = false) {
$curl = $this->make($url);
$curl->setopt(CURLOPT_URL, $url);
$curl->setopt(CURLOPT_HEADER, false);
$curl->setopt(CURLOPT_FOLLOWLOCATION, true);
$fh = false;
if ($filename) {
$fh = fopen($filename, 'w');
$curl->setopt(CURLOPT_FILE, $fh);
$curl->fileHandle = $fh;
} else {
$curl->setopt(CURLOPT_RETURNTRANSFER, true);
}
return $curl;
}
}
/**
* Downloader interface to retrieve PHP distributions from php.net
* @todo Allow user to specify arbitrary download path without having
* to subclass
*/
class PHPDownloader
{
public $mirrorURL = 'http://us3.php.net';
/**
* Array of download links indexed by version number, and then
* 'php' and 'pecl' for those downloads (will be missing if not
* present). /from/a/mirror links are munged to /from/this/mirror .
* If a path with no authority is given, use $mirrorURL.
*/
public $index = false;
/**
* Contains an instance of CURLFactory, primarily for unit testing
*/
protected $curlFactory;
public function __construct($factory = false) {
if (!$factory) $factory = new CURLFactory();
$this->curlFactory = $factory;
}
protected function getReleasesURL() {
return $this->mirrorURL . '/releases/';
}
protected function getDownloadsURL() {
return $this->mirrorURL . '/downloads.php';
}
protected function getQAURL() {
return 'http://qa.php.net';
}
protected function getSnapsURL() {
return 'http://snaps.php.net';
}
public function getPHPFilename($version) {
return $_ENV['TMP'] . "\\php-$version-Win32.zip";
}
public function getPECLFilename($version) {
return $_ENV['TMP'] . "\\pecl-$version-Win32.zip";
}
protected function extractLinks($url) {
$curl = $this->curlFactory->makeDownload($url);
$html = $curl->exec();
if (!$html) $curl->throwException('Releases download failed');
$doc = new DOMDocument();
@$doc->loadHTML($html);
$xpath = new DOMXPath($doc);
return $xpath->query('//a');
}
/**
* Loads an index of download paths for all PHP versions
*/
public function buildIndex() {
if ($this->index !== false) return;
$this->index = array();
$link_sets = array();
$link_sets[] = $this->extractLinks($this->getDownloadsURL());
$link_sets[] = $this->extractLinks($this->getReleasesURL());
$link_sets[] = $this->extractLinks($this->getQAURL());
$first = true;
foreach($link_sets as $links) {
foreach ($links as $link) {
$url = $link->getAttribute('href');
if (!$url) continue;
if (strpos($url, 'Win32') === false) continue;
if (strpos($url, 'nts') !== false) continue; // ignore non-thread safe binaries
$result = preg_match('#/(php|pecl)-([^-]+)-Win32\.zip#', $url, $matches);
if (!$result) continue;
list($full, $type, $version) = $matches;
$url = str_replace('/from/a/mirror', '/from/this/mirror', $url); // make download ready
if ($url[0] == '/') $url = $this->mirrorURL . $url; // heuristic assumes absolute paths are used
$this->index[$version][$type] = $url;
if (strpos($url, 'RC') !== false) {
$this->index['RC'][$type] = $url; // create alias
} elseif (!isset($this->index['latest'][$type])) {
$this->index['latest'][$type] = $url;
}
}
}
// snapshots require a little different procedure
$links = $this->extractLinks($this->getSnapsURL());
foreach ($links as $link) {
$url = $link->getAttribute('href');
if (!$url) continue;
if (strpos($url, 'win32') === false) continue;
$result = preg_match('#/(php|pecl)([^-]+)-win32-\d+\.zip#', $url, $matches);
if (!$result) continue;
list($full, $type, $version) = $matches;
$url = $this->getSnapsURL() . '/' . $url;
$this->index[$version . '-dev'][$type] = $url;
}
}
/**
* Downloads relevant zips for PHP version to temporary directory
* @param $version Version of PHP to download
* @return array(PHP file download location, PECL file download location)
*/
public function download($version) {
$this->buildIndex();
if (!isset($this->index[$version])) {
throw new Exception("PHP $version not found on releases page");
}
if (!isset($this->index[$version]['php'])) {
throw new Exception("No PHP download found for $version");
}
$urls = $this->index[$version];
$curl = $this->curlFactory
->makeDownload($urls['php'], $php_file = $this->getPHPFilename($version));
$result = $curl->exec();
if (!$result) $curl->throwException('PHP download failed');
if (isset($urls['pecl'])) {
$curl = $this->curlFactory
->makeDownload($urls['pecl'], $pecl_file = $this->getPECLFilename($version));
$result = $curl->exec();
if (!$result) $curl->throwException('PECL download failed');
} else {
$pecl_file = false;
}
return array($php_file, $pecl_file);
}
}Code: Select all
$downloader = new PHPDownloader();
list($php_file, $pecl_file) = $downloader->download($version);
// do something to $php_file and $pecl_file, which point to the filename file was downloaded into- Standard 'x.y.z'
- Development 'x.y-dev'
- Release candidate 'x.y.zRC#'
- Release candidate alias 'RC'
- Latest version alias 'latest'