jrobinsonc
11/6/2018 - 3:12 PM

SatisPress Downloader

Class to download packages from a SatisPress installation.

SatisPress Downloader

Usage

$downloader = new \Jrdev\SatisPressDownloader('https://domain.tld/satispress/packages.json', [
    // 'auth' => ['user', 'pass'], // Basic auth, if required.
    // 'baseDir' => __DIR__, // Base directory to create the sub-directories `cache` and `packages`.
    // 'versions' => 1, // How many versions of each package should be downloaded: 1 = the latest, 2 = the two latest versions, etc.
]);

$downloader->run();
<?php

namespace Jrdev;

use ErrorException;
use stdClass;

class SatisPressDownloader
{
    protected $config;

    public function __construct(string $satisJson, array $extraConf = [])
    {
        $this->config = new stdClass;

        $this->config->satisJson = $satisJson;

        $this->config->baseDir = $extraConf['baseDir'] ?? (sys_get_temp_dir() . '/'
            . substr(strrchr(static::class, '\\'), 1));

        $this->config->cacheDir = $this->config->baseDir . '/cache';
        $this->config->packagesDir = $this->config->baseDir . '/packages';

        // Create required directories if they don't exist.
        foreach ([
            $this->config->cacheDir,
            $this->config->packagesDir
        ] as $dirPath) {
            if (!is_dir($dirPath) && !mkdir($dirPath, 0755, true)) {
                throw new ErrorException('Directory cannot be created: '. basename($dirPath));
            }
        }

        // How many versions of each package should be stored.
        $this->config->versions = $extraConf['versions'] ?? 1;

        // If authentication is needed to download the packages.
        $this->config->auth = null;

        if (isset($extraConf['auth']) && !empty($extraConf['auth'])) {
            $this->config->auth = new stdClass;

            $this->config->auth->type = 'basic';
            $this->config->auth->creds = $extraConf['auth'];
        }
    }

    protected function copyFile(string $src, string $dest)
    {
        $srcHandle = fopen($src, 'r');

        if (false === $srcHandle) {
            throw new ErrorException('File cannot be downloaded: ' . $src);
        }

        $destDir = dirname($dest);

        if (!is_dir($destDir) && !mkdir($destDir, 0755, true)) {
            throw new ErrorException('Directory cannot be created: '. basename($destDir));
        }

        $destHandle = fopen($dest, 'w');

        if (false === $destHandle) {
            fclose($srcHandle);
            throw new ErrorException('File cannot be created: ' . $dest);
        }

        while (!feof($srcHandle)) {
            $chunk = fread($srcHandle, 4096);
            
            fwrite($destHandle, $chunk);
        }

        fclose($destHandle);
        fclose($srcHandle);
    }

    protected function fetchPackages(): string
    {
        $cacheFile = $this->config->cacheDir . '/packages.json';

        if (!file_exists($cacheFile)) {
            $this->copyFile($this->config->satisJson, $cacheFile);
        }

        return file_get_contents($cacheFile);
    }

    protected function parsePackage(stdClass $packageData): array
    {
        $output = [];
        $versionCount = 0;

        foreach ($packageData as $packageVersion) {
            // For now, we only accept ZIPs.
            if ($packageVersion->dist->type !== 'zip') {
                throw new ErrorException('Unknown file type: '. $packageVersion->dist->type
                    . ' for package ' . $packageVersion->name);
            }

            $package = new stdClass;

            $package->name = str_replace('satispress/', '', $packageVersion->name);
            $package->name .= "-{$packageVersion->version}";
            $package->name .= ".{$packageVersion->dist->type}";

            $package->downloadUrl = $packageVersion->dist->url;
            
            if (!is_null($this->config->auth)) {
                $authStr = implode(':', $this->config->auth->creds);
                $package->downloadUrl = str_replace('://', '://' . $authStr . '@', $package->downloadUrl);
            }
            
            $package->type = $packageVersion->type;

            $output[] = $package;

            $versionCount++;

            if ($this->config->versions === $versionCount) {
                break;
            }
        }

        return $output;
    }

    protected function downloadPackage(array $package)
    {
        foreach ($package as $packageVersion) {
            $filePath = "{$this->config->packagesDir}/{$packageVersion->type}/{$packageVersion->name}";

            if (file_exists($filePath)) {
                return;
            }

            $this->copyFile($packageVersion->downloadUrl, $filePath);
        }
    }

    protected function parseJson(string $jsonData): \Generator
    {
        $parsedJson = json_decode($jsonData);

        if (null === $parsedJson) {
            throw new ErrorException('JSON data cannot be parsed.');
        }

        foreach ($parsedJson->packages as $package) {
            yield $this->parsePackage($package);
        }
    }

    public function run()
    {
        // Run.
        $jsonData = $this->fetchPackages();
        
        foreach ($this->parseJson($jsonData) as $package) {
            $this->downloadPackage($package);
        }
    }
}