Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Use Zend\Http\Client in Zend\Version #4625

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 141 additions & 33 deletions library/Zend/Version/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace Zend\Version;

use Zend\Http;
use Zend\Json\Json;

/**
Expand Down Expand Up @@ -52,6 +53,7 @@ public static function compareVersion($version)
{
$version = strtolower($version);
$version = preg_replace('/(\d)pr(\d?)/', '$1a$2', $version);

return version_compare($version, strtolower(self::VERSION));
}

Expand All @@ -64,54 +66,160 @@ public static function compareVersion($version)
* numbers with version_compare().
*
* If $service is set to VERSION_SERVICE_ZEND this will fall back to calling the
* classic style of version retreival.
*
* classic style of version retrieval.
*
* @see http://developer.github.com/v3/git/refs/#get-all-references
* @link https://api.github.com/repos/zendframework/zf2/git/refs/tags/release-
* @link http://framework.zend.com/api/zf-version?v=2
* @param string $service Version Service with which to retrieve the version
* @see http://developer.github.com/v3/git/refs/#get-all-references
* @link https://api.github.com/repos/zendframework/zf2/git/refs/tags/release-
* @link http://framework.zend.com/api/zf-version?v=2
* @param string $service Version service with which to retrieve the version
* @param Http\Client $httpClient HTTP client with which to retrieve the version
* @return string
*/
public static function getLatest($service = self::VERSION_SERVICE_ZEND)
public static function getLatest($service = self::VERSION_SERVICE_ZEND, Http\Client $httpClient = null)
{
if (null === static::$latestVersion) {
static::$latestVersion = 'not available';
if ($service == self::VERSION_SERVICE_GITHUB) {
$url = 'https://api.github.com/repos/zendframework/zf2/git/refs/tags/release-';

$apiResponse = Json::decode(file_get_contents($url), Json::TYPE_ARRAY);

// Simplify the API response into a simple array of version numbers
$tags = array_map(function ($tag) {
return substr($tag['ref'], 18); // Reliable because we're filtering on 'refs/tags/release-'
}, $apiResponse);

// Fetch the latest version number from the array
static::$latestVersion = array_reduce($tags, function ($a, $b) {
return version_compare($a, $b, '>') ? $a : $b;
});
} elseif ($service == self::VERSION_SERVICE_ZEND) {
$handle = fopen('http://framework.zend.com/api/zf-version?v=2', 'r');
if (false !== $handle) {
static::$latestVersion = stream_get_contents($handle);
fclose($handle);
}
}
if (null !== self::$latestVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use static instead of self.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self make more sense, because this class is marked as final, which means that this class cannot be extended.

return self::$latestVersion;
}

self::$latestVersion = 'not available';

if (null === $httpClient && !ini_get('allow_url_fopen')) {
trigger_error(
sprintf(
'allow_url_fopen is not set, and no Zend\Http\Client ' .
'was passed. You must either set allow_url_fopen in ' .
'your PHP configuration or pass a configured ' .
'Zend\Http\Client as the second argument to %s.',
__METHOD__
),
E_USER_WARNING
);

return self::$latestVersion;
}

return static::$latestVersion;
$response = false;
if ($service === self::VERSION_SERVICE_GITHUB) {
$response = self::getLatestFromGithub($httpClient);
} elseif ($service === self::VERSION_SERVICE_ZEND) {
$response = self::getLatestFromZend($httpClient);
} else {
trigger_error(
sprintf(
'Unknown version service: %s',
$service
),
E_USER_WARNING
);
}

if ($response) {
self::$latestVersion = $response;
}

return self::$latestVersion;
}

/**
* Returns true if the running version of Zend Framework is
* the latest (or newer??) than the latest tag on GitHub,
* which is returned by static::getLatest().
* which is returned by self::getLatest().
*
* @return bool
*/
public static function isLatest()
{
return static::compareVersion(static::getLatest()) < 1;
return self::compareVersion(self::getLatest()) < 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change static:: to self:: ?

static:: provides more flexibility for extending classes.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class cannot be extended, its a final class.

}

/**
* Get the API response to a call from a configured HTTP client
*
* @param Http\Client $httpClient Configured HTTP client
* @return string|false API response or false on error
*/
protected static function getApiResponse(Http\Client $httpClient)
{
try {
$response = $httpClient->send();
} catch (Http\Exception\RuntimeException $e) {
return false;
}

if (!$response->isSuccess()) {
return false;
}

return $response->getBody();
}

/**
* Get the latest version from Github
*
* @param Http\Client $httpClient Configured HTTP client
* @return string|null API response or false on error
*/
protected static function getLatestFromGithub(Http\Client $httpClient = null)
{
$url = 'https://api.github.com/repos/zendframework/zf2/git/refs/tags/release-';

if ($httpClient === null) {
$context = stream_context_create(
array(
'http' => array(
'user_agent' => sprintf('Zend-Version/%s', self::VERSION),
),
)
);
$apiResponse = file_get_contents($url, false, $context);
} else {
$request = new Http\Request();
$request->setUri($url);
$httpClient->setRequest($request);
$apiResponse = self::getApiResponse($httpClient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing... why self:: vs. static::?

}

if (!$apiResponse) {
return false;
}

$decodedResponse = Json::decode($apiResponse, Json::TYPE_ARRAY);

// Simplify the API response into a simple array of version numbers
$tags = array_map(function ($tag) {
return substr($tag['ref'], 18); // Reliable because we're
// filtering on 'refs/tags/release-'
}, $decodedResponse);

// Fetch the latest version number from the array
return array_reduce($tags, function ($a, $b) {
return version_compare($a, $b, '>') ? $a : $b;
});
}

/**
* Get the latest version from framework.zend.com
*
* @param Http\Client $httpClient Configured HTTP client
* @return string|null API response or false on error
*/
protected static function getLatestFromZend(Http\Client $httpClient = null)
{
$url = 'http://framework.zend.com/api/zf-version?v=2';

if ($httpClient === null) {
$apiResponse = file_get_contents($url);
} else {
$request = new Http\Request();
$request->setUri($url);
$httpClient->setRequest($request);
$apiResponse = self::getApiResponse($httpClient);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before; self vs. static

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted before, the class is declared final.

}

if (!$apiResponse) {
return false;
}

return $apiResponse;
}
}
3 changes: 3 additions & 0 deletions library/Zend/Version/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"require": {
"php": ">=5.3.3"
},
"suggest": {
"zendframework/zend-http": "Allows use of Zend\\Http\\Client to check version information"
},
"extra": {
"branch-alias": {
"dev-master": "2.2-dev",
Expand Down
102 changes: 100 additions & 2 deletions tests/ZendTest/Version/VersionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* @package Zend_VersionTest.php
*/

use Zend\Http;
use Zend\Version\Version;

/**
Expand Down Expand Up @@ -54,7 +55,10 @@ public function testVersionCompare()
}

/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @group ZF-10363
* @runInSeparateProcess
*/
public function testFetchLatestVersion()
{
Expand All @@ -64,21 +68,115 @@ public function testFetchLatestVersion()
if (!extension_loaded('openssl')) {
$this->markTestSkipped('This test requires openssl extension to be enabled in PHP');
}

$actual = Version::getLatest();

$this->assertRegExp('/^[1-2](\.[0-9]+){2}/', $actual);
}

public function testFetchLatestZENDVersion()
/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @runInSeparateProcess
*/
public function testFetchLatestGithubVersion()
{
if (!constant('TESTS_ZEND_VERSION_ONLINE_ENABLED')) {
$this->markTestSkipped('Version online tests are not enabled');
}
if (!extension_loaded('openssl')) {
$this->markTestSkipped('This test requires openssl extension to be enabled in PHP');
}
$actual = Version::getLatest('ZEND');

$actual = Version::getLatest(Version::VERSION_SERVICE_GITHUB);

$this->assertRegExp('/^[1-2](\.[0-9]+){2}/', $actual);
}

/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @expectedException PHPUnit_Framework_Error_Warning
* @runInSeparateProcess
*/
public function testFetchLatestVersionWarnsIfAllowUrlFopenIsDisabled()
{
if (!constant('TESTS_ZEND_VERSION_ONLINE_ENABLED')) {
$this->markTestSkipped('Version online tests are not enabled');
}
if (ini_get('allow_url_fopen')) {
$this->markTestSkipped('Test only works with allow_url_fopen disabled');
}

$actual = Version::getLatest(Version::VERSION_SERVICE_ZEND);
}

/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @expectedException PHPUnit_Framework_Error_Warning
* @runInSeparateProcess
*/
public function testFetchLatestVersionWarnsIfBadServiceIsPassed()
{
if (!constant('TESTS_ZEND_VERSION_ONLINE_ENABLED')) {
$this->markTestSkipped('Version online tests are not enabled');
}

$actual = Version::getLatest('bogus service');
}

/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @runInSeparateProcess
*/
public function testFetchLatestVersionUsesSuppliedZendHttpClient()
{
if (!constant('TESTS_ZEND_VERSION_ONLINE_ENABLED')) {
$this->markTestSkipped('Version online tests are not enabled');
}
if (!extension_loaded('openssl')) {
$this->markTestSkipped('This test requires openssl extension to be enabled in PHP');
}

$httpClient = new Http\Client(
'http://example.com',
array(
'sslverifypeer' => false,
)
);

$actual = Version::getLatest(Version::VERSION_SERVICE_GITHUB, $httpClient);
$this->assertRegExp('/^[1-2](\.[0-9]+){2}/', $actual);

$lastRequest = $httpClient->getRequest();
$this->assertContains('github.com', (string) $lastRequest->getUri());
}

/**
* Run in separate process to avoid Version::$latestParameter caching
*
* @runInSeparateProcess
*/
public function testFetchLatestVersionDoesNotThrowZendHttpClientException()
{
if (!constant('TESTS_ZEND_VERSION_ONLINE_ENABLED')) {
$this->markTestSkipped('Version online tests are not enabled');
}
if (!extension_loaded('openssl')) {
$this->markTestSkipped('This test requires openssl extension to be enabled in PHP');
}

$httpClient = new Http\Client(
'http://example.com',
array(
'sslcapath' => '/dev/null',
'sslverifypeer' => true,
)
);

$actual = Version::getLatest(Version::VERSION_SERVICE_GITHUB, $httpClient);
$this->assertEquals('not available', $actual);
}
}