Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
localzet committed Jul 28, 2022
0 parents commit ab96af8
Show file tree
Hide file tree
Showing 29 changed files with 6,158 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
composer.lock
vendor
examples
build
test
logs
artifacts

.buildpath
.settings
.DS_Store
.phpunit*
.project
.vscode
.idea
.env

*.env
*.log
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

46 changes: 46 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "localzet/psr",
"description": "PSR Classes",
"type": "library",
"keywords": [
"localzet"
],
"license": "GPL-3.0-only",
"authors": [{
"name": "localzet",
"email": "[email protected]",
"homepage": "https://www.localzet.ru",
"role": "Developer"
}],
"support": {
"email": "[email protected]",
"docs": "https://localzet.gitbook.io"
},
"funding": [{
"type": "other",
"url": "https://www.tinkoff.ru/cf/Ad5MBNhodod"
}],
"require": {
"php": ">=5.4.0",
"psr/http-message": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"autoload": {
"psr-4": {
"localzet\\PSR\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
}
}
}
254 changes: 254 additions & 0 deletions src/PSR7/AppendStream.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
<?php

/**
* @package PSR Http Message
* @link https://localzet.gitbook.io
*
* @author localzet <[email protected]>
*
* @copyright Copyright (c) 2018-2020 Zorin Projects
* @copyright Copyright (c) 2020-2022 NONA Team
*
* @license https://www.localzet.ru/license GNU GPLv3 License
*/

namespace localzet\PSR\PSR7;

use Psr\Http\Message\StreamInterface;

/**
* Reads from multiple streams, one after the other.
*
* This is a read-only stream decorator.
*/
class AppendStream implements StreamInterface
{
/** @var StreamInterface[] Streams being decorated */
private $streams = [];

private $seekable = true;
private $current = 0;
private $pos = 0;

/**
* @param StreamInterface[] $streams Streams to decorate. Each stream must
* be readable.
*/
public function __construct(array $streams = [])
{
foreach ($streams as $stream) {
$this->addStream($stream);
}
}

public function __toString()
{
try {
$this->rewind();
return $this->getContents();
} catch (\Exception $e) {
return '';
}
}

/**
* Add a stream to the AppendStream
*
* @param StreamInterface $stream Stream to append. Must be readable.
*
* @throws \InvalidArgumentException if the stream is not readable
*/
public function addStream(StreamInterface $stream)
{
if (!$stream->isReadable()) {
throw new \InvalidArgumentException('Each stream must be readable');
}

// The stream is only seekable if all streams are seekable
if (!$stream->isSeekable()) {
$this->seekable = false;
}

$this->streams[] = $stream;
}

public function getContents()
{
return copy_to_string($this);
}

/**
* Closes each attached stream.
*
* {@inheritdoc}
*/
public function close()
{
$this->pos = $this->current = 0;
$this->seekable = true;

foreach ($this->streams as $stream) {
$stream->close();
}

$this->streams = [];
}

/**
* Detaches each attached stream.
*
* Returns null as it's not clear which underlying stream resource to return.
*
* {@inheritdoc}
*/
public function detach()
{
$this->pos = $this->current = 0;
$this->seekable = true;

foreach ($this->streams as $stream) {
$stream->detach();
}

$this->streams = [];
}

public function tell()
{
return $this->pos;
}

/**
* Tries to calculate the size by adding the size of each stream.
*
* If any of the streams do not return a valid number, then the size of the
* append stream cannot be determined and null is returned.
*
* {@inheritdoc}
*/
public function getSize()
{
$size = 0;

foreach ($this->streams as $stream) {
$s = $stream->getSize();
if ($s === null) {
return null;
}
$size += $s;
}

return $size;
}

public function eof()
{
return !$this->streams ||
($this->current >= count($this->streams) - 1 &&
$this->streams[$this->current]->eof());
}

public function rewind()
{
$this->seek(0);
}

/**
* Attempts to seek to the given position. Only supports SEEK_SET.
*
* {@inheritdoc}
*/
public function seek($offset, $whence = SEEK_SET)
{
if (!$this->seekable) {
throw new \RuntimeException('This AppendStream is not seekable');
} elseif ($whence !== SEEK_SET) {
throw new \RuntimeException('The AppendStream can only seek with SEEK_SET');
}

$this->pos = $this->current = 0;

// Rewind each stream
foreach ($this->streams as $i => $stream) {
try {
$stream->rewind();
} catch (\Exception $e) {
throw new \RuntimeException('Unable to seek stream '
. $i . ' of the AppendStream', 0, $e);
}
}

// Seek to the actual position by reading from each stream
while ($this->pos < $offset && !$this->eof()) {
$result = $this->read(min(8096, $offset - $this->pos));
if ($result === '') {
break;
}
}
}

/**
* Reads from all of the appended streams until the length is met or EOF.
*
* {@inheritdoc}
*/
public function read($length)
{
$buffer = '';
$total = count($this->streams) - 1;
$remaining = $length;
$progressToNext = false;

while ($remaining > 0) {

// Progress to the next stream if needed.
if ($progressToNext || $this->streams[$this->current]->eof()) {
$progressToNext = false;
if ($this->current === $total) {
break;
}
$this->current++;
}

$result = $this->streams[$this->current]->read($remaining);

// Using a loose comparison here to match on '', false, and null
if ($result == null) {
$progressToNext = true;
continue;
}

$buffer .= $result;
$remaining = $length - strlen($buffer);
}

$this->pos += strlen($buffer);

return $buffer;
}

public function isReadable()
{
return true;
}

public function isWritable()
{
return false;
}

public function isSeekable()
{
return $this->seekable;
}

public function write($string)
{
throw new \RuntimeException('Cannot write to an AppendStream');
}

public function getMetadata($key = null)
{
return $key ? null : [];
}
}
Loading

0 comments on commit ab96af8

Please sign in to comment.