Skip to content

Commit e8d5ccd

Browse files
author
Greg Bowler
committed
Full text() functionality using React
1 parent f5781f8 commit e8d5ccd

File tree

4 files changed

+157
-87
lines changed

4 files changed

+157
-87
lines changed

src/BodyResponse.php

+61-15
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,38 @@
22
namespace Gt\Fetch;
33

44
use Gt\Curl\JsonDecodeException;
5+
use Gt\Http\Header\ResponseHeaders;
56
use Gt\Http\Response;
7+
use Gt\Http\StatusCode;
8+
use Psr\Http\Message\UriInterface;
69
use React\Promise\Deferred;
710
use React\Promise\Promise;
11+
use stdClass;
812

13+
/**
14+
* @property-read ResponseHeaders $headers
15+
* @property-read bool $ok
16+
* @property-read bool $redirected
17+
* @property-read int $status
18+
* @property-read string $statusText
19+
* @property-read string $type
20+
* @property-read UriInterface $uri
21+
* @property-read UriInterface $url
22+
*/
923
class BodyResponse extends Response {
10-
public function arrayBuffer():Promise {
11-
$deferred = new Deferred();
24+
/** @var Deferred */
25+
protected $deferred;
1226

13-
return $deferred->promise();
27+
public function arrayBuffer():TODO {
1428
}
1529

16-
public function blob():Promise {
17-
$deferred = new Deferred();
18-
return $deferred->promise();
30+
public function blob():TODO {
1931
}
2032

21-
public function formData():Promise {
22-
$deferred = new Deferred();
23-
return $deferred->promise();
33+
public function formData():TODO {
2434
}
2535

26-
public function json(int $depth = 512, int $options = 0):Promise {
36+
public function json(int $depth = 512, int $options = 0):StdClass {
2737
$deferred = new Deferred();
2838

2939
$json = json_decode(
@@ -43,10 +53,46 @@ public function json(int $depth = 512, int $options = 0):Promise {
4353
}
4454

4555
public function text():Promise {
46-
$value = $this->getBody();
47-
var_dump($value->getContents());die();
48-
$deferred = new Deferred();
49-
$deferred->resolve($value);
50-
return $deferred->promise();
56+
$this->deferred = new Deferred();
57+
$promise = $this->deferred->promise();
58+
return $promise;
59+
}
60+
61+
public function completeResponse():void {
62+
$position = $this->stream->tell();
63+
$this->stream->rewind();
64+
$contents = $this->stream->getContents();
65+
$this->stream->seek($position);
66+
$this->deferred->resolve($contents);
67+
}
68+
69+
public function __get(string $name) {
70+
switch($name) {
71+
case "headers":
72+
return $this->getHeaders();
73+
break;
74+
75+
case "ok":
76+
return ($this->statusCode >= 200
77+
&& $this->statusCode < 300);
78+
79+
case "redirected":
80+
break;
81+
82+
case "status":
83+
return $this->getStatusCode();
84+
85+
case "statusText":
86+
return StatusCode::REASON_PHRASE[$this->status];
87+
88+
case "type":
89+
// TODO: What exactly is this property for?
90+
break;
91+
92+
case "uri":
93+
case "url":
94+
// TODO: How do we get the URI from the request?
95+
break;
96+
}
5197
}
5298
}

src/GlobalFetch.php

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php
2-
32
namespace Gt\Fetch;
43

54
use React\Promise\PromiseInterface;

src/Http.php

+7-9
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,20 @@
44
use Gt\Http\Uri;
55
use Http\Client\HttpClient;
66
use Http\Client\HttpAsyncClient;
7-
use Http\Promise\Promise;
87
use Psr\Http\Message\RequestInterface;
98
use Psr\Http\Message\ResponseInterface;
109
use Psr\Http\Message\UriInterface;
11-
use React\EventLoop\StreamSelectLoop;
10+
use React\EventLoop\Factory;
11+
use React\EventLoop\LoopInterface;
1212
use React\Promise\Deferred;
13-
use React\EventLoop\Factory as EventLoopFactory;
1413
use React\Promise\PromiseInterface;
1514

1615
class Http extends GlobalFetchHelper implements HttpClient, HttpAsyncClient {
1716
const REFERRER = "PhpGt/Fetch";
1817

1918
/** @var float */
2019
protected $interval;
21-
/** @var StreamSelectLoop */
20+
/** @var LoopInterface */
2221
protected $loop;
2322
/** @var RequestResolver */
2423
protected $requestResolver;
@@ -37,7 +36,7 @@ public function __construct(
3736
$this->options = $options + $this->options;
3837
$this->interval = $interval;
3938

40-
$this->loop = EventLoopFactory::create();
39+
$this->loop = Factory::create();
4140
$this->requestResolver = new RequestResolver($this->loop);
4241
}
4342

@@ -80,6 +79,7 @@ public function fetch($input, array $init = []):PromiseInterface {
8079
$init,
8180
$deferred
8281
);
82+
8383
return $promise;
8484
}
8585

@@ -103,13 +103,12 @@ public function ensureUriInterface($uri):UriInterface {
103103
* Executes all promises in parallel, returning only when all promises
104104
* have been fulfilled.
105105
*/
106-
public function wait() {
107-
$this->timer = $this->loop->addPeriodicTimer(
106+
public function wait():void {
107+
$timer = $this->loop->addPeriodicTimer(
108108
$this->interval,
109109
[$this->requestResolver, "tick"]
110110
);
111111
$this->loop->run();
112-
// $this->requestResolver->temporaryThing();
113112
}
114113

115114
/**
@@ -120,7 +119,6 @@ public function all():PromiseInterface {
120119
$deferred = new Deferred();
121120
$promise = $deferred->promise();
122121
$this->wait();
123-
124122
$deferred->resolve(true);
125123
return $promise;
126124
}

src/RequestResolver.php

+89-62
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,32 @@
66
use Gt\Curl\CurlMulti;
77
use Gt\Curl\CurlMultiInterface;
88
use Gt\Http\Header\Parser;
9-
use Gt\Http\Response;
109
use Psr\Http\Message\UriInterface;
1110
use React\EventLoop\LoopInterface;
1211
use React\Promise\Deferred;
12+
use React\Promise\Promise;
1313

1414
class RequestResolver {
1515
/** @var LoopInterface */
1616
protected $loop;
17-
/** @var CurlMultiInterface */
18-
protected $curlMulti;
1917

18+
/** @var CurlMultiInterface[] */
19+
protected $curlMultiList;
2020
/** @var CurlInterface[] */
2121
protected $curlList;
22-
/** @var Deferred[] */
22+
/** @var Promise[] */
2323
protected $deferredList;
24-
/** @var Response[] */
24+
/** @var BodyResponse[] */
2525
protected $responseList;
2626
/** @var string[] */
27-
protected $headersList;
27+
protected $headerList;
2828

29-
public function __construct(
30-
LoopInterface $loop,
31-
string $curlMultiClass = CurlMulti::class
32-
) {
29+
public function __construct(LoopInterface $loop) {
3330
$this->loop = $loop;
3431
$this->curlList = [];
32+
$this->curlMultiList = [];
3533
$this->deferredList = [];
36-
$this->headersList = [];
37-
$this->curlMulti = new $curlMultiClass();
34+
$this->headerList = [];
3835
}
3936

4037
public function add(
@@ -49,78 +46,108 @@ public function add(
4946
}
5047

5148
$curl->setOpt(CURLOPT_RETURNTRANSFER, false);
52-
$curl->setOpt(CURLOPT_HEADER, true);
49+
$curl->setOpt(CURLOPT_HEADER, false);
50+
$curl->setOpt(CURLOPT_HEADERFUNCTION, [$this, "writeHeader"]);
5351
$curl->setOpt(CURLOPT_WRITEFUNCTION, [$this, "write"]);
5452

55-
$this->curlMulti->add($curl);
53+
$curlMulti = new CurlMulti();
54+
$curlMulti->add($curl);
55+
5656
$this->curlList []= $curl;
57+
$this->curlMultiList []= $curlMulti;
5758
$this->deferredList []= $deferred;
58-
$this->headersList []= "";
59-
$this->responseList []= new Response();
59+
$this->responseList []= new BodyResponse();
60+
$this->headerList []= "";
6061
}
6162

62-
public function write($chIncoming, $content):int {
63-
$match = false;
64-
foreach($this->curlList as $i => $curl) {
65-
if($chIncoming === $curl->getHandle()) {
66-
$match = true;
67-
break;
68-
}
69-
}
63+
public function write($ch, $content):int {
64+
$i = $this->getIndex($ch);
7065

71-
if(!$match) {
72-
// TODO: Throw exception.
73-
die("NO CURL HANDLE!!!!");
66+
$body = $this->responseList[$i]->getBody();
67+
$body->write($content);
68+
69+
if($this->deferredList[$i]) {
70+
$this->deferredList[$i]->resolve($this->responseList[$i]);
71+
$this->deferredList[$i] = null;
7472
}
7573

76-
if(!strstr($this->headersList[$i], "\r\n\r\n")) {
77-
echo "1";
78-
if(strstr($content, "\r\n\r\n")) {
79-
$parser = new Parser($this->headersList[$i]);
80-
$this->responseList[$i] = $this->responseList[$i]->withProtocolVersion(
81-
$parser->getProtocolVersion()
82-
);
83-
$this->responseList[$i] = $this->responseList[$i]->withStatus(
84-
$parser->getStatusCode()
85-
);
86-
foreach($parser->getKeyValues() as $key => $value) {
87-
$this->responseList[$i] = $this->responseList[$i]->withAddedHeader(
88-
$key,
89-
$value
90-
);
74+
return strlen($content);
75+
}
76+
77+
public function writeHeader($ch, string $rawHeader) {
78+
$i = $this->getIndex($ch);
79+
80+
$headerLine = trim($rawHeader);
81+
82+
if($headerLine === "") {
83+
$parser = new Parser($this->headerList[$i]);
84+
$this->responseList[$i] = $this->responseList[$i]->withProtocolVersion(
85+
$parser->getProtocolVersion()
86+
);
87+
$this->responseList[$i] = $this->responseList[$i]->withStatus(
88+
$parser->getStatusCode()
89+
);
90+
foreach($parser->getKeyValues() as $key => $value) {
91+
if(empty($key)) {
92+
continue;
9193
}
94+
$this->responseList[$i] = $this->responseList[$i]->withAddedHeader(
95+
$key,
96+
$value
97+
);
9298
}
93-
94-
$this->headersList[$i] .= $content;
95-
}
96-
else {
97-
echo "2";
98-
$body = $this->responseList[$i]->getBody();
99-
$body->write($content);
100-
$this->deferredList[$i]->resolve($body);
10199
}
102100

103-
echo ".";
104-
105-
return strlen($content);
101+
$this->headerList[$i] .= $rawHeader;
102+
return strlen($rawHeader);
106103
}
107104

108105
public function tick():void {
109-
$active = 0;
106+
$totalActive = 0;
107+
108+
foreach($this->curlMultiList as $i => $curlMulti) {
109+
$active = 0;
110+
111+
do {
112+
$status = $curlMulti->exec($active);
113+
}
114+
while($status === CURLM_CALL_MULTI_PERFORM);
115+
116+
if($status !== CURLM_OK) {
117+
// TODO: Throw exception.
118+
die("ERROR!");
119+
}
120+
121+
$totalActive += $active;
122+
123+
if($active === 0) {
124+
// TODO: Resolve body's complete promise.
125+
$this->responseList[$i]->completeResponse();
126+
}
110127

111-
do {
112-
$status = $this->curlMulti->exec($active);
113-
}
114-
while($status === CURLM_CALL_MULTI_PERFORM);
115128

116-
if($status !== CURLM_OK) {
117-
// TODO: Throw exception.
118-
die("ERROR!");
119129
}
120130

121-
if($active === 0) {
131+
if($totalActive === 0) {
122132
$this->loop->stop();
123133
return;
124134
}
125135
}
136+
137+
protected function getIndex($chIncoming):int {
138+
$match = false;
139+
foreach($this->curlList as $i => $curl) {
140+
if($chIncoming === $curl->getHandle()) {
141+
$match = true;
142+
break;
143+
}
144+
}
145+
146+
if(!$match) {
147+
// TODO: Throw exception.
148+
die("NO CURL HANDLE!!!!");
149+
}
150+
151+
return $i;
152+
}
126153
}

0 commit comments

Comments
 (0)