Commit:    ae57162e8dce7a000a1e3ea3e05387a9263ef0bc
Author:    Peter Kokot <peterko...@gmail.com>         Sun, 16 Dec 2018 09:45:38 
+0100
Parents:   6712d9e13cdc37af96018976ac50b4b1c62b5604
Branches:  master

Link:       
http://git.php.net/?p=web/bugs.git;a=commitdiff;h=ae57162e8dce7a000a1e3ea3e05387a9263ef0bc

Log:
Refactor fetching versions

- Procedural code moved to OOP
- Added unit tests
- Added a more flexible tmp folder location and introduce var folder
  (in production this won't work yet, so we still use /tmp there).
  For local development environments var directory in project root
  is used for faster and easier project setup.
- Added initial extensible PSR-16-alike semi-compatible cache class
  and refactored storing fetched versions.
- The versions list generator is now simpler and a bit more logical
  what is happening. Versions sort order is the same as before.
- Added ComposerScripts utility/service class for creating required
  directories (uploads and var/cache), and configuration file, when
  installing application in development environment.

Changed paths:
  M  .gitignore
  M  README.md
  M  composer.json
  D  include/php_versions.php
  A  src/Utils/Cache.php
  A  src/Utils/ComposerScripts.php
  A  src/Utils/Versions/Client.php
  A  src/Utils/Versions/Generator.php
  A  tests/Utils/CacheTest.php
  A  tests/Utils/Versions/ClientTest.php
  A  tests/Utils/Versions/GeneratorTest.php
  A  tests/fixtures/versions/versions.php
  A  tests/mock/responses/dev-body.txt
  A  tests/mock/responses/stable-body.txt
  M  www/report.php

diff --git a/.gitignore b/.gitignore
index 6c2d011..07c61bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,5 +7,8 @@
 # Local specific PHPUnit configuration
 /phpunit.xml
 
+# Transient and temporary generated files
+/var/
+
 # Generated by Composer
 /vendor/
diff --git a/README.md b/README.md
index 526ee6a..e24aca4 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,6 @@ This is a unified bug tracking system for PHP hosted online at
 
 ## Local installation
 
-* Copy configuration and modify it accordingly for your local system:
-
-```bash
-cp local_config.php.sample local_config.php
-```
-
 * Install development dependencies with Composer:
 
 ```bash
@@ -24,6 +18,10 @@ pear channel-update pear.php.net
 pear install --alldeps Text_Diff
 ```
 
+* Configuration:
+
+Modify `local_config.php` according to your local development environment.
+
 * Database:
 
 Create a new MySQL/MariaDB database using `sql/database.sql`, create database
@@ -58,6 +56,7 @@ Source code of this application is structured in the 
following directories:
  ├─ templates/              # Application templates
  ├─ tests/                  # Application automated tests
  ├─ uploads/                # Uploaded patch files
+ ├─ var/                    # Transient and temporary generated files
  ├─ vendor/                 # Dependencies generated by Composer
  └─ www/                    # Publicly accessible directory for online 
bugs.php.net
     ├─ css/                 # Stylesheets
diff --git a/composer.json b/composer.json
index a4fea73..cddad87 100644
--- a/composer.json
+++ b/composer.json
@@ -42,5 +42,11 @@
         "psr-4": {
             "App\\Tests\\": "tests/"
         }
+    },
+    "scripts":{
+        "post-install-cmd": [
+            "App\\Utils\\ComposerScripts::installConfig",
+            "App\\Utils\\ComposerScripts::createDirectories"
+        ]
     }
 }
