Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
crazywhalecc committed Aug 31, 2020
1 parent 71e6c16 commit 7671a19
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
composer.phar
/vendor/
composer.lock
.phpunit.result.cache

# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
Expand Down
24 changes: 24 additions & 0 deletions composer.json
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"
}
}
}
148 changes: 148 additions & 0 deletions src/ZM/Requests/ZMRequest.php
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];
}
}
86 changes: 86 additions & 0 deletions src/ZM/Requests/ZMWebSocket.php
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;
}
}
9 changes: 9 additions & 0 deletions test/README.md
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` 文件即可。
12 changes: 12 additions & 0 deletions test/ZMRequestTest.php
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"));
}
}
59 changes: 59 additions & 0 deletions test/phpunit
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();

0 comments on commit 7671a19

Please sign in to comment.