Skip to content

Allow MultipartStreamBuilder subclass to manipulate data #49

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

Merged
merged 4 commits into from
Dec 22, 2020

Conversation

yookoala
Copy link
Contributor

@yookoala yookoala commented Dec 18, 2020

  • Change property $data visibility from "private" to "protected".
    Leaving more flexibilities for subclassing.
Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Related tickets
Documentation This is one simple signature change for subclassing.
License MIT

What's in this PR?

Allow subclass of MultipartStreamBuilder to manipulate $data property.

Why?

I am recently working on Range Request support
for my application. For a range request with multiple specified ranges, I need to return a "multipart/byteranges" response.
The signature of MultipartStreamBuilder::addResource forces me to add a field name ($name), which is totally non-sense for
my use case.

I tried to extend the MultipartStreamBuilder with my own class and have an extra method that can properly add
the streams without the unnecessary extra handling, but since the visibility of MultipartStreamBuilder::data is "private",
I cannot do that.

Example Usage

class MyBuilder extends MultipartStreamBuilder
{
    public function addStream($resource, array $options = [])
    {
        $options += ['headers' => []];
        $this->data[] = [
            'contents' => $this->createStream($resource),
            'headers' => $options['headers'],
        ];
    }
}

// If you added new features, show examples of how to use them here
// (remove this section if not a new feature)
$response = $resposneFactory->createResponse(416);

// Assuming I have to deal with a range request "Range: bytes=0-8192, 65536-131072"
$steam = (new MyBuilder($streamFactory))
  ->addPart(custom_splice_resource_func(fopen('/path/to/file1), 0, 8192), ['Content-Range', 'bytes 0-8192/8192'])
  ->addPart(custom_splice_resource_func(fopen('/path/to/file1), 65536, 131072), ['Content-Range', 'bytes 65536-131072/65536']);

// Emit the response to client
$emitter->emit($response->withBody($stream));

Checklist

  • Updated CHANGELOG.md to describe BC breaks / deprecations | new feature | bugfix
  • Documentation pull request created (if not simply a bugfix)

To Do

  • If the PR is not complete but you want to discuss the approach, list what remains to be done here

@dbu
Copy link
Contributor

dbu commented Dec 19, 2020

this sounds really cool and makes me wonder if it would make sense to add that functionality into this repository, if you are motivated to contribute it.

looking at how we put things into data in
https://github.com/php-http/multipart-stream-builder/pull/49/files#diff-28351bcab63ba2060821f4c2f4e0c12877a30426b7f6819c2faf72e6db1250c1R107 i wonder if instead it would make sense to have a protected method addData(stream $contents, array $headers, string $filename) and keep $data private? otherwise it is rather easy to mess things up.

hm, and it seems that the 'filename' in the $data is not used, only the one in the 'headers' seems relevant... so it might be just addData(stream $contents, array $headers)?

@yookoala
Copy link
Contributor Author

I am currently writing a multipart stream implementation from ground up to fulfill my own needs. My approach is quite different than yours so I don't think it is suitable as a PR.

Instead of building the entire stream content string, I opt to dynamically seek and read the underlying resources on StreamInterface::read. This approach, although heavier in code (thus probably slower), would be more memory efficient for large file uploads / downloads.

I am still in the middle of my process. I can share more with you later.

@yookoala
Copy link
Contributor Author

this sounds really cool and makes me wonder if it would make sense to add that functionality into this repository, if you are motivated to contribute it.

looking at how we put things into data in
https://github.com/php-http/multipart-stream-builder/pull/49/files#diff-28351bcab63ba2060821f4c2f4e0c12877a30426b7f6819c2faf72e6db1250c1R107 i wonder if instead it would make sense to have a protected method addData(stream $contents, array $headers, string $filename) and keep $data private? otherwise it is rather easy to mess things up.

hm, and it seems that the 'filename' in the $data is not used, only the one in the 'headers' seems relevant... so it might be just addData(stream $contents, array $headers)?

This approach makes sense to me. If you're OK, I can write a PR of it.

@dbu
Copy link
Contributor

dbu commented Dec 20, 2020

would be great if you can refactor it to have a clean, protected addData method. that would allow you to do the thing you want, right?

and i see, this different approach to a stream builder sounds interesting but can not replace the one here. in that case i think it is best to keep it separate. if you open source it, we'd be happy to link to it from the readme here, with the explanation that the php-http implementation does not support range requests, and people who need that should look at your implementation.

* Added a public method addData for manipulating the private $data
  array properly.
@yookoala yookoala force-pushed the fix-builder-properties branch from bd7c05e to e451466 Compare December 22, 2020 09:36
@yookoala
Copy link
Contributor Author

Rewritten into a public addData function. Please check.

Copy link
Contributor

@dbu dbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have addResource call the addData method? though there is some handling on the thing we created with getStream to determine the "filename" from the uri. would it make sense to have that filename header determined that way in addData as well, if it is not set?

@yookoala
Copy link
Contributor Author

can we have addResource call the addData method? though there is some handling on the thing we created with getStream to determine the "filename" from the uri. would it make sense to have that filename header determined that way in addData as well, if it is not set?

If the 'filename' key is not used at all elsewhere, can I omit it in MultipartStreamBuilder::data? Seem to me it's only used in MultipartStreamBuilder::prepareHeaders.

* Rewrite with new addData method.
* Remove internal 'filename' key from MultipartStreamBuilder::data.
@dbu
Copy link
Contributor

dbu commented Dec 22, 2020

If the 'filename' key is not used at all elsewhere, can I omit it in MultipartStreamBuilder::data? Seem to me it's only used in MultipartStreamBuilder::prepareHeaders.

i agree, i can't see it being used anywhere once it goes into data. i guess its a leftover after a previous refactoring. and with data being private, we can be sure there is no BC break if we drop it from the $data.

@yookoala
Copy link
Contributor Author

Please see if 7323e85 (rewrite addResource with addData) is OK.

Copy link
Contributor

@dbu dbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you, this looks good to me - i have one question to double-check i understood correctly.

and please keep the blank line before the return. i think if you rebase on master, the styleci run would work again and complain about them too ;-)

* add a line before final returns.
* use addData to immediately return $this.
@yookoala
Copy link
Contributor Author

Updated. Please see if 0db0d5c works.

Copy link
Contributor

@dbu dbu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great, looks good to me!

can you please change the branch-alias in composer.json to 1.x-dev (it is currently 1.1-dev but what you do should go into 1.2. with the 1.x-dev notation we avoid having to update it on each minor version upgrade). and please add a note in the CHANGELOG.md describing the new feature.

@yookoala
Copy link
Contributor Author

Updated. Please check.

@dbu dbu merged commit 1c33633 into php-http:master Dec 22, 2020
@dbu
Copy link
Contributor

dbu commented Dec 22, 2020

i added a changelog entry in 9addfcf

thanks a lot! can you rebase your other MR on master to get the latest changes in?

@yookoala yookoala deleted the fix-builder-properties branch December 23, 2020 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants