clrghouz/app/Classes/File/Receive.php
2021-06-13 01:32:22 +10:00

173 lines
4.3 KiB
PHP

<?php
namespace App\Classes\File;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
/**
* Object representing the files we are receiving
*
* @property-read resource $fd
* @property-read int total_recv
* @property-read int total_recv_bytes
*/
final class Receive extends Item
{
private Collection $list;
private ?Item $receiving;
private mixed $f; // File descriptor
private int $start; // Time we started receiving
private int $file_pos; // Current write pointer
public function __construct()
{
// Initialise our variables
$this->list = collect();
$this->receiving = NULL;
$this->file_pos = 0;
$this->f = NULL;
}
public function __get($key)
{
switch ($key) {
case 'fd':
return is_resource($this->f);
case 'filepos':
return $this->file_pos;
case 'mtime':
case 'name':
case 'size':
return $this->receiving ? $this->receiving->{'file_'.$key} : NULL;
case 'to_get':
return $this->list
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === FALSE; })
->count();
case 'total_recv':
return $this->list
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === TRUE; })
->count();
case 'total_recv_bytes':
return $this->list
->filter(function($item) { return ($item->action & self::I_RECV) && $item->received === TRUE; })
->sum(function($item) { return $item->file_size; });
default:
throw new Exception('Unknown key: '.$key);
}
}
/**
* Close the file descriptor for our incoming file
*
* @throws Exception
*/
public function close(): void
{
if (! $this->f)
throw new Exception('No file to close');
if (! $this->file_pos != $this->receiving->file_size)
Log::warning(sprintf('%s: - Closing [%s], but missing [%d] bytes',__METHOD__,$this->receiving->file_name,$this->receiving->file_size-$this->file_pos));
$this->receiving->received = TRUE;
$end = time()-$this->start;
Log::debug(sprintf('%s: - Closing [%s], received in [%d]',__METHOD__,$this->receiving->file_name,$end));
fclose($this->f);
$this->file_pos = 0;
$this->receiving = NULL;
$this->f = NULL;
}
/**
* Open the file descriptor to receive a file
*
* @param bool $check
* @return bool
* @throws Exception
*/
public function open(bool $check=FALSE): bool
{
Log::debug(sprintf('%s: + Start [%d]',__METHOD__,$check));
// Check we can open this file
// @todo
// @todo implement return 2 - SKIP file
// @todo implement return 4 - SUSPEND(?) file
if ($check) {
return 0;
}
if (! $this->receiving)
throw new Exception('No files currently receiving');
$this->file_pos = 0;
$this->start = time();
Log::debug(sprintf('%s: - Opening [%s]',__METHOD__,$this->receiving->recvas));
$this->f = fopen($this->receiving->recvas,'wb');
if (! $this->f) {
Log::error(sprintf('%s: ! Unable to open file [%s] for writing',__METHOD__,$this->receiving->file_name));
return 3; // @todo change to const
}
Log::info(sprintf('%s: = End - File [%s] opened for writing',__METHOD__,$this->receiving->file_name));
return 0; // @todo change to const
}
/**
* Add a new file to receive
*
* @param array $file
* @throws Exception
*/
public function new(array $file): void
{
Log::debug(sprintf('%s: + Start',__METHOD__),['file'=>$file]);
if ($this->receiving)
throw new Exception('Can only have 1 file receiving at a time');
$o = new Item($file,self::I_RECV);
$this->list->push($o);
$this->receiving = $o;
}
/**
* Write data to the file we are receiving
*
* @param string $buf
* @return int
* @throws Exception
*/
public function write(string $buf): int
{
if (! $this->f)
throw new Exception('No file open for read');
if ($this->file_pos+strlen($buf) > $this->receiving->file_size)
throw new Exception(sprintf('Too many bytes received [%d] (%d)?',$this->file_pos+strlen($buf),$this->receiving->file_size));
$rc = fwrite($this->f,$buf);
if ($rc === FALSE)
throw new FileException('Error while writing to file');
$this->file_pos += $rc;
Log::debug(sprintf('%s: - Write [%d] bytes, file pos now [%d] of [%d]',__METHOD__,$rc,$this->file_pos,$this->receiving->file_size));
return $rc;
}
}