Completed
Push — master ( 73a5aa...d5d862 )
by Ryosuke
03:31
created

UploaderTrait::uploadImageAsync()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 3
crap 1
1
<?php
2
3
namespace mpyw\Cowitter\Traits;
4
5
use mpyw\Cowitter\HttpException;
6
use mpyw\Co\Co;
7
use mpyw\Cowitter\ResponseInterface;
8
9
trait UploaderTrait
10
{
11
    abstract public function getAsync($endpoint, array $params = [], $return_response_object = false);
12
    abstract public function postAsync($endpoint, array $params = [], $return_response_object = false);
13
    abstract public function postMultipartAsync($endpoint, array $params = [], $return_response_object = false);
14
15 9
    protected static function validateChunkSize($value)
16 9
    {
17 9
        if (false === $value = filter_var($value, FILTER_VALIDATE_INT)) {
18 1
            throw new \InvalidArgumentException('Chunk size must be integer.');
19
        }
20 8
        if ($value < 10000) {
21 1
            throw new \LengthException('Chunk size must be no less than 10000 bytes.');
22
        }
23 7
        return $value;
24
    }
25
26 7
    protected static function getMimeType(\SplFileObject $file)
27 7
    {
28 7
        $finfo = new \finfo(FILEINFO_MIME_TYPE);
29 7
        $type = $finfo->buffer($file->fread(1024));
30 7
        $file->rewind();
31 7
        return $type;
32
    }
33
34 9
    public function uploadAsync(\SplFileObject $file, $media_category = null, callable $on_progress = null, $chunk_size = 300000)
35 9
    {
36 9
        $response = (yield $this->uploadStep1($file, $media_category, $chunk_size));
37 7
        if (!isset($response->getContent()->processing_info)) {
38 1
            yield Co::RETURN_WITH => $response->getContent();
39
        }
40 6
        yield Co::RETURN_WITH => (yield $this->uploadStep2($response, $on_progress));
41
        // @codeCoverageIgnoreStart
42
    }
43
    // @codeCoverageIgnoreEnd
44
45 9
    protected function uploadStep1(\SplFileObject $file, $media_category = null, $chunk_size = 300000)
46 9
    {
47 9
        $chunk_size = static::validateChunkSize($chunk_size);
48 7
        $info = (yield $this->postAsync('media/upload', [
49 7
            'command' => 'INIT',
50 7
            'media_type' => static::getMimeType($file),
51 7
            'total_bytes' => $file->getSize(),
52 7
            'media_category' => $media_category,
53
        ]));
54 7
        $tasks = [];
55 7
        for ($i = 0; '' !== $buffer = $file->fread($chunk_size); ++$i) {
56 7
            $tasks[] = $this->postMultipartAsync('media/upload', [
57 7
                'command' => 'APPEND',
58 7
                'media_id' => $info->media_id_string,
59 7
                'segment_index' => $i,
60 7
                'media' => $buffer,
61
            ]);
62
        }
63 7
        yield $tasks;
64 7
        yield Co::RETURN_WITH => (yield $this->postAsync('media/upload', [
65 7
            'command' => 'FINALIZE',
66 7
            'media_id' => $info->media_id_string,
67 7
        ], true));
68
        // @codeCoverageIgnoreStart
69
    }
70
    // @codeCoverageIgnoreEnd
71
72 6
    protected function uploadStep2(ResponseInterface $response, callable $on_progress = null)
73 6
    {
74 6
        $info = $response->getContent();
75 6
        $canceled = false;
76 6
        while ($info->processing_info->state === 'pending' || $info->processing_info->state === 'in_progress') {
77 6
            $percent = isset($info->processing_info->progress_percent)
78 4
                ? $info->processing_info->progress_percent
79 6
                : null
80
            ;
81 6
            if ($on_progress) {
82 4
                if ((new \ReflectionFunction($on_progress))->isGenerator()) {
83 2
                    Co::async(function () use ($on_progress, $percent, $response, &$canceled) {
84 2
                        if (false === (yield $on_progress($percent, $response))) {
85 2
                            $canceled = true;
86
                        }
87 2
                    });
88 2
                } elseif (false === $on_progress($percent, $response)) {
89 1
                    yield Co::RETURN_WITH => $info;
90
                }
91
            }
92 5
            if ($canceled) yield Co::RETURN_WITH => $info;
93 4
            yield Co::DELAY => $info->processing_info->check_after_secs;
94 4
            if ($canceled) yield Co::RETURN_WITH => $info;
95 4
            $response = (yield $this->getAsync('media/upload', [
96 4
                'command' => 'STATUS',
97 4
                'media_id' => $info->media_id_string,
98 4
            ], true));
99 4
            $info = $response->getContent();
100
        }
101
102 3
        if ($info->processing_info->state === 'failed') {
103 2
            $message = isset($info->processing_info->error->message)
104 1
                ? $info->processing_info->error->message
105 2
                : $info->processing_info->error->name;
106 2
            throw new HttpException(
107
                $message,
108 2
                $info->processing_info->error->code,
109 2
                $response->getHandle(),
110
                $response
111
            );
112
        }
113
114 1
        yield Co::RETURN_WITH => $info;
115
        // @codeCoverageIgnoreStart
116
    }
117
    // @codeCoverageIgnoreEnd
118
119 6
    public function uploadImageAsync(\SplFileObject $file, callable $on_progress = null, $chunk_size = 300000)
120 6
    {
121 6
        return $this->uploadAsync($file, 'tweet_image', $on_progress, $chunk_size);
122
    }
123
124 1
    public function uploadAnimeGifAsync(\SplFileObject $file, callable $on_progress = null, $chunk_size = 300000)
125 1
    {
126 1
        return $this->uploadAsync($file, 'tweet_gif', $on_progress, $chunk_size);
127
    }
128
129 1
    public function uploadVideoAsync(\SplFileObject $file, callable $on_progress = null, $chunk_size = 300000)
130 1
    {
131 1
        return $this->uploadAsync($file, 'tweet_video', $on_progress, $chunk_size);
132
    }
133
}
134