-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
71e6c16
commit 7671a19
Showing
7 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "zhamao/request", | ||
"description": "A fast HTTP/WebSocket client based on swoole coroutine runtime", | ||
"license": "Apache-2.0", | ||
"authors": [ | ||
{ | ||
"name": "whale", | ||
"email": "crazysnowcc@gmail.com" | ||
} | ||
], | ||
"minimum-stability": "stable", | ||
"require": { | ||
"php": ">=7.2", | ||
"swoole/ide-helper": "@dev" | ||
}, | ||
"require-dev": { | ||
"phpunit/phpunit": "^9.3" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"ZM\\Requests\\": "src/ZM/Requests" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php | ||
|
||
|
||
namespace ZM\Requests; | ||
|
||
|
||
use Swoole\Coroutine\Http\Client; | ||
|
||
class ZMRequest | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
public static $last_error = ''; | ||
|
||
/** | ||
* 使用Swoole协程客户端发起HTTP GET请求 | ||
* @param $url | ||
* @param array $headers | ||
* @param array $set | ||
* @param bool $return_body | ||
* @return bool|string|Client | ||
* @version 1.1 | ||
* 返回请求后的body | ||
* 如果请求失败或返回状态不是200,则返回 false | ||
*/ | ||
public static function get($url, $headers = [], $set = [], $return_body = true) { | ||
/** @var Client $cli */ | ||
list($cli, $parse) = self::getNewClient($url); | ||
if($cli === null) return false; | ||
$cli->set($set == [] ? ['timeout' => 15.0] : $set); | ||
$cli->setHeaders($headers); | ||
$cli->get($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : "")); | ||
if ($return_body) { | ||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false; | ||
$a = $cli->body; | ||
$cli->close(); | ||
return $a; | ||
} else { | ||
$cli->close(); | ||
return $cli; | ||
} | ||
} | ||
|
||
/** | ||
* 使用Swoole协程客户端发起HTTP POST请求 | ||
* 返回请求后的body | ||
* 如果请求失败或返回状态不是200,则返回 false | ||
* @param $url | ||
* @param array $header | ||
* @param $data | ||
* @param array $set | ||
* @param bool $return_body | ||
* @return bool|string|Client | ||
*/ | ||
public static function post($url, array $header, $data, $set = [], $return_body = true) { | ||
/** @var Client $cli */ | ||
list($cli, $parse) = self::getNewClient($url); | ||
if($cli === null) return false; | ||
$cli->set($set == [] ? ['timeout' => 15.0] : $set); | ||
$cli->setHeaders($header); | ||
$cli->post($parse["path"] . (isset($parse["query"]) ? ("?" . $parse["query"]) : ""), $data); | ||
if ($return_body) { | ||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false; | ||
$a = $cli->body; | ||
$cli->close(); | ||
return $a; | ||
} else { | ||
$cli->close(); | ||
return $cli; | ||
} | ||
} | ||
|
||
/** | ||
* @param $url | ||
* @param array $set | ||
* @param array $header | ||
* @return ZMWebSocket | ||
* @since 1.5 | ||
*/ | ||
public static function websocket($url, $set = ['websocket_mask' => true], $header = []) { | ||
return new ZMWebSocket($url, $set, $header); | ||
} | ||
|
||
/** | ||
* @param $url | ||
* @param array $attribute | ||
* @param bool $return_body | ||
* @return bool|string|Client | ||
*/ | ||
public static function request($url, $attribute = [], $return_body = true) { | ||
/** @var Client $cli */ | ||
list($cli, $parse) = self::getNewClient($url); | ||
if($cli === null) return false; | ||
$cli->set($attribute["set"] ?? ["timeout" => 15.0]); | ||
$cli->setMethod($attribute["method"] ?? "GET"); | ||
$cli->setHeaders($attribute["headers"] ?? []); | ||
if(isset($attribute["data"])) $cli->setData($attribute["data"]); | ||
if(isset($attribute["file"])) { | ||
foreach($attribute["file"] as $k => $v) { | ||
$cli->addFile($v["path"], $v["name"], $v["mime_type"] ?? null, $v["filename"] ?? null, $v["offset"] ?? 0, $v["length"] ?? 0); | ||
} | ||
} | ||
$cli->execute($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : "")); | ||
if ($return_body) { | ||
if ($cli->errCode != 0 || $cli->statusCode != 200) return false; | ||
$a = $cli->body; | ||
$cli->close(); | ||
return $a; | ||
} else { | ||
$cli->close(); | ||
return $cli; | ||
} | ||
} | ||
|
||
/** | ||
* @param $url | ||
* @param null|bool $dst | ||
* @return bool | ||
*/ | ||
public static function downloadFile($url, $dst = null) { | ||
/** @var Client $cli */ | ||
list($cli, $parse) = self::getNewClient($url); | ||
if($cli === null) return false; | ||
$cli->set(["timeout" => 60.0]); | ||
$save_path = $dst === null ? "/tmp/_zm_".mt_rand(1000000, 9999999) : $dst; | ||
$result = $cli->download($parse["path"] . (isset($parse["query"]) ? "?" . $parse["query"] : ""), $save_path); | ||
if($result === false) return false; | ||
elseif ($dst === null) return $save_path; | ||
else return true; | ||
} | ||
|
||
/** | ||
* @param $url | ||
* @return bool|array | ||
*/ | ||
private static function getNewClient($url) { | ||
$parse = parse_url($url); | ||
if (!isset($parse["host"])) { | ||
self::$last_error = ("ZMRequest: url must contains scheme such as \"http(s)://\""); | ||
return false; | ||
} | ||
if(!isset($parse["path"])) $parse["path"] = "/"; | ||
$port = $parse["port"] ?? (($parse["scheme"] ?? "http") == "https" ? 443 : 80); | ||
$cli = new Client($parse["host"], $port, ($parse["scheme"] ?? "http") == "https"); | ||
return [$cli, $parse]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
<?php | ||
|
||
|
||
namespace ZM\Requests; | ||
|
||
|
||
use Swoole\Coroutine\Http\Client; | ||
use Swoole\WebSocket\Frame; | ||
|
||
/** | ||
* Class ZMWebSocket | ||
* @package ZM\Utils | ||
* @since 1.5 | ||
*/ | ||
class ZMWebSocket | ||
{ | ||
private $parse; | ||
private $client; | ||
|
||
public $is_available = false; | ||
|
||
private $close_func; | ||
private $message_func; | ||
|
||
public function __construct($url, $set = ['websocket_mask' => true], $header = []) { | ||
$this->parse = parse_url($url); | ||
if (!isset($this->parse["host"])) { | ||
ZMRequest::$last_error = ("ZMRequest: url must contains scheme such as \"ws(s)://\""); | ||
return; | ||
} | ||
if (!isset($this->parse["path"])) $this->parse["path"] = "/"; | ||
$port = $this->parse["port"] ?? (($this->parse["scheme"] ?? "ws") == "wss" ? 443 : 80); | ||
$this->client = new Client($this->parse["host"], $port, ($this->parse["scheme"] ?? "ws") == "wss"); | ||
$this->client->set($set); | ||
if ($header != []) $this->client->setHeaders($header); | ||
$this->is_available = true; | ||
} | ||
|
||
/** | ||
* @return bool | ||
*/ | ||
public function upgrade() { | ||
if (!$this->is_available) return false; | ||
$r = $this->client->upgrade($this->parse["path"] . (isset($this->parse["query"]) ? ("?" . $this->parse["query"]) : "")); | ||
if ($r) { | ||
go(function () { | ||
while (true) { | ||
$result = $this->client->recv(60); | ||
if ($result === false) { | ||
if ($this->client->connected === false) { | ||
go(function () { | ||
call_user_func($this->close_func, $this->client); | ||
}); | ||
break; | ||
} | ||
} elseif ($result instanceof Frame) { | ||
go(function () use ($result) { | ||
$this->is_available = false; | ||
call_user_func($this->message_func, $result, $this->client); | ||
}); | ||
} | ||
} | ||
}); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* @param callable $callable | ||
* @return ZMWebSocket | ||
*/ | ||
public function onMessage(callable $callable) { | ||
$this->message_func = $callable; | ||
return $this; | ||
} | ||
|
||
/** | ||
* @param callable $callable | ||
* @return $this | ||
*/ | ||
public function onClose(callable $callable) { | ||
$this->close_func = $callable; | ||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# 代码测试 | ||
问题:在 Swoole 的协程环境下才能运行的代码不能直接使用 PHPUnit 进行单元测试。 | ||
|
||
解决:使用此文件夹内的 `phpunit` 文件作为 PHPUnit 可执行文件进行测试: | ||
```bash | ||
./phpunit ZMRequestTest.php | ||
``` | ||
|
||
如果使用 IDE(如 PhpStorm),请将 `Test Frameworks` 中 PHPUnit 的配置 `Path to phpunit.phar` 改为此 `phpunit` 文件即可。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?php | ||
|
||
|
||
use PHPUnit\Framework\TestCase; | ||
use ZM\Requests\ZMRequest; | ||
|
||
class ZMRequestTest extends TestCase | ||
{ | ||
public function testGet() { | ||
$this->assertIsString(ZMRequest::get("http://captive.apple.com")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#!/usr/bin/env php | ||
<?php | ||
/** | ||
* Copyright: Swlib | ||
* Author: Twosee <twose@qq.com> | ||
* Date: 2018/4/14 下午10:58 | ||
*/ | ||
|
||
Co::set([ | ||
'log_level' => SWOOLE_LOG_INFO, | ||
'trace_flags' => 0 | ||
]); | ||
|
||
if (!ini_get('date.timezone')) { | ||
ini_set('date.timezone', 'UTC'); | ||
} | ||
|
||
foreach ([ | ||
__DIR__ . '/../../../autoload.php', | ||
__DIR__ . '/../../autoload.php', | ||
__DIR__ . '/../vendor/autoload.php', | ||
__DIR__ . '/vendor/autoload.php' | ||
] as $file | ||
) { | ||
if (file_exists($file)) { | ||
define('PHPUNIT_COMPOSER_INSTALL', $file); | ||
break; | ||
} | ||
} | ||
|
||
if (!defined('PHPUNIT_COMPOSER_INSTALL')) { | ||
fwrite( | ||
STDERR, | ||
'You need to set up the project dependencies using Composer:' . PHP_EOL . PHP_EOL . | ||
' composer install' . PHP_EOL . PHP_EOL . | ||
'You can learn all about Composer on https://getcomposer.org/.' . PHP_EOL | ||
); | ||
|
||
die(1); | ||
} else { | ||
if (array_reverse(explode('/', __DIR__))[0] ?? '' === 'test') { | ||
$vendor_dir = dirname(PHPUNIT_COMPOSER_INSTALL); | ||
$bin_unit = "{$vendor_dir}/bin/phpunit"; | ||
$unit_uint = "{$vendor_dir}/phpunit/phpunit/phpunit"; | ||
if (file_exists($bin_unit)) { | ||
@unlink($bin_unit); | ||
@symlink(__FILE__, $bin_unit); | ||
} | ||
if (file_exists($unit_uint)) { | ||
@unlink($unit_uint); | ||
@symlink(__FILE__, $unit_uint); | ||
} | ||
} | ||
} | ||
require PHPUNIT_COMPOSER_INSTALL; | ||
go(function (){ | ||
PHPUnit\TextUI\Command::main(false); | ||
}); | ||
Swoole\Event::wait(); |