<commit_before><?php

namespace Laravie\Codex\Support;

use Http\Discovery\StreamFactoryDiscovery;
use Http\Message\MultipartStream\MultipartStreamBuilder;

trait MultipartRequest
{
    /**
     * Prepare multipart request payloads.
     *
     * @param  array  $headers
     * @param  array  $body
     * @param  array  $files
     *
     * @return array
     */
    final public function prepareMultipartRequestPayloads(array $headers = [], array $body = [], array $files = [])
    {
        $multipart = isset($headers['Content-Type']) && $headers['Content-Type'] == 'multipart/form-data';

        if (empty($files) && ! $multipart) {
            return [$headers, $body];
        }

        $builder = new MultipartStreamBuilder(StreamFactoryDiscovery::find());

        $this->addBodyToMultipartBuilder($builder, $this->sanitizeFrom($body));
        $this->addFilesToMultipartBuilder($builder, $files);

        $headers['Content-Type'] = 'multipart/form-data; boundary='.$builder->getBoundary();

        return [$headers, $builder->build()];
    }

    /**
     * Add body to multipart stream builder.
     *
     * @param  \Http\Message\MultipartStream\MultipartStreamBuilder  $builder
     * @param array  $body
     * @param string|null  $prefix
     *
     * @return void
     */
    final protected function addBodyToMultipartBuilder(MultipartStreamBuilder $builder, array $body, $prefix = null)
    {
        foreach ($body as $key => $value) {
            $name = $key;

            if (! is_null($prefix)) {
                $name = "{$prefix}[{$key}]";
            }

            if (is_array($value)) {
                $this->addBodyToMultipartBuilder($builder, $value, $name);
                continue;
            }

            $builder->addResource($name, $value, ['Content-Type' => 'text/plain']);
        }
    }

    /**
     * Add files to multipart stream builder.
     *
     * @param  \Http\Message\MultipartStream\MultipartStreamBuilder  $builder
     * @param  array  $files
     *
     * @return void
     */
    final protected function addFilesToMultipartBuilder(MultipartStreamBuilder $builder, array $files = [])
    {
        foreach ($files as $key => $file) {
            if (! is_null($file)) {
                $builder->addResource($key, fopen($file, 'r'));
            }
        }
    }

    /**
     * Sanitize "to" for content.
     *
     * @param  array|mixed  $content
     *
     * @return array
     */
    abstract public function sanitizeTo($content);
}
<commit_msg>Allow to stream as multipart using request.<commit_after><?php

namespace Laravie\Codex\Support;

use Laravie\Codex\Endpoint;
use Http\Discovery\StreamFactoryDiscovery;
use Http\Message\MultipartStream\MultipartStreamBuilder;

trait MultipartRequest
{
    /**
     * Stream (multipart) the HTTP request.
     *
     * @param  string  $method
     * @param  \Laravie\Codex\Endpoint|string  $path
     * @param  array  $headers
     * @param  \Psr\Http\Message\StreamInterface|array|null  $body
     * @param  array  $files
     *
     * @return \Laravie\Codex\Contracts\Response
     */
    public function stream($method, $uri, array $headers = [], $body = [], array $files = [])
    {
        $body = $this->sanitizeFrom($body);

        $endpoint = ($path instanceof Endpoint)
                        ? $this->getApiEndpoint($path->getPath())->addQuery($path->getQuery())
                        : $this->getApiEndpoint($path);

        return $this->client->stream($method, $this->resolveUri($endpoint), $headers, $body, $files)
                    ->setSanitizer($this->getSanitizer())
                    ->validate();
    }

    /**
     * Prepare multipart request payloads.
     *
     * @param  array  $headers
     * @param  array  $body
     * @param  array  $files
     *
     * @return array
     */
    final public function prepareMultipartRequestPayloads(array $headers = [], array $body = [], array $files = [])
    {
        $multipart = isset($headers['Content-Type']) && $headers['Content-Type'] == 'multipart/form-data';

        if (empty($files) && ! $multipart) {
            return [$headers, $body];
        }

        $builder = new MultipartStreamBuilder(StreamFactoryDiscovery::find());

        $this->addBodyToMultipartBuilder($builder, $this->sanitizeFrom($body));
        $this->addFilesToMultipartBuilder($builder, $files);

        $headers['Content-Type'] = 'multipart/form-data; boundary='.$builder->getBoundary();

        return [$headers, $builder->build()];
    }

    /**
     * Add body to multipart stream builder.
     *
     * @param  \Http\Message\MultipartStream\MultipartStreamBuilder  $builder
     * @param array  $body
     * @param string|null  $prefix
     *
     * @return void
     */
    final protected function addBodyToMultipartBuilder(MultipartStreamBuilder $builder, array $body, $prefix = null)
    {
        foreach ($body as $key => $value) {
            $name = $key;

            if (! is_null($prefix)) {
                $name = "{$prefix}[{$key}]";
            }

            if (is_array($value)) {
                $this->addBodyToMultipartBuilder($builder, $value, $name);
                continue;
            }

            $builder->addResource($name, $value, ['Content-Type' => 'text/plain']);
        }
    }

    /**
     * Add files to multipart stream builder.
     *
     * @param  \Http\Message\MultipartStream\MultipartStreamBuilder  $builder
     * @param  array  $files
     *
     * @return void
     */
    final protected function addFilesToMultipartBuilder(MultipartStreamBuilder $builder, array $files = [])
    {
        foreach ($files as $key => $file) {
            if (! is_null($file)) {
                $builder->addResource($key, fopen($file, 'r'));
            }
        }
    }

    /**
     * Sanitize "to" for content.
     *
     * @param  array|mixed  $content
     *
     * @return array
     */
    abstract public function sanitizeTo($content);
}

{'fraction_non_alphanumeric': 0.14156475121460882, 'fraction_numerical': 0.0, 'mean_word_length': 2.2569558101472995, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1986339', 'n_tokens_mistral': 1756, 'n_tokens_neox': 1721, 'n_words': 349}