Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify filefetcher, remove chunking and use guzzle #26

Merged
merged 13 commits into from
Feb 17, 2023
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/vendor/
composer.lock
.idea
.vscode
.phpunit*
coverage
.phpunit.result.cache
13 changes: 11 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"minimum-stability": "dev",
"require-dev": {
"phpunit/phpunit": "^9.0",
"getdkan/mock-chain": "dev-master"
"getdkan/mock-chain": "dev-master",
"squizlabs/php_codesniffer": "@stable",
"mikey179/vfsstream": "^2.0@dev"
},
"authors": [
{
Expand All @@ -21,6 +23,13 @@
},
"require": {
"getdkan/procrastinator": "^4.0.0",
"ext-curl": "*"
"ext-curl": "*",
"guzzlehttp/guzzle": "^6.3"
},
"scripts": {
"phpunit": "./vendor/bin/phpunit",
"phpunit-coverage": "./vendor/bin/phpunit --coverage-html ./coverage",
"phpcs": "./vendor/bin/phpcs -ps",
"phpcbf": "./vendor/bin/phpcbf -p"
}
}
14 changes: 14 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="getdkan-file-fetcher">
<arg name="extensions" value="inc,install,module,php,profile,test,theme,yml"/>
<description>PHP CodeSniffer configuration for File Fetcher</description>

<exclude-pattern>.circleci/</exclude-pattern>
<exclude-pattern>.ddev/</exclude-pattern>
<exclude-pattern>vendor/</exclude-pattern>

<file>.</file>

<rule ref="PSR1"/>
<rule ref="PSR2"/>
</ruleset>
2 changes: 0 additions & 2 deletions src/FileFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use FileFetcher\Processor\Local;
use FileFetcher\Processor\ProcessorInterface;
use FileFetcher\Processor\Remote;
use FileFetcher\Processor\LastResort;
use Procrastinator\Job\AbstractPersistentJob;