diff --git a/include/php_versions.php b/include/php_versions.php
deleted file mode 100644
index 8aa07af..0000000
--- a/include/php_versions.php
+++ /dev/null
@@ -1,106 +0,0 @@
-<?php
-
-/*
-The RC and dev versions are pulled from the https://qa.php.net/api.php
-if you want to add a new version, add it there at include/release-qa.php
-the result is cached for an hour in /tmp/<systemd>/tmp/versions.php
-the versions are weighted by the following:
-- major+minor version desc (7>5.4>5.3>master)
-- between a minor version we order by the micro if available: first the qa 
releases: alpha/beta/rc, then the stable, then the Git versions(snaps, Git)
-
-Stable releases are pulled from https://php.net/releases/active.php
-*/
-
-// Custom versions appended to the list
-$custom_versions = [
-       'Next Major Version',
-       'Next Minor Version',
-       'Irrelevant'
-];
-
-if(!file_exists("/tmp/versions.php") || filemtime("/tmp/versions.php") < 
$_SERVER['REQUEST_TIME'] - 3600) {
-       $versions = buildVersions();
-       $versions_data = var_export($versions, true);
-       file_put_contents("/tmp/versions.php", '<?php $versions = 
'.$versions_data.';');
-} else {
-       include "/tmp/versions.php";
-}
-
-$versions = array_merge($versions, $custom_versions);
-
-function buildVersions() {
-       $dev_versions = 
json_decode(file_get_contents('https://qa.php.net/api.php?type=qa-releases&format=json&only=dev_versions'));
-
-       $versions = [];
-
-       $date = date('Y-m-d');
-       $default_versions = [
-               "Git-{$date} (snap)",
-               "Git-{$date} (Git)",
-       ];
-
-       foreach ($dev_versions as $dev_version) {
-               $dev_version_parts = parseVersion($dev_version);
-
-               // if it is a dev version, then add that branch, add the 
minor-1 version, if it's appropriate
-               if ($dev_version_parts['type'] == 'dev') {
-                       if 
(!isset($versions[$dev_version_parts['major']][$dev_version_parts['minor']])) {
-                               
$versions[$dev_version_parts['major']][$dev_version_parts['minor']] = [];
-                       }
-               }
-               // then it is a qa version (alpha|beta|rc)
-               else {
-                       
$versions[$dev_version_parts['major']][$dev_version_parts['minor']][$dev_version_parts['micro']]
 = $dev_version_parts;
-                       
ksort($versions[$dev_version_parts['major']][$dev_version_parts['minor']]);
-               }
-       }
-
-       $stable_releases = 
json_decode(file_get_contents('https://php.net/releases/active.php'), true);
-       foreach ($stable_releases as $major => $major_releases) {
-               foreach ($major_releases as $release) {
-                       $version_parts = parseVersion($release['version']);
-                       
$versions[$version_parts['major']][$version_parts['minor']][$version_parts['micro']]
 = $version_parts;
-                       
ksort($versions[$version_parts['major']][$version_parts['minor']]);
-               }
-       }
-
-       $flat_versions = [];
-
-       // add master to the end of the list
-       foreach ($default_versions as $default_version) {
-               $flat_versions[] = 'master-'.$default_version;
-       }
-
-       // add the fetched versions to the list
-       foreach ($versions as $major_number => $major) {
-               foreach ($major as $minor_number => $minor) {
-                       // add the default versions to ever minor branch
-                       foreach ($default_versions as $default_version) {
-                               $flat_versions[] = 
$major_number.'.'.$minor_number.$default_version;
-                       }
-                       foreach ($minor as $micro_number => $micro) {
-                               $flat_versions[] = $micro['original_version'];
-                       }
-               }
-       }
-
-       // reverse the order, this makes it descending
-       $flat_versions = array_reverse($flat_versions);
-
-       return $flat_versions;
-}
-
-function parseVersion($version){
-       $version_parts  = [];
-       $raw_parts      = [];
-       
preg_match('#(?P<major>\d+)\.(?P<minor>\d+).(?P<micro>\d+)[-]?(?P<type>RC|alpha|beta|dev)?(?P<number>[\d]?).*#ui',
 $version, $raw_parts);
-       $version_parts = [
-               'major'                 => $raw_parts['major'],
-               'minor'                 => $raw_parts['minor'],
-               'micro'                 => $raw_parts['micro'],
-               'type'                  => 
strtolower($raw_parts['type']?$raw_parts['type']:'stable'),
-               'number'                => $raw_parts['number'],
-               'original_version'      => $version,
-       ];
-       return $version_parts;
-}
diff --git a/src/Utils/Cache.php b/src/Utils/Cache.php
new file mode 100644
index 0000000..2cf29a5
--- /dev/null
+++ b/src/Utils/Cache.php
@@ -0,0 +1,157 @@
+<?php
+
+namespace App\Utils;
+
+/**
+ * A simple cache service for storing data in file(s).
+ */
+class Cache
+{
+    /**
+     * Pool of items.
+     *
+     * @var array
+     */
+    private $pool = [];
+
+    /**
+     * Temporary cache directory where data is stored.
+     *
+     * @var string
+     */
+    private $dir;
+
+    /**
+     * Default time after cache file is considered expired in seconds.
+     */
+    public const TTL = 3600;
+
+    /**
+     * Class constructor.
+     */
+    public function __construct(string $dir)
+    {
+        $this->dir = $dir;
+
+        // Create cache directory if it doesn't exist.
+        if (!file_exists($this->dir)) {
+            mkdir($this->dir, 0777, true);
+            chmod($this->dir, 0777);
+        }
+
+        // Validate cache directory
+        if (!is_dir($this->dir)) {
+            throw new \Exception($this->dir.' is not a valid directory.');
+        }
+    }
+
+    /**
+     * Write data to cache file.
+     */
+    public function set(string $key, $data, int $ttl = self::TTL): void
+    {
+        if (!$this->validateKey($key)) {
+            throw new Exception('Key name '.$key.' is invalid.');
+        }
+
+        $item = [time() + $ttl, serialize($data)];
+        $this->pool[$key] = $data;
+
+        $string = '<?php return '.var_export($item, true).";\n";
+
+        file_put_contents($this->dir.'/'.$key.'.php', $string);
+    }
+
+    /**
+     * Check if item has been cached and is available.
+     */
+    public function has(string $key): bool
+    {
+        if (isset($this->pool[$key])) {
+            return true;
+        }
+
+        $file = $this->dir.'/'.$key.'.php';
+
+        if (!is_file($file)) {
+            return false;
+        }
+
+        $data = require $file;
+
+        if (is_array($data) && isset($data[0]) && is_int($data[0]) && time() < 
$data[0]) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Get data from the cache pool.
+     */
+    public function get(string $key): ?array
+    {
+        if (isset($this->pool[$key])) {
+            return $this->pool[$key];
+        }
+
+        $file = $this->dir.'/'.$key.'.php';
+
+        if (is_file($file)) {
+            $data = require $file;
+            $this->pool[$key] = unserialize($data[1]);
+
+            return $this->pool[$key];
+        }
+
+        return null;
+    }
+
+    /**
+     * Wipes entire cache.
+     */
+    public function clear(): bool
+    {
+        $success = true;
+
+        $this->pool = [];
+
+        foreach (new \DirectoryIterator($this->dir) as $fileInfo) {
+            if ($fileInfo->isDot()) {
+                continue;
+            }
+
+            if (!unlink($fileInfo->getRealPath())) {
+                $success = false;
+            }
+        }
+
+        return $success;
+    }
+
+    /**
+     * Delete item from the cache.
+     */
+    public function delete(string $key): bool
+    {
+        $success = true;
+
+        unset($this->pool[$key]);
+
+        $file = $this->dir.'/'.$key.'.php';
+
+        if (is_file($file) && !unlink($file)) {
+            $success = false;
+        }
+
+        return $success;
+    }
+
+    /**
+     * Validate key.
+     */
+    private function validateKey(string $key): bool
+    {
+        return (bool) preg_match('/[a-z\_\-0-9]/i', $key);
+    }
+}
diff --git a/src/Utils/ComposerScripts.php b/src/Utils/ComposerScripts.php
new file mode 100644
index 0000000..769856d
--- /dev/null
+++ b/src/Utils/ComposerScripts.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace App\Utils;
+
+use Composer\Script\Event;
+
+/**
+ * Service for running Composer scripts when installing application.
+ */
+class ComposerScripts
+{
+    private static $cacheDir = __DIR__.'/../../var/cache';
+    private static $uploadsDir = __DIR__.'/../../uploads';
+
+    /**
+     * Create a default configuration settings for the development environment.
+     */
+    public static function installConfig(Event $event)
+    {
+        $sampleFile = __DIR__.'/../../local_config.php.sample';
+        $targetFile = __DIR__.'/../../local_config.php';
+
+        if ($event->isDevMode() && !file_exists($targetFile)) {
+            copy($sampleFile, $targetFile);
+        }
+    }
+
+    /**
+     * Create application temporary and upload directories which are not 
tracked
+     * in Git.
+     */
+    public static function createDirectories(Event $event)
+    {
+        if (!$event->isDevMode()) {
+            return;
+        }
+
+        $vendorDir = $event->getComposer()->getConfig()->get('vendor-dir');
+
+        require_once $vendorDir.'/autoload.php';
+
+        if (!file_exists(self::$cacheDir)) {
+            mkdir(self::$cacheDir, 0777, true);
+            chmod(self::$cacheDir, 0777);
+        }
+
+        if (!file_exists(self::$uploadsDir)) {
+            mkdir(self::$uploadsDir, 0777, true);
+            chmod(self::$uploadsDir, 0777);
+        }
+    }
+}
diff --git a/src/Utils/Versions/Client.php b/src/Utils/Versions/Client.php
new file mode 100644
index 0000000..bf91a9c
--- /dev/null
+++ b/src/Utils/Versions/Client.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Utils\Versions;
+
+/**
+ * API client for sending requests to PHP API pages.
+ */
+class Client
+{
+    /**
+     * API URL for fetching development PHP versions.
+     *
+     * @var string
+     */
+    private $devVersionsUrl = 
'https://qa.php.net/api.php?type=qa-releases&format=json&only=dev_versions';
+
+    /**
+     * API URL for fetching active PHP versions.
+     *
+     * @var string
+     */
+    private $stableVersionsUrl = 'https://php.net/releases/active.php';
+
+    /**
+     * Fetches data from remote URL.
+     */
+    public function fetchDevVersions(): array
+    {
+        $json = file_get_contents($this->devVersionsUrl);
+
+        return json_decode($json, true);
+    }
+
+    /**
+     * Fetch stable versions from remote URL.
+     */
+    public function fetchStableVersions(): array
+    {
+        $json = file_get_contents($this->stableVersionsUrl);
+
+        return json_decode($json, true);
+    }
+}
diff --git a/src/Utils/Versions/Generator.php b/src/Utils/Versions/Generator.php
new file mode 100644
index 0000000..0631c46
--- /dev/null
+++ b/src/Utils/Versions/Generator.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace App\Utils\Versions;
+
+use App\Utils\Cache;
+
+/**
+ * Service for retrieving a list of valid PHP versions when reporting bugs. PHP
+ * versions have format MAJOR.MINOR.MICRO{TYPE} where TYPE is one of alpha,
+ * beta, RC, or dev.
+ *
+ * Stable releases are pulled from the https://php.net. The RC and dev versions
+ * are pulled from the https://qa.php.net.
+ *
+ * To add a new PHP version add it to:
+ * https://git.php.net/?p=web/qa.git;a=blob;f=include/release-qa.php
+ *
+ * The versions are weighted by the following criteria:
+ * - major+minor version desc (7>5.4>5.3>master)
+ * - Between minor versions ordering is done by the micro version if available.
+ *   First the QA releases: alpha/beta/rc, then stable, then nightly versions
+ *   (Git, snaps). Snaps are more or less Windows snapshot builds.
+ *
+ * The result is cached for 1 hour into a temporary file.
+ */
+class Generator
+{
+    /**
+     * PHP API pages client.
+     *
+     * @var Client
+     */
+    private $client;
+
+    /**
+     * Cache service for storing fetched versions.
+     *
+     * @var Cache
+     */
+    private $cache;
+
+    /**
+     * Time after cache file is considered expired in seconds.
+     */
+    private const TTL = 3600;
+
+    /**
+     * Additional versions appended to the list of generated versions.
+     */
+    private const APPENDICES = [
+        'Next Major Version',
+        'Next Minor Version',
+        'Irrelevant',
+    ];
+
+    /**
+     * Class constructor.
+     */
+    public function __construct(Client $client, Cache $cache)
+    {
+        $this->client = $client;
+        $this->cache = $cache;
+    }
+
+    /**
+     * Get a list of valid PHP versions. Versions are cached for efficiency.
+     */
+    public function getVersions(): array
+    {
+        if (!$this->cache->has('versions')) {
+            $this->cache->set('versions', $this->generateVersions(), 
self::TTL);
+        }
+
+        return $this->cache->get('versions');
+    }
+
+    /**
+     * Return fetched and processed versions.
+     */
+    private function generateVersions(): array
+    {
+        $versions = array_merge($this->getDevVersions(), 
$this->getStableVersions());
+        rsort($versions);
+
+        // Get minor branches (PHP 7.2, PHP 7.3, etc)
+        $branches = [];
+        foreach ($versions as $version) {
+            $parts = $this->parseVersion($version);
+            $branch = $parts['major'].'.'.$parts['minor'];
+            $branches[$branch] = $branch;
+        }
+
+        $sorted = [];
+
+        // Add versions grouped by branches
+        foreach ($branches as $branch) {
+            foreach ($versions as $version) {
+                $parts = $this->parseVersion($version);
+                if ($parts['major'].'.'.$parts['minor'] === $branch) {
+                    $sorted[] = $version;
+                }
+            }
+
+            // Append Git and snaps for each branch
+            foreach ($this->getAffixes() as $item) {
+                $sorted[] = $branch.$item;
+            }
+        }
+
+        // Append master branch to the versions list
+        foreach ($this->getAffixes() as $item) {
+            $sorted[] = 'master-'.$item;
+        }
+
+        // Append human readable versions
+        $sorted = array_merge($sorted, self::APPENDICES);
+
+        return $sorted;
+    }
+
+    /**
+     * Get version affixes such as Git or snapshots.
+     */
+    protected function getAffixes(): array
+    {
+        $date = date('Y-m-d');
+
+        return [
+            'Git-'.$date.' (Git)',
+            'Git-'.$date.' (snap)',
+        ];
+    }
+
+    /**
+     * Get alpha, beta and RC versions.
+     */
+    private function getDevVersions(): array
+    {
+        $versions = [];
+
+        foreach ($this->client->fetchDevVersions() as $version) {
+            $parts = $this->parseVersion($version);
+            if ('dev' !== $parts['type']) {
+                $versions[] = $version;
+            }
+        }
+
+        return $versions;
+    }
+
+    /**
+     * Get stable versions.
+     */
+    private function getStableVersions(): array
+    {
+        $versions = [];
+
+        foreach ($this->client->fetchStableVersions() as $releases) {
+            foreach ($releases as $release) {
+                $versions[] = $release['version'];
+            }
+        }
+
+        return $versions;
+    }
+
+    /**
+     * Parse the versions data string and convert it to array.
+     */
+    private function parseVersion(string $version): array
+    {
+        $matches = [];
+        
preg_match('#(?P<major>\d+)\.(?P<minor>\d+).(?P<micro>\d+)[-]?(?P<type>RC|alpha|beta|dev)?(?P<number>[\d]?).*#ui',
 $version, $matches);
+        $parts = [
+            'major'    => $matches['major'],
+            'minor'    => $matches['minor'],
+            'micro'    => $matches['micro'],
+            'type'     => strtolower($matches['type'] ? $matches['type'] : 
'stable'),
+            'number'   => $matches['number'],
+            'original' => $version,
+        ];
+
+        return $parts;
+    }
+}
diff --git a/tests/Utils/CacheTest.php b/tests/Utils/CacheTest.php
new file mode 100644
index 0000000..0e045ed
--- /dev/null
+++ b/tests/Utils/CacheTest.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Tests\Utils;
+
+use App\Utils\Cache;
+use PHPUnit\Framework\TestCase;
+
+class CacheTest extends TestCase
+{
+    private $cacheDir = __DIR__.'/../../var/cache/test';
+    private $cache;
+
+    public function setUp()
+    {
+        $this->cache = new Cache($this->cacheDir);
+        $this->cache->clear();
+    }
+
+    public function tearDown()
+    {
+        $this->cache->clear();
+        rmdir($this->cacheDir);
+    }
+
+    public function testHas()
+    {
+        $this->assertFalse($this->cache->has('foo'));
+
+        $this->cache->set('foo', [1, 2, 3]);
+        $this->assertTrue($this->cache->has('foo'));
+    }
+
+    public function testDelete()
+    {
+        $this->cache->set('bar', [1, 2, 3]);
+        $this->assertFileExists($this->cacheDir.'/bar.php');
+
+        $this->cache->delete('bar');
+        $this->assertFalse(file_exists($this->cacheDir.'/bar.php'));
+    }
+}
diff --git a/tests/Utils/Versions/ClientTest.php 
b/tests/Utils/Versions/ClientTest.php
new file mode 100644
index 0000000..54f3ec0
--- /dev/null
+++ b/tests/Utils/Versions/ClientTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Tests\Utils\Versions;
+
+use PHPUnit\Framework\TestCase;
+use App\Utils\Versions\Client;
+
+class ClientTest extends TestCase
+{
+    private $client;
+
+    public function setUp()
+    {
+        $this->client = new Client();
+
+        $reflection = new \ReflectionClass($this->client);
+
+        $devVersionsUrl = $reflection->getProperty('devVersionsUrl');
+        $devVersionsUrl->setAccessible(true);
+        $devVersionsUrl->setValue($this->client, 
__DIR__.'/../../mock/responses/dev-body.txt');
+
+        $stableVersionsUrl = $reflection->getProperty('stableVersionsUrl');
+        $stableVersionsUrl->setAccessible(true);
+        $stableVersionsUrl->setValue($this->client, 
__DIR__.'/../../mock/responses/stable-body.txt');
+    }
+
+    public function testFetchDevVersions()
+    {
+        $this->assertInternalType('array', $this->client->fetchDevVersions());
+    }
+
+    public function testFetchStableVersions()
+    {
+        $this->assertInternalType('array', 
$this->client->fetchStableVersions());
+    }
+}
diff --git a/tests/Utils/Versions/GeneratorTest.php 
b/tests/Utils/Versions/GeneratorTest.php
new file mode 100644
index 0000000..386a5b6
--- /dev/null
+++ b/tests/Utils/Versions/GeneratorTest.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace App\Tests\Utils\Versions;
+
+use PHPUnit\Framework\TestCase;
+use App\Utils\Versions\Generator;
+use App\Utils\Versions\Client;
+use App\Utils\Cache;
+
+class GeneratorTest extends TestCase
+{
+    private $cacheDir = __DIR__.'/../../../var/cache/test';
+    private $cache;
+    private $client;
+    private $generator;
+
+    public function setUp()
+    {
+        $this->cache = new Cache($this->cacheDir);
+        $this->cache->clear();
+
+        // The results returned by the client depend on the remote URLs so we
+        // mock the returned results.
+        $this->client = $this->getMockBuilder(Client::class)
+            ->setMethods(['fetchDevVersions', 'fetchStableVersions'])
+            ->getMock();
+
+        $this->client->expects($this->once())
+            ->method('fetchDevVersions')
+            
->will($this->returnValue(json_decode(file_get_contents(__DIR__.'/../../mock/responses/dev-body.txt',
 true))));
+
+        $this->client->expects($this->once())
+            ->method('fetchStableVersions')
+            
->will($this->returnValue(json_decode(file_get_contents(__DIR__.'/../../mock/responses/stable-body.txt'),
 true)));
+
+        $this->generator = $this->getMockBuilder(Generator::class)
+            ->setConstructorArgs([$this->client, $this->cache])
+            ->setMethods(['getAffixes'])
+            ->getMock();
+
+        // The extra versions are always date dependant so we mock it to 
include
+        // static date done on the tests day.
+        $date = '2018-12-26';
+        $this->generator->expects($this->any())
+            ->method('getAffixes')
+            ->will($this->returnValue(['Git-'.$date.' (snap)', 'Git-'.$date.' 
(Git)',]));
+    }
+
+    public function tearDown()
+    {
+        $this->cache->clear();
+        rmdir($this->cacheDir);
+    }
+
+    public function testVersions()
+    {
+        $versions = $this->generator->getVersions();
+
+        $this->assertInternalType('array', $versions);
+        $this->assertGreaterThan(5, count($versions));
+
+        $fixture = require __DIR__.'/../../fixtures/versions/versions.php';
+        $cached = require $this->cacheDir.'/versions.php';
+
+        $this->assertEquals($fixture[1], $cached[1]);
+        $this->assertContains('Next Major Version', $versions);
+        $this->assertContains('Irrelevant', $versions);
+        $this->assertContains('7.2.14RC1', $versions);
+    }
+}
diff --git a/tests/fixtures/versions/versions.php 
b/tests/fixtures/versions/versions.php
new file mode 100644
index 0000000..6cae195
--- /dev/null
+++ b/tests/fixtures/versions/versions.php
@@ -0,0 +1,4 @@
+<?php return array (
+  0 => 1545811329,
+  1 => 'a:22:{i:0;s:8:"7.3.1RC1";i:1;s:5:"7.3.0";i:2;s:24:"7.3Git-2018-12-26 
(snap)";i:3;s:23:"7.3Git-2018-12-26 
(Git)";i:4;s:9:"7.2.14RC1";i:5;s:6:"7.2.13";i:6;s:24:"7.2Git-2018-12-26 
(snap)";i:7;s:23:"7.2Git-2018-12-26 
(Git)";i:8;s:6:"7.1.25";i:9;s:24:"7.1Git-2018-12-26 
(snap)";i:10;s:23:"7.1Git-2018-12-26 
(Git)";i:11;s:6:"7.0.33";i:12;s:24:"7.0Git-2018-12-26 
(snap)";i:13;s:23:"7.0Git-2018-12-26 
(Git)";i:14;s:6:"5.6.39";i:15;s:24:"5.6Git-2018-12-26 
(snap)";i:16;s:23:"5.6Git-2018-12-26 (Git)";i:17;s:28:"master-Git-2018-12-26 
(snap)";i:18;s:27:"master-Git-2018-12-26 (Git)";i:19;s:18:"Next Major 
Version";i:20;s:18:"Next Minor Version";i:21;s:10:"Irrelevant";}',
+);
diff --git a/tests/mock/responses/dev-body.txt 
b/tests/mock/responses/dev-body.txt
new file mode 100644
index 0000000..77697a6
--- /dev/null
+++ b/tests/mock/responses/dev-body.txt
@@ -0,0 +1 @@
+["5.6.40-dev","7.0.33-dev","7.1.25-dev","7.2.14-dev","7.2.14RC1","7.3.1-dev","7.3.1RC1"]
diff --git a/tests/mock/responses/stable-body.txt 
b/tests/mock/responses/stable-body.txt
new file mode 100644
index 0000000..d4daa41
--- /dev/null
+++ b/tests/mock/responses/stable-body.txt
@@ -0,0 +1 @@
+{"5":{"5.6":{"announcement":true,"source":[{"filename":"php-5.6.39.tar.bz2","name":"PHP
 5.6.39 
(tar.bz2)","sha256":"b3db2345f50c010b01fe041b4e0f66c5aa28eb325135136f153e18da01583ad5","date":"06
 Dec 2018"},{"filename":"php-5.6.39.tar.gz","name":"PHP 5.6.39 
(tar.gz)","sha256":"127b122b7d6c7f3c211c0ffa554979370c3131196137404a51a391d8e2e9c7bb","date":"06
 Dec 2018"},{"filename":"php-5.6.39.tar.xz","name":"PHP 5.6.39 
(tar.xz)","sha256":"8147576001a832ff3d03cb2980caa2d6b584a10624f87ac459fcd3948c6e4a10","date":"06
 Dec 
2018"}],"version":"5.6.39"}},"7":{"7.0":{"announcement":true,"source":[{"filename":"php-7.0.33.tar.bz2","name":"PHP
 7.0.33 
(tar.bz2)","sha256":"4933ea74298a1ba046b0246fe3771415c84dfb878396201b56cb5333abe86f07","date":"06
 Dec 2018"},{"filename":"php-7.0.33.tar.gz","name":"PHP 7.0.33 
(tar.gz)","sha256":"d71a6ecb6b13dc53fed7532a7f8f949c4044806f067502f8fb6f9facbb40452a","date":"06
 Dec 2018"},{"filename":"php-7.0.33.tar.xz","name":"PHP 7.0.33 
(tar.xz)","sha256":"ab8c5be6e32b1f8d032909dedaaaa4bbb1a209e519abb01a52ce3914f9a13d96","date":"06
 Dec 
2018"}],"version":"7.0.33"},"7.1":{"announcement":true,"source":[{"filename":"php-7.1.25.tar.bz2","name":"PHP
 7.1.25 
(tar.bz2)","sha256":"002cdc880ac7cfaede2c389204d366108847db0f3ac72edf1ba95c0577f9aaac","date":"06
 Dec 2018"},{"filename":"php-7.1.25.tar.gz","name":"PHP 7.1.25 
(tar.gz)","sha256":"7dc40e202140e8b4fb3d992c15a68d98dc06b805e6b218497d260abbe51f5958","date":"06
 Dec 2018"},{"filename":"php-7.1.25.tar.xz","name":"PHP 7.1.25 
(tar.xz)","sha256":"0fd8dad1903cd0b2d615a1fe4209f99e53b7292403c8ffa1919c0f4dd1eada88","date":"06
 Dec 
2018"}],"version":"7.1.25"},"7.2":{"announcement":true,"source":[{"filename":"php-7.2.13.tar.bz2","name":"PHP
 7.2.13 
(tar.bz2)","sha256":"5b4a46fb76491bcd3eee1213773382e570f6ecf9b22d623b24e2822298b3e92d","date":"06
 Dec 2018"},{"filename":"php-7.2.13.tar.gz","name":"PHP 7.2.13 
(tar.gz)","sha256":"e563cee406b1ec96649c22ed2b35796cfe4e9aa9afa6eab6be4cf2fe5d724744","date":"06
 Dec 2018"},{"filename":"php-7.2.13.tar.xz","name":"PHP 7.2.13 
(tar.xz)","sha256":"14b0429abdb46b65c843e5882c9a8c46b31dfbf279c747293b8ab950c2644a4b","date":"06
 Dec 
2018"}],"version":"7.2.13"},"7.3":{"announcement":true,"source":[{"filename":"php-7.3.0.tar.bz2","name":"PHP
 7.3.0 
(tar.bz2)","sha256":"7a267daec9969a997c5c8028c350229646748e0fcc71e2f2dbb157ddcee87c67","date":"06
 Dec 2018"},{"filename":"php-7.3.0.tar.gz","name":"PHP 7.3.0 
(tar.gz)","sha256":"391bd0f91d9bdd01ab47ef9607bad8c65e35bc9bb098fb7777b2556e2c847b11","date":"06
 Dec 2018"},{"filename":"php-7.3.0.tar.xz","name":"PHP 7.3.0 
(tar.xz)","sha256":"7d195cad55af8b288c3919c67023a14ff870a73e3acc2165a6d17a4850a560b5","date":"06
 Dec 2018"}],"version":"7.3.0"}}}
diff --git a/www/report.php b/www/report.php
index b5e4ebe..9a7b61b 100644
--- a/www/report.php
+++ b/www/report.php
@@ -2,9 +2,12 @@
 
 use App\Repository\PackageRepository;
 use App\Repository\ReasonRepository;
+use App\Utils\Cache;
 use App\Utils\Captcha;
 use App\Utils\PatchTracker;
 use App\Utils\Uploader;
+use App\Utils\Versions\Client;
+use App\Utils\Versions\Generator;
 
 // Obtain common includes
 require_once '../include/prepend.php';
@@ -22,7 +25,11 @@ $pseudo_pkgs = 
$packageRepository->findEnabled($_GET['project'] ?? '');
 // Authenticate
 bugs_authenticate($user, $pw, $logged_in, $user_flags);
 
-require "{$ROOT_DIR}/include/php_versions.php";
+$versionsClient = new Client();
+$cacheDir = (defined('DEVBOX') && true === DEVBOX) ? __DIR__.'/../var/cache' : 
'/tmp';
+$cache = new Cache($cacheDir);
+$versionsGenerator = new Generator($versionsClient, $cache);
+$versions = $versionsGenerator->getVersions();
 
 // captcha is not necessary if the user is logged in
 if (!$logged_in) {
-- 
PHP Webmaster List Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to