/**
Expand Down Expand Up @@ -88,7 +87,6 @@ private static function getDefaultProcessors()
$processors = [];
$processors[Local::class] = new Local();
$processors[Remote::class] = new Remote();
$processors[LastResort::class] = new LastResort();
return $processors;
}

Expand Down
23 changes: 0 additions & 23 deletions src/LastResortException.php

This file was deleted.

24 changes: 0 additions & 24 deletions src/PhpFunctionsBridge.php

This file was deleted.

21 changes: 0 additions & 21 deletions src/PhpFunctionsBridgeTrait.php

This file was deleted.

16 changes: 9 additions & 7 deletions src/Processor/AbstractChunkedProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

namespace FileFetcher\Processor;

use FileFetcher\PhpFunctionsBridgeTrait;
use FileFetcher\TemporaryFilePathFromUrl;
use Procrastinator\Result;

abstract class AbstractChunkedProcessor implements ProcessorInterface
/**
* Base class for chunk-by-chunk file fetchers.
*
* @deprecated This is no longer used for included processors, but is included
* to maintain backwards compatibiliy with custom processors.
*
* @codeCoverageIgnore
*/
abstract class AbstractChunkedProcessor extends ProcessorBase implements ProcessorInterface
{
use PhpFunctionsBridgeTrait;
use TemporaryFilePathFromUrl;

abstract public function isServerCompatible(array $state): bool;
abstract protected function getFileSize(string $filePath): int;
abstract protected function getChunk(string $filePath, int $start, int $end);
Expand All @@ -19,7 +22,6 @@ abstract protected function getChunk(string $filePath, int $start, int $end);
*/
public function __construct()
{
$this->initializePhpFunctionsBridge();
}


Expand Down
139 changes: 4 additions & 135 deletions src/Processor/LastResort.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,144 +2,13 @@

namespace FileFetcher\Processor;

use FileFetcher\LastResortException;
use FileFetcher\PhpFunctionsBridgeTrait;
use FileFetcher\TemporaryFilePathFromUrl;
use Procrastinator\Result;

/**
* Class LastResort
*
* The "last resort" processor does a regular copy of a file if non of the safer options were possible. This
* processor will attempt at getting all of the data in one shot and placing it in a file.
* @deprecated No longer needed, but class name maintained for backwards
* compatibility.
*
* @package FileFetcher\Processor
* @codeCoverageIgnore
*/
class LastResort implements ProcessorInterface
class LastResort extends Local
{
use TemporaryFilePathFromUrl;
use PhpFunctionsBridgeTrait;

/**
* LastResort constructor.
*/
public function __construct()
{
$this->initializePhpFunctionsBridge();
}

public function isServerCompatible(array $state): bool
{
return true;
}

public function setupState(array $state): array
{
$state['total_bytes'] = PHP_INT_MAX;
$state['temporary'] = true;
$state['destination'] = $this->getTemporaryFilePath($state);

return $state;
}

public function isTimeLimitIncompatible(): bool
{
return true;
}

public function copy(array $state, Result $result, int $timeLimit = PHP_INT_MAX): array
{
list($from, $to) = $this->validateAndGetInfoFromState($state);

$bytesToRead = 10 * 1000 * 1000;
$bytesCopied = 0;

$fin = $this->ensureExistsForReading($from);
$fout = $this->ensureCreatingForWriting($to);

while (!feof($fin)) {
$bytesCopied += $this->readAndWrite(
$fin,
$fout,
$bytesToRead,
$state
);
}

$result->setStatus(Result::DONE);
fclose($fin);
fclose($fout);
$state['total_bytes_copied'] = $bytesCopied;
$state['total_bytes'] = $bytesCopied;

return ['state' => $state, 'result' => $result];
}

private function readAndWrite($fin, $fout, $bytesToRead, $state): int
{
list($from, $to) = $this->validateAndGetInfoFromState($state);

$bytesRead = $this->php->fread($fin, $bytesToRead);
if ($bytesRead === false) {
throw new LastResortException("reading from", $from);
}
$bytesWritten = fwrite($fout, $bytesRead);
if ($bytesWritten === false) {
throw new LastResortException("writing to", $to);
}
return $bytesWritten;
}

private function validateAndGetInfoFromState($state)
{
if (!isset($state['source']) && !isset($state['destination'])) {
throw new \Exception("Incorrect state missing source, destination, or both.");
}
return [$state['source'], $state['destination']];
}

/**
* Ensure the target file can be read from.
*
* @param string $from
* The target filename.
*
* @return false|resource
* @throws \FileFetcher\LastResortException
*/
private function ensureExistsForReading(string $from)
{
$fin = @$this->php->fopen($from, "rb");
if ($fin === false) {
throw new LastResortException("opening", $from);
}
return $fin;
}

/**
* Ensure the destination file can be created.
*
* @param string $to
* The destination filename.
*
* @return false|resource
* @throws \FileFetcher\LastResortException
*/
private function ensureCreatingForWriting(string $to)
{
// Delete destination first to avoid appending if existing.
$this->deleteFile($to);
$fout = $this->php->fopen($to, "w");
if ($fout === false) {
throw new LastResortException("creating", $to);
}
return $fout;
}

private function deleteFile($file)
{
if (file_exists($file)) {
unlink($file);
}
}
}
24 changes: 5 additions & 19 deletions src/Processor/Local.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,15 @@

namespace FileFetcher\Processor;

use FileFetcher\PhpFunctionsBridgeTrait;
use FileFetcher\TemporaryFilePathFromUrl;
use Procrastinator\Result;

class Local implements ProcessorInterface
class Local extends ProcessorBase implements ProcessorInterface
{

use PhpFunctionsBridgeTrait;
use TemporaryFilePathFromUrl;

/**
* Local constructor.
*/
public function __construct()
{
$this->initializePhpFunctionsBridge();
}

public function isServerCompatible(array $state): bool
{
$path = $state['source'];

if ($this->php->file_exists($path) && !$this->php->is_dir($path)) {
if (file_exists($path) && !is_dir($path)) {
return true;
}

Expand All @@ -34,7 +20,7 @@ public function isServerCompatible(array $state): bool
public function setupState(array $state): array
{
$state['total_bytes'] = PHP_INT_MAX;
$state['total_bytes'] = $this->php->filesize($state['source']);
$state['total_bytes'] = filesize($state['source']);
$state['temporary'] = true;
$state['destination'] = $this->getTemporaryFilePath($state);

Expand All @@ -48,9 +34,9 @@ public function isTimeLimitIncompatible(): bool

public function copy(array $state, Result $result, int $timeLimit = PHP_INT_MAX): array
{
$this->php->copy($state['source'], $state['destination']);
$state['total_bytes_copied'] = $this->php->filesize($state['destination']);
copy($state['source'], $state['destination']);
$result->setStatus(Result::DONE);
$state['total_bytes_copied'] = $state['total_bytes'] = filesize($state['destination']);

return ['state' => $state, 'result' => $result];
}
Expand Down
Loading